Почему Android изменяет значение EditTexts с тем же идентификатором?

У меня есть фрагмент, содержащий LinearLayout, где разные элементы раздуваются в зависимости от некоторой бизнес-логики. В этих элементах содержится EditText. Когда у меня есть несколько элементов с различным контентом, и я отсоединяю/прикрепляю фрагмент, все EditTexts каким-то образом получают одинаковый текст. Это происходит только до тех пор, пока EditText имеет идентификатор в файле макета.

Почему это происходит? Есть ли другой способ предотвратить это, кроме удаления идентификатора? Я хотел бы использовать findViewById на моих раздутых элементах для доступа к представлениям, а не к ошибкам getChildAt.

Я создал минималистский пример, чтобы продемонстрировать проблему на https://github.com/rodja/EditTextValueProblem

Ответы

Ответ 1

Его можно просто зафиксировать, установив android:saveEnabled="false" в определении макета EditTexts. Конечно, вам нужно убедиться, что содержимое сохранено/восстановлено самостоятельно. Так что это неинтуитивная работа - но это работает для моего дела. Тем не менее, все это выглядит как ошибка Android:

Хорошей особенностью системы макета Android является то, что

Идентификатор не должен быть уникальным во всем дереве [...]

как указано в документации для Android. Это значительно упрощает использование кода и компоновки и в значительной степени используется разработчиками. Я думаю, что реализация состояния экземпляра save/restore для представлений использует идентификатор представления как ключ для сохранения состояния, поэтому он полагается на уникальность в целом дерева. WTF?

Обновление

Я добавил пример ListView в в GitHub, который демонстрирует, что ListView почти наверняка использует аналогичное обходное решение для предотвращения использования EditTexts в этой проблеме, Как видно, текст, который вводится в EditText внутри ListView, не восстанавливается автоматически.

Ответ 2

Существует другая возможность, просто измените идентификатор текста редактирования, например,

mEditText.setId((parentView.getId()+editTextPosition+someFinalNumber));

Или, если это EditText внутри некоторого настраиваемого макета:

mEditText.setId((this.getId()+someFinalNumber));

Таким образом, все EditTexts будут иметь другой идентификатор, и текст будет восстановлен правильно.

Ответ 3

Я думаю, это связано с тем, что состояние всех EditTexts с идентификатором автоматически сохраняется. Если вы выполните FragmentTransactions или измените ориентацию, активность будет воссоздана, и состояние EditTexts будет восстановлено, так как все они имеют одинаковый идентификатор, все они получат одинаковое значение. Мое первое предложение будет переопределять методы onRestoreInstanceState и onSaveInstanceState вашей деятельности, например:

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    // super.onRestoreInstanceState(savedInstanceState);  <-- Make sure this is at least commented out or deleted altogether
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    // super.onSaveInstanceState(outState); <-- Make sure this is at least commented out or deleted altogether
}

Если методы в суперклассе не вызываются, он должен прекратить автоматическое сохранение и восстановление состояний. Дайте мне знать, если это сработает, я не могу проверить это прямо сейчас. Если это не сработает, я уверен, что мы можем найти другую работу.

В любом случае у вас есть проблемы с вашими руками, с которыми не так много приходится иметь дело, использует ListView вместо опции LinearLayout no? Потому что я уверен, что использование ListView также решит проблему.