Android的片段 – 如何保存片段中的视图的状态,当另一个片段被推上它的顶部
在android中,一个片段(比如说FragA
)被添加到backstack中,另一个片段(比如FragB
)到达顶端。 现在打回FragA
来到顶部和onCreateView()
被调用。 现在我把FragA
置于一个特定的状态,然后FragB
被推到了最前面。
我的问题是如何将FragA
恢复到之前的状态? 有没有一种方法来保存状态(如在一个捆绑说),如果是的话,那么我应该重写哪种方法?
在片段指南 FragmentList的例子中,你可以find:
@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); } }
我是碎片的初学者,但它似乎是你的问题的解决scheme;)OnActivityCreated从片段返回栈后调用。
片段的onSaveInstanceState(Bundle outState)
永远不会被调用,除非片段的活动调用它本身和附加片段。 因此,这个方法将不会被调用,直到某些事件(通常是旋转)将活动强制为SaveInstanceState
并稍后恢复。 但是,如果你只有一个活动和大量的片段(大量使用replace
),并且应用程序只能在一个方向上运行,那么activity的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); } }
第二个但不那么迂腐的方法 – 把variables保存在单例中
第三 – 不要replace()
片段,而是add()
/ show()
/ hide()
它们。
只要注意,如果你使用ViewPager处理碎片,这很容易。 你只需要调用这个方法: setOffscreenPageLimit()
。
根据文档调整:
在空闲状态下,将视图层次结构中应保留在当前页面任一侧的页数。 超出此限制的页面将在需要时从适配器重新创build。
Simmilar问题在这里
只是膨胀你的观点一次。
例如:
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; }
}
我处理了一个与此非常相似的问题。 因为我知道我会经常返回到前一个片段,所以我检查了片段.isAdded()
是否为true,如果是这样,而不是做一个transaction.replace()
我只是做一个transaction.show()
。 如果已经在堆栈上,这将保持片段不被重新创build – 不需要状态保存。
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();
另外需要注意的是,虽然这会保留碎片本身的自然顺序,但您仍然可能需要处理被定向(config)更改而被销毁和重新创build的活动。 在AndroidManifest.xml中为你的节点解决这个问题:
android:configChanges="orientation|screenSize"
在Android 3.0及更高版本中, screenSize
显然是必需的。
祝你好运
我发现的最佳解决scheme如下:
onSavedInstanceState():在活动closures时总是调用内部片段(将活动从一个移动到另一个或configuration更改)。 所以如果我们在相同的活动上调用多个片段,那么我们必须使用以下方法:
使用片段的OnDestroyView()并将整个对象保存在该方法中。 然后OnActivityCreated():检查对象是否为null(因为此方法每次调用)。 现在恢复对象的状态。
它的作品总是!
如果你正在处理Android清单中指定的片段活动中的configuration更改,像这样
<activity android:name=".courses.posts.EditPostActivity" android:configChanges="keyboardHidden|orientation" android:screenOrientation="unspecified" />
那么片段的onSaveInstanceState
将不会被调用,并且savedInstanceState
对象将始终为空。
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) { }
我使用了一个包含列表视图的片段的混合方法。 它似乎是高性能的,因为我不replace当前的片段,而是添加新的片段,并隐藏当前的片段。 在托pipe我的碎片的活动中,我有以下方法:
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()
返回一个string数组,当我添加一个新的片段时(见上面的addFragment
方法中的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); }
我不认为onSaveInstanceState
是一个很好的解决scheme。 它只是用于已经破坏的活动。
从android 3.0开始,Fragmen已经被FragmentManagerpipe理,条件是:一个活动映射manny片段,当在backStack中添加片段(而不是replace:将被重新创build)时,视图将被注销。 当回到最后一个,它会像以前一样显示。
所以我认为fragmentManger和交易是足够好的来处理它。
最后,在尝试了许多这些复杂的解决scheme之后,我只需要在Fragment(EditText的内容)中保存/恢复单个值,虽然它可能不是最优雅的解决scheme,但创buildSharedPreference并存储我的状态那里为我工作