在android中,一个片段(例如FragA
)被添加到了backstack中,另一个片段(例如FragB
)进入了顶部。现在回击FragA
到顶部并被onCreateView()
调用。现在,我FragA
处于一种特殊的状态,然后FragB
才被推上去。
我的问题是如何恢复FragA
到以前的状态?有没有一种方法可以保存状态(例如在捆绑中说),如果可以,那么我应该重写哪种方法?
在android中,一个片段(例如FragA
)被添加到了backstack中,另一个片段(例如FragB
)进入了顶部。现在回击FragA
到顶部并被onCreateView()
调用。现在,我FragA
处于一种特殊的状态,然后FragB
才被推上去。
我的问题是如何恢复FragA
到以前的状态?有没有一种方法可以保存状态(例如在捆绑中说),如果可以,那么我应该重写哪种方法?
Answers:
在片段指南 FragmentList示例中,您可以找到:
@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);
}
}
我是Fragments的初学者,但似乎是您的问题的解决方案;)在片段从后堆栈返回后,将调用OnActivityCreated。
onSaveInstanceState
仅在其相应活动也关闭时调用。
片段的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()
代替。
Fragment.onSaveInstanceState()
是永远不会被调用。只需将您自己的数据保存到参数中,包括列表视图中的项目或其ID(如果您还有其他集中式数据管理器)。无需保存列表视图的位置-已保存并自动恢复。
String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);
始终是null
。有什么问题?
getArguments()
方法,包括在ViewPager中嵌套片段。我使用1个活动并交换许多片段进/出,这非常有效。这是一个简单的测试,供您审核所有建议的解决方案:1)从Fragment A转到Fragment B;2)改变设备方向两次;3)按设备上的后退按钮。
setArguments(new Bundle());
覆盖了旧的Bundle。因此,请确保仅创建一次片段,然后使用该实例,而不是每次都创建一个。
请注意,如果您使用ViewPager处理Fragments,则非常简单。您只需要调用此方法:setOffscreenPageLimit()
。
根据文档:
设置在空闲状态下视图层次结构中应保留到当前页面任一侧的页面数。超出此限制的页面将在需要时从适配器重新创建。
只需一次为您的视图充气。
示例如下:
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;
}
}
Fragment
指的是它的根视图不会这样做。尽管某些GC根元素将引用非ui线程变量,例如全局静态属性。一个Fragment
实例是不是GC-根,因此它可以被垃圾收集。因此,其根源也是如此。
我处理的问题与此非常相似。因为我知道我会经常来恢复回以前的片段,我检查了一下片段是否.isAdded()
是真实的,如果是这样,而不是做一个transaction.replace()
我只是做了transaction.show()
。如果片段已经在堆栈中,则可以防止片段被重新创建-无需保存状态。
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
显然是必需的。
祝好运
android:configChanges=everythinYouCanThinkOf|moreThingsYouFoundOnTheInternets
清单。相反,学习如何从这里保存和恢复状态,例如:speakerdeck.com/cyrilmottier/...
我发现的最佳解决方案如下:
onSavedInstanceState():总是在活动要关闭时在片段内部调用(将活动从一个移动到另一个或更改配置)。因此,如果我们在同一活动上调用多个片段,那么我们必须使用以下方法:
使用片段的OnDestroyView()并将整个对象保存在该方法内。然后OnActivityCreated():检查object是否为null(因为每次都会调用此方法)。现在,在此处还原对象的状态。
它的作品总是!
我对包含列表视图的片段使用了混合方法。因为我不替换当前片段而是添加新片段并隐藏当前片段,所以它似乎表现出色。在承载我的片段的活动中,我有以下方法:
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()
当我添加一个新片段时,返回一个字符串数组,用作不同片段的标签(请参见上面transaction.add
方法中的addFragment
方法)。
在包含列表视图的片段中,我通过其onPause()方法执行此操作:
@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);
}
在活动中保持不同片段中字段值的简单方法
创建片段实例并添加而不是替换和删除
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()
;
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) {
}