【问题标题】:Multiple Fragments on Orientation Changes方向变化的多个片段
【发布时间】:2017-02-27 06:54:06
【问题描述】:

我在 Android 中遇到了一个烦人的任务,即在方向更改时维护片段的状态。

首先,我在 StackOver flow 上尝试了所有解决方案,但我只得到了一个我没有要求的好结果。

我能够维护 Fragment 中的更改,在 Orientation 更改时,但无法在用户切换 Fragment 时保存 Fragment 状态,因此我删除了此解决方案并寻找一个新的、更好的解决方案。

我的想法是显示/隐藏片段,而不需要整体替换它们,因为它们只会被隐藏一小段时间,如果它们不再可见,再次创建它们没有问题。

少说话,多写代码。

int mID = (int) drawerItem.getIdentifier();

                        String mTag = "";
                        switch (mID){
                            case 0:
                                mTag = "ViewPager";
                                break;
                            case 1:
                                mTag = "Browser";
                                break;
                            case 2:
                                mTag = "Settings";
                                break;
                        }

                        if (savedInstanceState == null) {
                            ShowHideFrags(mID);
                        } else {
                            switch (mID){
                                case 0:
                                    Log.i("ASDSADSA","4");
                                    mViewPager = (ViewPagerFragment) getSupportFragmentManager().findFragmentByTag(mTag);
                                    ShowHideFrags(mID);
                                    break;
                                case 1:
                                    Log.i("ASDSADSA","5");
                                    mWebFrag = (WVFragment) getSupportFragmentManager().findFragmentByTag(mTag);
                                    ShowHideFrags(mID);
                                    break;
                                case 2:
                                    Log.i("ASDSADSA","6");
                                    mSettings = (SettingsFragment) getSupportFragmentManager().findFragmentByTag(mTag);
                                    ShowHideFrags(mID);
                                    break;
                            }
                        }

此代码处理导航抽屉中的点击,我正在使用Mikepenz 的“Material Drawer”库。

如代码所示,我有三个片段,分别是ViewPagerBrowserSettings

我的问题是,它被重新创建,没有方向和getFragment 方法,代码中没有问题,但是当我添加对此更改的支持时,片段会再次重新创建。我已经尝试过多次更改代码,记录更改,看看有什么问题。

第一次启动时,它调用ShowHideFrags(int x)方法,然后转到else

ShowHideFrags(int x) 的代码:

private void ShowHideFrags(int SelectedFrag){
    if(mFragmentManager == null)
        mFragmentManager = getSupportFragmentManager();

    android.support.v4.app.FragmentTransaction ft = mFragmentManager.beginTransaction();
    switch (SelectedFrag){
        case 0:
            if(mViewPager == null)
                mViewPager = new ViewPagerFragment();

            if(!mViewPager.isAdded())
                ft.add(R.id.fragment,mViewPager,"Viewpager");

            if(!mViewPager.isVisible()){
                if((mSettings != null && mSettings.isVisible()))
                    ft.hide(mSettings);

                if((mWebFrag != null && mWebFrag.isVisible()))
                    ft.hide(mWebFrag);

                ft.show(mViewPager);
            } else {
                if((mSettings != null && mSettings.isVisible()))
                    ft.hide(mSettings);

                if((mWebFrag != null && mWebFrag.isVisible()))
                    ft.hide(mWebFrag);
            }

            ft.commit();
            break;
        case 1:
            if(mWebFrag == null)
                mWebFrag = new WVFragment();


            if(!mWebFrag.isAdded())
                ft.add(R.id.fragment,mWebFrag,"Browser");

            if(!mWebFrag.isVisible()) {
                if((mSettings != null && mSettings.isVisible()))
                    ft.hide(mSettings);

                if(mViewPager != null && mViewPager.isVisible())
                    ft.hide(mViewPager);

                ft.show(mWebFrag);

            } else {
                if((mSettings != null && mSettings.isVisible()))
                    ft.hide(mSettings);

                if((mViewPager != null && mViewPager.isVisible()))
                    ft.hide(mViewPager);
            }
            ft.commit();
            break;
        case 2:
            if(mSettings == null)
                mSettings = new SettingsFragment();

            if(!mSettings.isAdded())
                ft.add(R.id.fragment,mSettings,"Settings");

            if(!mSettings.isVisible()) {
                if((mViewPager != null && mViewPager.isVisible()))
                    ft.hide(mViewPager);

                if((mWebFrag != null && mWebFrag.isVisible()))
                    ft.hide(mWebFrag);

                ft.show(mSettings);
            } else {
                if((mViewPager != null && mViewPager.isVisible()))
                    ft.hide(mViewPager);

                if((mWebFrag != null && mWebFrag.isVisible()))
                    ft.hide(mWebFrag);
            }
            ft.commit();
            break;
    }
}

我的 onSaveInstanceState 的代码:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    try{
        long mSelectedItem = result.getCurrentSelection();
        android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
        android.support.v4.app.Fragment currentFragment = fragmentManager.findFragmentById(R.id.fragment);
        getSupportFragmentManager().putFragment(outState,currentFragment.getTag(),currentFragment);
        outState.putLong("SelectedItem",mSelectedItem);
    } catch (Exception e){
        e.printStackTrace();
    }


}

【问题讨论】:

    标签: android android-fragments


    【解决方案1】:

    所以在尝试了 4 天后,我坐下来喝了一杯好酒,然后我想出了解决这个烦人问题的方法。

    由于我在解释进度或步骤方面并不是那么出色,因此我会直截了当,并尽我所能像专业回答者一样。

    首先,由于我的应用程序有一个导航抽屉,我想显示/隐藏片段而不重新创建它们,在不花费太多时间的情况下完成这样的任务有点困难,所以我需要了解生命周期和发生了什么,因此我在每一步都添加了日志,以了解幕后发生的事情。

    当应用程序启动时,您只切换片段.show().hide(),捆绑包将为空。因此

    if (savedInstanceState == null) {
      LogMessage("Bundle Is Null, normal stuff");
      ShowHideFrags(mID);
    }
    

    最后会附上用到的方法ShowHideFrags,它的任务只是检查fragment是否为null,隐藏未选中的,显示选中的,如果为null,该方法将a 创建一个新片段并添加它。

    第一件事很简单,当定向发生时,bundle 不会为空,它会有保存的片段,首先我们需要将它保存在 bundle 中。

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        long mSelectedItem = result.getCurrentSelection();
    
        try{
            FragmentManager mFM = getSupportFragmentManager();
            Fragment currentFragment = null;
    
    
            switch (mTag){
                case "ViewPager":
                    currentFragment = mViewPager;
                    break;
                case "Browser":
                    currentFragment = mWebFrag;
                    break;
                case "Settings":
                    currentFragment = mSettings;
                    break;
            }
    
            mFM.putFragment(outState,mTag,currentFragment);
            LogMessage(String.format("The following fragment '%1$s' with position %2$s was saved",mTag,String.valueOf(mSelectedItem)));
        } catch (Exception e){
            e.printStackTrace();
        }
        outState.putLong("SelectedItem",mSelectedItem);
    }
    

    会问一个问题,为什么使用硬编码字符串而不将它们作为变量?因为我很懒。重要提示,不要通过findFragmentByTag 方法保存当前片段,它会给你最后应用的片段而不是选择的片段,因此我必须手动检查是否选择了哪些片段。

    别忘了提到片段,我在代码的开头有以下片段

    private ViewPagerFragment mViewPager;
    private WVFragment mWebFrag;
    private SettingsFragment mSettings;
    

    mTag 是片段标签,在导航抽屉的onClick 方法内,我通过以下代码添加了mTag 值:

    switch (mID) {
      case 0:
      mTag = "Viewpager";
      break;
    
      case 1:
      mTag = "Browser";
      break;
    
      case 2:
      mTag = "Settings";
      break;
    }
    

    现在,我们已经分配了 mTag,当 bundle 为 null 时检查完成,并且 bundle 保存部分也完成了,现在我们需要在 bundle 不为 null 时执行部分。

    先是代码,后是解释

    if(getSupportFragmentManager().findFragmentByTag(mTag) != null){
                                            LogMessage(String.format("Fragment with tag '%1$s' was found",mTag));
                                            mViewPager = (ViewPagerFragment) getSupportFragmentManager().findFragmentByTag(mTag);
                                            if(!mViewPager.isVisible()) {
                                                LogMessage("Not Visible");
                                                ShowHideFrags(mID);
                                            } else {
                                                LogMessage("Visible");
                                            }
                                        } else {
                                            LogMessage(String.format("Fragment with tag '%1$s' was NOT found", mTag));
    
                                               if(!ReturnCurrentFragment().equalsIgnoreCase(mTag)){
                                                LogMessage("Not the Same");
                                                ShowHideFrags(mID);
                                            } else {
                                                LogMessage("It's the Same");
                                            }
                                        }
    
    private Fragment ReturnCurrentFragment(){
        return getSupportFragmentManager().findFragmentById(R.id.fragment);
    }
    

    上面的代码在 else 子句中,当 bundle 不为空时。首先我们检查选择的片段(保存在包中)是否确实可以加载,如果是,我们分配它,如果它不可见,则表示其他片段可见,因此我们称之为ShowHideFrags .

    当片段可见时,我们什么都不做。此外,我们检查它是否不在捆绑包中,如果不存在,是否已经可以重新加载,所以我们不会重新创建它两次 *1。

    有了这段代码,我几乎处理了所有可能发生的恼人错误希望

    ShowHideFrags(int x)的代码:

    private void ShowHideFrags(int SelectedFrag){
        if(mFragmentManager == null)
            mFragmentManager = getSupportFragmentManager();
    
        android.support.v4.app.FragmentTransaction ft = mFragmentManager.beginTransaction();
        switch (SelectedFrag){
            case 0:
                if(mViewPager == null)
                    mViewPager = new ViewPagerFragment();
    
                if(!mViewPager.isAdded())
                    ft.add(R.id.fragment,mViewPager,"Viewpager");
    
                if(!mViewPager.isVisible()){
                    if((mSettings != null && mSettings.isVisible()))
                        ft.hide(mSettings);
    
                    if((mWebFrag != null && mWebFrag.isVisible()))
                        ft.hide(mWebFrag);
    
                    ft.show(mViewPager);
                } else {
                    if((mSettings != null && mSettings.isVisible()))
                        ft.hide(mSettings);
    
                    if((mWebFrag != null && mWebFrag.isVisible()))
                        ft.hide(mWebFrag);
                }
    
                ft.commit();
                break;
            case 1:
                if(mWebFrag == null)
                    mWebFrag = new WVFragment();
    
    
                if(!mWebFrag.isAdded())
                    ft.add(R.id.fragment,mWebFrag,"Browser");
    
                if(!mWebFrag.isVisible()) {
                    if((mSettings != null && mSettings.isVisible()))
                        ft.hide(mSettings);
    
                    if(mViewPager != null && mViewPager.isVisible())
                        ft.hide(mViewPager);
    
                    ft.show(mWebFrag);
    
                } else {
                    if((mSettings != null && mSettings.isVisible()))
                        ft.hide(mSettings);
    
                    if((mViewPager != null && mViewPager.isVisible()))
                        ft.hide(mViewPager);
                }
                ft.commit();
                break;
            case 2:
                if(mSettings == null)
                    mSettings = new SettingsFragment();
    
                if(!mSettings.isAdded())
                    ft.add(R.id.fragment,mSettings,"Settings");
    
                if(!mSettings.isVisible()) {
                    if((mViewPager != null && mViewPager.isVisible()))
                        ft.hide(mViewPager);
    
                    if((mWebFrag != null && mWebFrag.isVisible()))
                        ft.hide(mWebFrag);
    
                    ft.show(mSettings);
                } else {
                    if((mViewPager != null && mViewPager.isVisible()))
                        ft.hide(mViewPager);
    
                    if((mWebFrag != null && mWebFrag.isVisible()))
                        ft.hide(mWebFrag);
                }
                ft.commit();
                break;
        }
    }
    

    *1: 让我们删除那个代码块,例如,当片段不存在,并且那个块也不存在,并且你尝试切换时,所选片段可能会覆盖在前一个片段上,或者可能显示空白一,因此如果它等于当前片段,我们需要在其中显示HideFrags,因此我们不会再次重新创建它并且有两个相同的片段彼此重叠。

    希望此答案对您有所帮助,请随时提出和/或更正此答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多