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

时间:2011-07-22 07:45:33

标签: android android-fragments

在android中,一个片段(比如FragA)被添加到backstack中,另一个片段(比如FragB)出现在顶部。现在回击FragA到达顶部,onCreateView()被调用。现在我在FragA被推到它之前的某个特定状态FragB

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

12 个答案:

答案 0 :(得分:100)

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。

答案 1 :(得分:80)

片段的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()

答案 2 :(得分:21)

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

根据文档:

  

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

Similar issue here

答案 3 :(得分:13)

只需将您的视图充气一次。

示例如下:

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

}

答案 4 :(得分:8)

我处理的问题非常类似于此。因为我知道我经常会回到之前的片段,所以我检查了片段.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 :(得分:5)

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

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

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

它的作品永远!

答案 6 :(得分:4)

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

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

然后不会调用片段的onSaveInstanceStatesavedInstanceState对象将始终为null。

答案 7 :(得分:1)

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

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

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

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

在尝试了许多这些复杂的解决方案之后,我只需要在Fragment中保存/恢复单个值(EditText的内容),尽管它可能不是最优雅的解决方案,但创建了SharedPreference和存储我的状态为我工作

答案 10 :(得分:0)

在活动中保持不同片段中字段值的简单方法

创建片段实例,然后添加而不是替换和删除

    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()

;

答案 11 :(得分:-1)

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