Android - 如何停止片段自动恢复其状态?

5

我创建了一个全新的项目来展示我的问题。基本上,我的Fragment里有两个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. 每次需要打开它时重新创建我的fragment。但是,创建新fragment时我的初始化阶段比较耗时,所以不能这样做。
在我的实际项目中,这个fragment就像是我的应用程序中某些设置的一个设置,它有一个取消和一个确定按钮。因此,每次打开这个fragment时,我希望它都处于默认状态,因为我认为例如用户可能配置了很多东西,但是然后按下取消,看到所有视图都处于它们的默认状态,而不是配置过的状态(也许这个想法导致了我的问题,这是一个好的用户体验吗?)
那么,如何防止我的fragment在没有我的允许下恢复它的状态呢?

你可以在这里查看我对类似问题的回答:https://dev59.com/MIfca4cB1Zd3GeqPdQVe#59822026 - gmk57
3个回答

1
如果我理解正确,您想在用户离开Fragment之前重置RadioButton的状态,以便为稍后重新进入Fragment的情况做准备。
由于您希望每次用户重新进入Fragment时都选中第一个RadioButton,但(我猜)不是当用户只是离开应用程序半个小时然后返回继续之前的操作时,因此您应该使用savedInstanceStateBundle来确保RadioButton根据情况具有正确的状态:
让我们引入一个字段private boolean isFirstButtonChecked = true;
保留OnCheckedChangeListener以跟踪RadioButton的选中状态。
isFirstButtonChecked = radioButtonFirst.isChecked();

此外,在OnClickListener中将boolean设置为true
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);更清洁和更安全。

你没有完全理解我的意图,所以我在问题的结尾添加了一些关于我的真实目的的信息。然而,你的这个陈述“你想在用户离开之前重置RadioButtons的状态”实际上是一个提示。因为,在onViewStateRestored中初始化视图的默认状态是一种有点奇怪的体验,因为我可以看到radioButtonSecond被选中,然后立即取消选中。但是,当用户点击取消按钮时,尝试将视图状态重置为其默认值实际上是很好的。 - peanut
所以感谢@0X0nosugar,这可以成为任何一般情况的答案,因为经过一些努力,我认为我们无法阻止系统的这种行为。 - peanut
1
@peanut - 通常情况下,当您按下“返回”按钮(或通过点击“取消”离开对话框)时,运行时会认为可以销毁Activity或(Dialog)Fragment。但是您的情况很特殊,因为您想保留该Fragment,所以您需要做一些额外的工作。这样做并没有什么不妥,但我想向您展示尽可能“顺应潮流”的方法。祝编码愉快 :) - Bö macht Blau

0

在你的片段中更改这些行:

    @Override
    public void onCreate(Bundle savedInstanceState) {
//      super.onCreate(savedInstanceState);
        super.onCreate(null); // <- to prevent the states to be restaured

0
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对象。 因此,如果您上次选择了第二个单选按钮并返回,下一次它将显示第二个按钮的第二个选择。
解决方案: 在openFragment函数中始终创建一个新的weirdfragment对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接