首页 文章

android片段 - 如何在片段上推送另一个片段时保存片段中的视图状态

提问于
浏览
128

在android中,一个片段(比如说 FragA )被添加到backstack中,而另一个片段(比如 FragB )就会出现在顶部 . 现在回击 FragA 来到顶部, onCreateView() 被调用 . 现在我已经 FragA 处于特定状态之前 FragB 被推到了它之上 .

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

11 回答

  • 21

    fragment guide 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 .

  • 0

    片段的 onSaveInstanceState(Bundle outState) 永远不会被调用,除非片段's activity call it on itself and attached fragments. Thus this method won'被调用,直到某些东西(通常是旋转)强制活动到 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() 而不是它们 .

  • 4

    请注意,如果您使用ViewPager处理Fragments,则非常简单 . 您只需要调用此方法: setOffscreenPageLimit() .

    符合文档:

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

    Simmilar issue here

  • 0

    Just inflate your View for once.

    例子如下:

    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;
    }
    

    }

  • 77

    我处理的问题非常类似于此 . 因为我知道我会经常回到之前的片段,我检查片段 .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 .

    祝好运

  • 5

    我找到的最佳解决方案如下:

    onSavedInstanceState():当活动要关闭时(从一个移动到另一个或配置更改),始终调用内部片段 . 因此,如果我们在同一个活动上调用多个片段,那么我们必须使用以下方法:

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

    它的作品永远!

  • 0

    如果您正在处理Android清单中指定的片段活动中的配置更改,就像这样

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

    那么片段的 onSaveInstanceState 将不会被调用, savedInstanceState 对象将始终为null .

  • -1

    我对包含列表视图的片段使用了混合方法 . 它似乎是高性能的,因为我不替换当前片段,而是添加新片段并隐藏当前片段 . 我在托管我的片段的活动中有以下方法:

    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 方法) .

    在包含列表视图的片段中,我在其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);
    }
    
  • 12

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

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

    所以我认为fragmentManger和transaction足以处理它 .

  • 8

    在尝试了许多这些复杂的解决方案之后,我只需要在Fragment中保存/恢复单个值(EditText的内容),虽然它可能不是最优雅的解决方案,但创建SharedPreference并存储我的状态那对我有用

  • 99
    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) {
    }
    

相关问题