Восстановить фрагмент с двумя представлениями с одинаковым идентификатором

У меня сложный макет для реализации. Он имеет 19 разделов, которые могут отображаться или не отображаться в зависимости от множества параметров, ранее введенных пользователем. Чтобы упростить код и не отображать неиспользуемые разделы, макет создается динамически.

Все внутри фрагмента. Фрагмент имеет LinearLayout, используемый в качестве контейнера, и при создании фрагмента я генерирую все необходимые разделы.

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

Все работает отлично. Проблема в том, что 2 раздела имеют одинаковую структуру, поэтому они имеют один и тот же макет xml. Из-за этого внутренние виды обоих разделов имеют одинаковый идентификатор. Это не проблема, так как раздел управляется локально в его адаптере. Проблема возникает, когда я перехожу к следующему фрагменту, а затем возвращаюсь к этому. Система пытается восстановить предыдущее состояние представления, и, поскольку эти 2 раздела имеют одинаковые идентификаторы, при восстановлении второго раздела его значения также устанавливаются равными первому.

Есть ли какое-либо решение, чтобы управлять этим или сказать фрагменту не восстанавливать свое состояние (поскольку все равно все перезагружается вручную).

Вот легкий пример текущей структуры:

фрагмент xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Раздел XML

<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/section_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

код фрагмента

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_layout, container, false);

    if (<condition>)
       createSection1(getContext(),view);

    if (<condition>)        
       createSection2(getContext(),view);

    return view;
}


private void createSection1(Context context, ViewGroup root){
    Section1Adapter adapter = new Section1Adapter(context, root);
    // ...
}

private void createSection2(Context context, ViewGroup root){
    Section2Adapter adapter = new Section2Adapter(context, root);
    // ...
}

код адаптера (одна и та же идея для обоих)

public Section2Adapter(LayoutInflater inflater, ViewGroup root) {

    View view = LayoutInflater.from(context).inflate(R.layout.section_layout, root, false);

    initView(view);

    root.addView(view);
}

person Eselfar    schedule 22.08.2017    source источник


Ответы (1)


Ваша проблема, как вы правильно сказали, в том, что в основном вы находитесь в этом состоянии: введите здесь описание изображения

Что вам нужно сделать, так это указать Android самому, на каком ключе в SparseArray сохранять состояние какого EditText. По сути, вам нужно добраться до этого:

введите описание изображения здесь

Механизм, с помощью которого вы этого добиваетесь, очень хорошо объяснен в этой замечательной статье, Паша Дудка. (также спасибо ему за красивые изображения)

Просто выполните поиск «Идентификаторы просмотра должны быть уникальными» в статье, и вы получите ответ.

Суть решения для вашей конкретной ситуации заключается в следующем:

  • вы можете создать подкласс LinearLayout s.t. ваш CustomLinearLayout будет знать раздел, к которому принадлежит ребенок, когда его состояние. Таким образом, вы можете сохранить все дочерние состояния в разделе в SparseArray, предназначенном только для этого раздела, и добавить выделенный SparseArray в глобальный SparseArray (как на картинке).
  • вы можете создать подкласс EditText, s.t. ваш CustomEditText знает, к какому разделу он принадлежит, и сохранит свое состояние в пользовательском ключе в SparseArray - например. section_text_Section1 для первого раздела и section_text_Section2 для второго

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

Надеюсь это поможет.

person cjurjiu    schedule 22.08.2017
comment
Ваху! Удивительный ответ! Большое спасибо, я не знал об этом. Это именно то, что я искал. - person Eselfar; 22.08.2017
comment
Я просто должен сказать, что это действительно, действительно сэкономило мне столько времени и боли. Большое спасибо за это! - person Sharp; 07.11.2019
comment
Ссылка не работает. Архивную версию можно найти здесь. - person l33t; 15.08.2020