Android: поток master / detail (двойная панель) с использованием 1 действия

как сообщает руководство для Android, двойной панели может быть достигнуто двумя способами:

  1. несколько фрагментов, один активность
  2. несколько фрагментов, несколько деятельность

Я использую первым делом (руководство Android объясняет только второй случай).

вот что происходит на 7" планшетов!--8-->:

  • поворот от пейзажа к портрету: воссоздается только фрагмент одной панели
  • поворот от портрета к пейзажу: все 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();
    }
}

2 ответов


я обнаружил, что это лучше чтобы добавить фрагменты в коде также для двойной панели.

Итак, вместо использования <fragment>, также использовать <FrameLayout> также для двухпанельного 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();
}

при вращении текущие активные фрагменты будут сохранены FragmentManager и использованы для автоматического воссоздания фрагментов при создании нового действия. Вы можете предотвратить отдых, не передавая 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, который вы хотите восстановить. Восстановление сохраненных фрагментов также зависит от вызова FragmentActivity.onCreate (savedInstanceState) с saveInstanceState != недействительный.

отдых происходит, потому что обычно вы хотите сохранить активный фрагмент вокруг (и, возможно, добавить вторую панель сведений в случае планшетов).

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