Android - Как остановить фрагмент от автоматического восстановления его состояния? - PullRequest
3 голосов
/ 27 апреля 2019

Я создал совершенно новый проект, чтобы продемонстрировать свою проблему. По сути, мой фрагмент имеет два RadioButton. Когда onCreateView, я всегда использую radioButtonFirst.setChecked(true);. Но если я проверяю radioButtonSecond, а затем нажимаю кнопку «Назад», при следующем открытии он автоматически проверяет radioButtonSecond. Такое поведение нарушает мою логику.

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private WeirdFragment weirdFragment;
    private FragmentManager fragmentManager;
    private FragmentTransaction fragmentTransaction;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        weirdFragment = new WeirdFragment();
        fragmentManager = getSupportFragmentManager();
    }
    public void openFragment(View v){
        fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.container, weirdFragment);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
    }
}

WeirdFragment.java

public class WeirdFragment extends Fragment {
    private RadioButton radioButtonFirst;
    private RadioButton radioButtonSecond;
    private Button buttonReturn;
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_weird, container, false);
        radioButtonFirst = view.findViewById(R.id.radio_button_first);
        radioButtonSecond = view.findViewById(R.id.radio_button_second);
        buttonReturn = view.findViewById(R.id.button_return);

        radioButtonFirst.setChecked(true);

        // I decide to set a listener right here, to see what happened to my radio button. So this listener will be removed in my real project.
        radioButtonSecond.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Log.v("weird", "i got here");
            }
        });
        buttonReturn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                getFragmentManager().beginTransaction().remove(WeirdFragment.this).commit();
            }
        });
        return view;
    }
}

activity_main.xml

<Button
    android:id="@+id/button_open"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="openFragment"
    android:text="Open"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

<FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toBottomOf="@id/button_open" />

fragment_weird.xml

<RadioGroup
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintTop_toTopOf="parent">

    <RadioButton
        android:id="@+id/radio_button_first"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:checked="true"
        android:text="First" />

    <RadioButton
        android:id="@+id/radio_button_second"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Second" />
</RadioGroup>

<Button
    android:id="@+id/button_return"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Return"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent" />

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

onCheckedChanged:31, WeirdFragment$1 (com.peanut.myweirdradiobutton)
setChecked:154, CompoundButton (android.widget)
toggle:113, CompoundButton (android.widget)
toggle:78, RadioButton (android.widget)
performClick:118, CompoundButton (android.widget)
run:19866, View$PerformClick (android.view)
handleCallback:739, Handler (android.os)
dispatchMessage:95, Handler (android.os)
loop:135, Looper (android.os)
main:5254, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
invoke:372, Method (java.lang.reflect)
run:903, ZygoteInit$MethodAndArgsCaller (com.android.internal.os)
main:698, ZygoteInit (com.android.internal.os)

Но когда эта точка останова запускается из ниоткуда, трассировка выглядит следующим образом:

onCheckedChanged:31, WeirdFragment$1 (com.peanut.myweirdradiobutton)
setChecked:154, CompoundButton (android.widget)
onRestoreInstanceState:522, CompoundButton (android.widget)
dispatchRestoreInstanceState:13740, View (android.view)
dispatchRestoreInstanceState:2893, ViewGroup (android.view)
dispatchRestoreInstanceState:2893, ViewGroup (android.view)
restoreHierarchyState:13718, View (android.view)
restoreViewState:494, Fragment (android.support.v4.app)
moveToState:1486, FragmentManagerImpl (android.support.v4.app)
moveFragmentToExpectedState:1784, FragmentManagerImpl (android.support.v4.app)
moveToState:1852, FragmentManagerImpl (android.support.v4.app)
executeOps:802, BackStackRecord (android.support.v4.app)
executeOps:2625, FragmentManagerImpl (android.support.v4.app)
executeOpsTogether:2411, FragmentManagerImpl (android.support.v4.app)
removeRedundantOperationsAndExecute:2366, FragmentManagerImpl (android.support.v4.app)
execPendingActions:2273, FragmentManagerImpl (android.support.v4.app)
run:733, FragmentManagerImpl$1 (android.support.v4.app)
handleCallback:739, Handler (android.os)
dispatchMessage:95, Handler (android.os)
loop:135, Looper (android.os)
main:5254, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
invoke:372, Method (java.lang.reflect)
run:903, ZygoteInit$MethodAndArgsCaller (com.android.internal.os)
main:698, ZygoteInit (com.android.internal.os)

Итак, я вижу, что onRestoreInstanceState проверяет мой radioButtonSecond. Но я не знаю, как это предотвратить. Вещи, которые я рассматриваю:

  1. Выполнение моей логики в onViewStateRestored. Но это скорее не чистое решение.
  2. Повторно создавайте мой фрагмент каждый раз, когда мне нужно его открыть. Но моя фаза инициализации при создании нового фрагмента дорогая, я не должен этого делать.

В моем реальном проекте этот фрагмент похож на настройку чего-либо в моем приложении, он имеет кнопку Отмена и кнопку ОК. Итак, я хочу, чтобы фрагмент находился в своем состоянии по умолчанию каждый раз, когда он открывается, потому что я думаю, что, например, пользователь может настроить много разных вещей, но затем нажать кнопку «Отмена», тогда разумно будет увидеть, что все представления имеют свои значения по умолчанию. состояния, а не настроенные (может быть, эта мысль вызывает у меня проблему, это хороший пользовательский опыт?)

Итак, как помешать моему фрагменту восстановить свое состояние без моего разрешения?

Ответы [ 2 ]

2 голосов
/ 27 апреля 2019

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

Поскольку вы хотите, чтобы первый RadioButton проверялся каждый раз, когда пользователи повторно вводят Fragment после его выхода, но (я полагаю) не тогда, когда пользователи просто оставляли ваше приложение на полчаса, а затем возвращались, чтобы продолжить прямо там, где они остановились, вы должны использовать saveInstanceState Bundle, чтобы убедиться, что RadioButton s имеют правильное состояние в зависимости от ситуации:

Давайте введем поле private boolean isFirstButtonChecked = true;.

Сохраните OnCheckedChangeListener для отслеживания проверенного состояния RadioButton с помощью

isFirstButtonChecked = radioButtonFirst.isChecked();

В дополнение к этому, установите boolean на true в OnClickListener

buttonReturn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            isFirstButtonChecked = true;
            getFragmentManager().beginTransaction().remove(WeirdFragment.this).commit();
        }
});

Теперь вы можете сохранить желаемое состояние в onSaveInstanceState():

     @Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean("FIRST_RB_STATE", isFirstButtonChecked);
}

@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);
    isFirstButtonChecked = savedInstanceState == null 
            ? true 
            : savedInstanceState.getBoolean("FIRST_RB_STATE");
    radioButtonFirst.setChecked(isFirstButtonChecked);
}

Я знаю, что это означает добавление довольно большого количества кода. Но так как вы неохотно воссоздаете свой Fragment с нуля каждый раз, я полагаю, он содержит более двух RadioButton с. Поэтому, позволяя среде выполнения работать на вас, а не бороться с ней, этот подход становится чище и безопаснее, чем просто вызов super.onViewStateRestored(null); в соответствующем методе.

0 голосов
/ 27 апреля 2019
public class MainActivity extends AppCompatActivity {
private WeirdFragment weirdFragment;
private FragmentManager fragmentManager;
private FragmentTransaction fragmentTransaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    fragmentManager = getSupportFragmentManager();
}
public void openFragment(View v){
    weirdFragment = new WeirdFragment();
    fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.add(R.id.container, weirdFragment);
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}

}

Проблема: Вы всегда вызываете один и тот же объект weirdFragment. поэтому, если вы в прошлый раз выберете вторую радиокнопку и вернетесь, в следующий раз будет показана вторая секунда выбранной кнопки.

Решение: Создайте новый объект weirdfragment object всегда в функции openFragment.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...