Подтвердить что ты не робот

Как я могу переключаться между двумя фрагментами, не каждый раз воссоздавая фрагменты?

Я работаю над андроидным приложением, которое использует навигационный ящик для переключения между двумя фрагментами. Однако каждый раз, когда я переключаюсь, фрагмент полностью воссоздается.

Вот код из моей основной деятельности.

/* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        selectItem(position);
    }
}

private void selectItem(int position) {
    android.support.v4.app.Fragment fragment;
    String tag;
    android.support.v4.app.FragmentManager; fragmentManager = getSupportFragmentManager();

    switch(position) {
        case 0:
            if(fragmentManager.findFragmentByTag("one") != null) {
                fragment = fragmentManager.findFragmentByTag("one");
            } else {
                fragment = new OneFragment();
            }
            tag = "one";
            break;
        case 1:
            if(fragmentManager.findFragmentByTag("two") != null) {
                fragment = fragmentManager.findFragmentByTag("two");
            } else {
                fragment = new TwoFragment();
            }
            tag = "two";
            break;
    }

    fragment.setRetainInstance(true);
    fragmentManager.beginTransaction().replace(R.id.container, fragment, tag).commit();

    // update selected item and title, then close the drawer
    mDrawerList.setItemChecked(position, true);
    setTitle(mNavTitles[position]);
    mDrawerLayout.closeDrawer(mDrawerList);
}

Я установил несколько протоколов отладки, и каждый раз, когда вызывается selectItem, один фрагмент уничтожается, а другой создается.

Есть ли способ предотвратить воссоздание фрагментов и просто повторно использовать их?

4b9b3361

Ответ 1

После того, как @meredrica указала, что replace() уничтожает фрагменты, я вернулся через документацию FragmentManager. Это решение, с которым я столкнулся, кажется, работает.

/* The click listener for ListView in the navigation drawer */
private class DrawerItemClickListener implements ListView.OnItemClickListener {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        selectItem(position);
    }
}

private void selectItem(int position) {
    android.support.v4.app.FragmentManager; fragmentManager = getSupportFragmentManager();

    switch(position) {
        case 0:
            if(fragmentManager.findFragmentByTag("one") != null) {
                //if the fragment exists, show it.
                fragmentManager.beginTransaction().show(fragmentManager.findFragmentByTag("one")).commit();
            } else {
                //if the fragment does not exist, add it to fragment manager.
                fragmentManager.beginTransaction().add(R.id.container, new OneFragment(), "one").commit();
            }
            if(fragmentManager.findFragmentByTag("two") != null){
                //if the other fragment is visible, hide it.
                fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag("two")).commit();
            }
            break;
        case 1:
            if(fragmentManager.findFragmentByTag("two") != null) {
                //if the fragment exists, show it.
                fragmentManager.beginTransaction().show(fragmentManager.findFragmentByTag("two")).commit();
            } else {
                //if the fragment does not exist, add it to fragment manager.
                fragmentManager.beginTransaction().add(R.id.container, new TwoFragment(), "two").commit();
            }
            if(fragmentManager.findFragmentByTag("one") != null){
                //if the other fragment is visible, hide it.
                fragmentManager.beginTransaction().hide(fragmentManager.findFragmentByTag("one")).commit();
            }
            break;
    }

    // update selected item and title, then close the drawer
    mDrawerList.setItemChecked(position, true);
    setTitle(mNavTitles[position]);
    mDrawerLayout.closeDrawer(mDrawerList);
}

Я также добавил этот бит, но я не уверен, что это необходимо или нет.

@Override
public void onDestroy() {
    super.onDestroy();
    FragmentManager fragmentManager = getSupportFragmentManager();
    if(fragmentManager.findFragmentByTag("one") != null){
        fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("one")).commit();
    }
    if(fragmentManager.findFragmentByTag("two") != null){
        fragmentManager.beginTransaction().remove(fragmentManager.findFragmentByTag("two")).commit();
    }
}

Ответ 2

Используйте attach/detach метод с тегами:

Отсоединение уничтожит представление hirachy, но сохраняет состояние, как если бы оно было на задней стороне; это позволит "незавидному" фрагменту иметь меньший объем памяти. Но помните, что вам нужно правильно реализовать жизненный цикл фрагмента (который вы должны сделать в первую очередь)

Отсоедините данный фрагмент от пользовательского интерфейса. Это то же самое состояние, что и при наложении на задний стек: фрагмент удаляется из пользовательского интерфейса, однако его состояние по-прежнему активно управляется менеджером фрагментов. При входе в это состояние его иерархия представлений уничтожается.

При первом добавлении фрагмента

FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.add(android.R.id.content, new MyFragment(),MyFragment.class.getSimpleName());
t.commit();

тогда вы отсоедините его

FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.detach(MyFragment.class.getSimpleName());
t.commit();

и прикрепите его снова, если он будет включен, состояние будет сохранено

FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()));
t.commit();

Но вам всегда нужно проверить, добавлен ли фрагмент, если нет, добавьте его, иначе просто присоедините его:

if (getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()) == null) {
    FragmentTransaction t = getSupportFragmentManager().beginTransaction();
    t.add(android.R.id.content, new MyFragment(), MyFragment.class.getSimpleName());
    t.commit();
} else {
    FragmentTransaction t = getSupportFragmentManager().beginTransaction();
    t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()));
    t.commit();
}

Ответ 3

Метод замещения уничтожает ваши фрагменты. Один из способов - установить их на Visibility.GONE, другой (менее простой) метод - удерживать их в переменной. Если вы это сделаете, убедитесь, что вы не просачиваете память влево и вправо.

Ответ 4

Я сделал это раньше:

        if (mPrevFrag != fragment) {

            // Change
            FragmentTransaction ft = fragmentManager.beginTransaction();
            if (mPrevFrag != null){
                ft.hide(mPrevFrag);
            }
            ft.show(fragment);
            ft.commit();
            mPrevFrag = fragment;

        }

(вам нужно будет отследить ваш искомый фрагмент в этом решении)

Ответ 5

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

mViewPager.setOffscreenPageLimit(3); // number of fragments here is 3

Ответ 6

Я думаю, вы не можете напрямую манипулировать механизмами жизненного цикла вашего Fragments. Сам факт, что вы можете findFragmentByTag, не очень плох. Это означает, что объект Фрагмент не воссоздается полностью, если он уже совершен. Существующий Fragment просто передает все этапы жизненного цикла, каждый из которых имеет Fragment - это означает, что только пользовательский интерфейс "воссоздан".

Это очень удобная и полезная стратегия управления памятью - и в большинстве случаев она подходит. Fragment, который ушел, имеет ресурсы, которые необходимо использовать для де-распределения памяти.

Если вы просто прекратите использовать эту стратегию, использование памяти вашего приложения может сильно ухудшиться.

Тем не менее, существуют сохраненные фрагменты, причем этот жизненный цикл немного отличается и не соответствует активности, к которой они привязаны. Как правило, они используются для сохранения некоторых вещей, которые вы хотите сохранить, например, управлять изменениями конфигурации

Однако стратегия создания фрагмента [re] зависит от контекста - то есть того, что вы хотели бы решить, и каковы компромиссы, которые вы готовы принять.

Ответ 7

Та же идея, что и Tester101, но это то, что я использовал.

    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    Fragment oldFragment = fragmentManager.findFragmentByTag( "" + m_lastDrawerSelectPosition );
    if ( oldFragment != null )
        fragmentTransaction.hide( oldFragment );

    Fragment newFragment = fragmentManager.findFragmentByTag( "" + position );
    if ( newFragment == null )
    {
        newFragment = getFragment( position );
        fragmentTransaction.add( R.id.home_content_frame, newFragment, "" + position );
    }

    fragmentTransaction.show( newFragment );
    fragmentTransaction.commit();

Ответ 8

Как играть с атрибутом Visible?