【问题标题】:Android: master/detail flow (dual-pane) using 1 activityAndroid:使用 1 个活动的主/详细信息流(双窗格)
【发布时间】:2014-10-15 07:40:51
【问题描述】:

正如Android guide 所报告的,双窗格可以通过两种方式实现:

  1. 多个片段,一个活动
  2. 多个片段,多个活动

我使用的是第一种情况(Android 指南只解释了第二种情况)。

7" 平板电脑会发生这种情况:

  • 从横向旋转到纵向:仅重新创建单窗格片段
  • 从纵向旋转到横向:重新创建所有 3 个片段(单窗格、双窗格主、双窗格细节)

问题:为什么是单窗格片段(我以编程方式创建,但使用布局中定义的 FrameLayout 作为容器)在双窗格上重新创建?

我在下面报告我的实现:

/layout/activity_main.xml:

<FrameLayout
    android:id="@+id/single_pane"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

/layout-w900dp/activity_main.xml:

<LinearLayout
    android:id="@+id/dual_pane"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment class="com.example.MasterFragment"
        android:id="@+id/master_dual"
        android:tag="MASTER_FRAGMENT_DUAL_PANE"
        android:layout_width="@dimen/master_frag_width"
        android:layout_height="match_parent"/>
    <fragment class="com.example.DetailFragment"
        android:id="@+id/detail_dual"
        android:tag="DETAIL_FRAGMENT_DUAL_PANE"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

这是主活动中的onCreate

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mDualPane = findViewById(R.id.dual_pane)!=null;

    FragmentManager fm = getFragmentManager();
    if (savedInstanceState==null) {
        // this is a non-UI fragment I am using for data processing purposes
        fm.beginTransaction().add(new NonUiFragment(), DATA_FRAGMENT).commit();
    }
    if (!mDualPane && fm.findFragmentById(R.id.single_pane)==null) {
        fm.beginTransaction().add(R.id.single_pane, new MasterFragment(), MASTER_FRAGMENT_SINGLE_PANE).commit();
    }
}

【问题讨论】:

    标签: android android-fragments tablet master-detail fragmentmanager


    【解决方案1】:

    我发现在代码中也为双窗格添加片段会好多了

    因此,不要使用&lt;fragment&gt;,也可以将&lt;FrameLayout&gt; 用于双窗格 XML。

    /layout-w900dp/activity_main.xml:

    <LinearLayout
        android:id="@+id/dual_pane"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <FrameLayout
            android:id="@+id/master_dual"
            android:layout_width="@dimen/master_frag_width"
            android:layout_height="match_parent"/>
        <FrameLayout
            android:id="@+id/detail_dual"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
    

    通过这种方式,您可以只使用一个 masterFragment 和 DetailFragment 实例,因此您不会陷入同一片段的多个实例的问题。

    为此,您需要在OnCreate 中将片段添加到容器中,从旧容器中分离:

        mDualPane = findViewById(R.id.dual_pane)!=null;
    
        if (savedInstanceState!=null) {
            mLastSinglePaneFragment = savedInstanceState.getString("lastSinglePaneFragment");
        }
    
        FragmentManager fm = getSupportFragmentManager();
    
        if (!mDualPane && fm.findFragmentById(R.id.single_pane)==null) {
            MasterFragment masterFragment = getDetatchedMasterFragment(false);
            fm.beginTransaction().add(R.id.single_pane, masterFragment, MASTER_FRAGMENT).commit();
            if (mLastSinglePaneFragment==DETAIL_FRAGMENT) {
                openSinglePaneDetailFragment();
            }
        }
        if (mDualPane && fm.findFragmentById(R.id.master_dual)==null) {
            MasterFragment masterFragment = getDetatchedMasterFragment(true);
            fm.beginTransaction().add(R.id.master_dual, masterFragment, MASTER_FRAGMENT).commit();
        }
        if (mDualPane && fm.findFragmentById(R.id.detail_dual)==null) {
            DetailFragment detailFragment = getDetatchedDetailFragment();
            fm.beginTransaction().add(R.id.detail_dual, detailFragment, DETAIL_FRAGMENT).commit();
        }
    

    使用这些函数:

    public static final String MASTER_FRAGMENT = "MASTER_FRAGMENT";
    public static final String DETAIL_FRAGMENT = "DETAIL_FRAGMENT";
    
    private MasterFragment getDetatchedMasterFragment(boolean popBackStack) {
        FragmentManager fm = getSupportFragmentManager();
        MasterFragment masterFragment = getSupportFragmentManager().findFragmentByTag(MASTER_FRAGMENT);
        if (masterFragment == null) {
            masterFragment = new MasterFragment();
        } else {
            if (popBackStack) {
                fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
            }
            fm.beginTransaction().remove(masterFragment).commit();
            fm.executePendingTransactions();
        }
        return masterFragment;
    }
    
    private DetailFragment getDetatchedDetailFragment() {
        FragmentManager fm = getSupportFragmentManager();
        DetailFragment detailFragment = getSupportFragmentManager().findFragmentByTag(DETAIL_FRAGMENT);
        if (detailFragment == null) {
            detailFragment = new DetailFragment();
        } else {
            fm.beginTransaction().remove(detailFragment).commit();
            fm.executePendingTransactions();
        }
        return detailFragment;
    }
    
    private void openSinglePaneDetailFragment() {
        FragmentManager fm = getSupportFragmentManager();
        fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        DetailFragment detailFragment = getDetatchedDetailFragment();
        FragmentTransaction fragmentTransaction = fm.beginTransaction();
        fragmentTransaction.replace(R.id.single_pane, detailFragment, DETAIL_FRAGMENT);
        fragmentTransaction.addToBackStack(null);
        fragmentTransaction.commit();
    }
    

    【讨论】:

      【解决方案2】:

      当你旋转时,FragmentManager 会保存当前活动的 Fragment,并用于在创建新 Activity 时自动重新创建 Fragment。您可以通过不将 savedInstanceState 传递给 super 方法来防止重新创建。例如。 super.onCreate(null);

      或者,如果您需要使用 FragmentActivity.onCreate(savedInstanceState) 方法(该方法调用 FragmentManager.restoreAllState() — 参见 https://github.com/android/platform_frameworks_base/blob/master/core/java/android/app/FragmentManager.java#L1759)恢复状态,您可以查找片段标签并在 onCreate 中手动删除它。之所以如此,是因为您有一个要恢复的非 ui 片段。保留 Fragment 的恢复还取决于使用 saveInstanceState != null 调用 FragmentActivity.onCreate(savedInstanceState)。

      重新创建是因为通常您希望保留活动片段(并且可能在平板电脑的情况下添加第二个详细信息窗格)。

      if (mDualPane) {
          Fragment singlePane = getFragmentManager().findFragmen‌​tByTag(MASTER_FRAGMENT_SINGLE_PANE);
          if (singlePane != null)
              getFragmentManager().beginTransaction().remove(fragment).commit(); 
      }
      

      【讨论】:

      • 嗨 Pierre-Antoine,我意识到所有 3 个片段都由片段管理器保存。但我不明白为什么单窗格片段应该在陆地空间上重新创建(尽管它的容器不在布局中),而双窗格片段却没有正确地在纵向上重新创建(因为它们不在布局)。
      • 我找不到实现你所说的方法。我正在检测它是基于R.id.dual_pane 布局的双窗格还是单窗格。我只有在setContentView() 之后才有这个信息,我猜应该是在super.OnCreate() 之后。
      • 与容器是否存在于你的内容视图无关。 FragmentManager 创建在方向更改导致 Fragment 被销毁后处于活动状态的所有 Fragment 的新实例。在 FragmentActivity.onCreate() 中,调用 mFragments.restoreAllState() 将实例化包中任何先前活动的 Fragment。见github.com/android/platform_frameworks_base/blob/master/core/…。为什么你不能保持你的代码原样并用 super.onCreate(null) 替换第一行的调用?
      • 您没有使用 savedInstanceState 并且每次方向更改都会替换活动的 Fragment,因此父 Activity 不需要恢复任何状态。
      • 我实际上也在使用非 UI 片段。我只是将它添加到问题中的代码中。我想我需要为此恢复活动状态,对吧?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-03
      • 1970-01-01
      • 2013-08-09
      • 2014-10-14
      • 2015-10-20
      • 1970-01-01
      相关资源
      最近更新 更多