Android 片段——当另一个片段被推到片段上时,如何在片段中保存视图的状态

在 android 中,一个片段(比如 FragA)被添加到回栈中,另一个片段(比如 FragB)出现在顶部。现在在回击 FragA来到顶部和 onCreateView()被称为。在 FragB被推到上面之前,我让 FragA处于一个特定的状态。

我的问题是如何将 FragA恢复到以前的状态?有没有保存状态的方法(比如在 Bundle 中) ? 如果有,那么我应该重写哪个方法?

153805 次浏览

碎片指南片段列表的例子中,你可以找到:

@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}

你以后可以这样使用:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
}

我是一个片段的初学者,但它似乎是你的问题的解决方案;) OnActivityCreated 在片段从回栈返回之后调用。

如果您正在处理在 android 清单中指定的片段活动中的配置更改,则如下所示

<activity
android:name=".courses.posts.EditPostActivity"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="unspecified" />

然后片段的 onSaveInstanceState将不会被调用,并且 savedInstanceState对象将始终为空。

我处理过类似的问题。因为我知道我会经常返回到前一个片段,所以我检查片段 .isAdded()是否为真,如果为真,那么我只做 transaction.show()而不做 transaction.replace()。这样,如果片段已经在堆栈上,就不会重新创建它——不需要保存状态。

Fragment target = <my fragment>;
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if(target.isAdded()) {
transaction.show(target);
} else {
transaction.addToBackStack(button_id + "stack_item");
transaction.replace(R.id.page_fragment, target);
}
transaction.commit();

需要记住的另一件事是,虽然这保留了片段本身的自然顺序,但您可能仍然需要处理在方向(配置)更改时被销毁和重新创建的活动本身。要在节点的 AndroidManifest.xml 中解决这个问题:

android:configChanges="orientation|screenSize"

在 Android 3.0或更高版本中,显然需要 screenSize

祝你好运

请注意,如果您使用 ViewPager 处理片段,那么它非常简单。您只需要调用这个方法: setOffscreenPageLimit()

根据文件:

在空闲状态下,设置应保留在视图层次结构中当前页的任一侧的页数。超出此限制的页将在需要时从适配器重新创建。

类似的问题

片段的 onSaveInstanceState(Bundle outState)将永远不会被调用,除非片段的活动调用它本身和附加的片段。因此,这个方法将不会被调用,直到某些东西(通常是旋转)将活动强制到 SaveInstanceState并在以后恢复它。 但是,如果您只有一个活动和大量的片段(大量使用 replace) ,并且应用程序只在一个定向活动中运行,那么可能很长时间都不会调用 onSaveInstanceState(Bundle outState)

我知道三种可能的解决办法。

第一:

使用片段的参数来保存重要数据:

public class FragmentA extends Fragment {
private static final String PERSISTENT_VARIABLE_BUNDLE_KEY = "persistentVariable";


private EditText persistentVariableEdit;


public FragmentA() {
setArguments(new Bundle());
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, null);


persistentVariableEdit = (EditText) view.findViewById(R.id.editText);


TextView proofTextView = (TextView) view.findViewById(R.id.textView);


Bundle mySavedInstanceState = getArguments();
String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);


proofTextView.setText(persistentVariable);




view.findViewById(R.id.btnPushFragmentB).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager()
.beginTransaction()
.replace(R.id.frameLayout, new FragmentB())
.addToBackStack(null)
.commit();
}
});


return view;
}


@Override
public void onPause() {
super.onPause();
String persistentVariable = persistentVariableEdit.getText().toString();


getArguments().putString(PERSISTENT_VARIABLE_BUNDLE_KEY, persistentVariable);
}
}

第二种方法,但不那么迂腐 在单例中保存变量

第三,不要 replace()片段,而要 add()/show()/hide()片段。

private ViewPager viewPager;
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {


@Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}


@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}


@Override
public void onPageScrollStateChanged(int arg0) {
}
});
}


@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}


@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// on tab selected
// show respected fragment view
viewPager.setCurrentItem(tab.getPosition());
}


@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}

对于包含列表视图的片段,我使用了一种混合方法。它看起来是性能良好的,因为我没有替换当前的片段,而是添加新的片段并隐藏当前的片段。在托管我的片段的活动中,我有以下方法:

public void addFragment(Fragment currentFragment, Fragment targetFragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setCustomAnimations(0,0,0,0);
transaction.hide(currentFragment);
// use a fragment tag, so that later on we can find the currently displayed fragment
transaction.add(R.id.frame_layout, targetFragment, tag)
.addToBackStack(tag)
.commit();
}

每当单击/点击一个列表项时,我都会在我的片段(包含列表视图)中使用这个方法(因此我需要启动/显示详细信息片段) :

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
SearchFragment currentFragment = (SearchFragment) fragmentManager.findFragmentByTag(getFragmentTags()[0]);
DetailsFragment detailsFragment = DetailsFragment.newInstance("some object containing some details");
((MainActivity) getActivity()).addFragment(currentFragment, detailsFragment, "Details");

getFragmentTags()返回一个字符串数组,当我添加一个新的片段时,这些字符串用作不同片段的标记(参见上面 addFragment方法中的 transaction.add方法)。

在包含列表视图的片段中,我使用其 onPuse ()方法执行以下操作:

@Override
public void onPause() {
// keep the list view's state in memory ("save" it)
// before adding a new fragment or replacing current fragment with a new one
ListView lv =  (ListView) getActivity().findViewById(R.id.listView);
mListViewState = lv.onSaveInstanceState();
super.onPause();
}

然后在片段的 onCreateView 中(实际上是在 onCreateView 中调用的方法中) ,恢复状态:

// Restore previous state (including selected item index and scroll position)
if(mListViewState != null) {
Log.d(TAG, "Restoring the listview's state.");
lv.onRestoreInstanceState(mListViewState);
}

我发现的最佳解决方案如下:

OnSavedInstanceState () : 总是在活动将要关闭时在片段内调用(将活动从一个片段移动到另一个片段或更改配置)。 因此,如果我们在同一个活动中调用多个片段,那么我们必须使用以下方法:

使用片段的 OnDestroyView ()并在该方法中保存整个对象。 然后 OnActivityCreated () : 检查 object 是否为 null (因为此方法每次都调用)。现在在这里恢复对象的状态。

它的作品永远!

我不认为 onSaveInstanceState是一个好的解决方案。它只是用于已经被破坏的活动。

从 android 3.0开始,Fragmen 已经成为 FragmentManager 的管理者,条件是: 一个活动映射许多片段,当片段在 backStack 中添加(而不是替换: 它将重新创建)时,视图将被删除。当返回到最后一个时,它会像以前一样显示。

所以我认为碎片管理器和交易足够好来处理它。

最后,我尝试了很多复杂的解决方案,因为我只需要在我的片段(EditText 的内容)中保存/恢复一个值,尽管它可能不是最优雅的解决方案,但是创建一个 SharedPreferences 并将我的状态存储在那里对我来说是有用的

仅仅一次充满你的视图

例如:

public class AFragment extends Fragment {


private View mRootView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(mRootView==null){
mRootView = inflater.inflate(R.id.fragment_a, container, false);
//......
}
return mRootView;
}

}

将字段的值保存在活动的不同片段中的一种简单方法

创建片段的实例并添加而不是替换和删除

    FragA  fa= new FragA();
FragB  fb= new FragB();
FragC  fc= new FragB();
fragmentManager = getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragmnt_container, fa);
fragmentTransaction.add(R.id.fragmnt_container, fb);
fragmentTransaction.add(R.id.fragmnt_container, fc);
fragmentTransaction.show(fa);
fragmentTransaction.hide(fb);
fragmentTransaction.hide(fc);
fragmentTransaction.commit();

然后只显示和隐藏片段,而不是添加和删除这些再次

    fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.hide(fa);
fragmentTransaction.show(fb);
fragmentTransaction.hide(fc);
fragmentTransaction.commit()

;