如何在两个片段之间切换,而不是每次都重新创建片段?

时间:2014-03-28 12:38:33

标签: android android-fragments

我正在开发一个Android应用程序,它使用导航抽屉在两个片段之间切换。但是,每次切换时,片段都会被完全重新创建。

以下是我主要活动的代码。

/* 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时,都会销毁一个片段,而另一个片段会被创建。

有没有办法阻止重新创建片段,而只是重用它们?

10 个答案:

答案 0 :(得分:46)

在@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();
    }
}

答案 1 :(得分:16)

attach / detach方法与标记一起使用:

分离将破坏视图hirachy但保持状态,就像在后台上;这将让&#34;不可见&#34;片段具有较小的内存占用。但请注意,您需要正确实现片段生命周期(首先应该执行)

  

从UI中分离给定的片段。这与将其放在后台堆栈时的状态相同:片段从UI中删除,但其状态仍由片段管理器主动管理。进入此状态时,其视图层次结构将被销毁。

第一次添加片段

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

答案 2 :(得分:5)

替换方法会破坏你的碎片。一种解决方法是将它们设置为Visibility.GONE,另一种(不太简单)方法是将它们保存在变量中。如果这样做,请确保不要左右泄漏内存。

答案 3 :(得分:3)

我之前就是这样做的:

        if (mPrevFrag != fragment) {

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

        }

(您需要在此解决方案中跟踪您的过去片段)

答案 4 :(得分:2)

我猜你无法直接操纵Fragments的生命周期机制。事实上你可以findFragmentByTag并不是很糟糕。这意味着如果已经提交了Fragment对象,则不会完全重新创建它。现有的Fragment只传递每个Fragment具有的所有生命周期步骤 - 这意味着只有UI被重新创建&#34;。

这是一种非常方便和有用的内存管理策略 - 在大多数情况下是适当的。消失的Fragment具有必须使用的资源以便解除分配内存。

如果您停止使用此策略,应用程序的内存使用量可能会严重增加。

尽管如此,还有保留片段,其生命周期略有不同,与其附加的活动不对应。通常,它们用于保留您要保存的一些内容,例如,manage configuration changes

然而,片段[re]创建策略取决于上下文 - 也就是说,您想要解决的问题,以及您愿意接受的权衡取舍。

答案 5 :(得分:1)

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

答案 6 :(得分:0)

只需找到调用getFragmentById(“容器ID”)的当前片段,然后隐藏它并显示所需的片段。

private void openFragment(Fragment fragment, String tag) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        Fragment existingFragment = fragmentManager.findFragmentByTag(tag);
        if (existingFragment != null) {
            Fragment currentFragment = fragmentManager.findFragmentById(R.id.container);
            fragmentTransaction.hide(currentFragment);
            fragmentTransaction.show(existingFragment);
        }
        else {
            fragmentTransaction.add(R.id.container, fragment, tag);
        }
        fragmentTransaction.commit();
    }

答案 7 :(得分:0)

使用扩展程序轻松隐藏在kotlin中:

fun FragmentManager.present(newFragment: Fragment, lastFragment: Fragment? = null, containerId: Int) {
    if (lastFragment == newFragment) return

    val transaction = beginTransaction()
    if (lastFragment != null && findFragmentByTag(lastFragment.getTagg()) != null) {
        transaction.hide(lastFragment)
    }

    val existingFragment = findFragmentByTag(newFragment.getTagg())
    if (existingFragment != null) {
        transaction.show(existingFragment).commit()
    } else {
        transaction.add(containerId, newFragment, newFragment.getTagg()).commit()
    }
}

fun Fragment.getTagg(): String = this::class.java.simpleName

用法

supportFragmentManager.present(fragment, lastFragment, R.id.fragmentPlaceHolder)
lastFragment = fragment

答案 8 :(得分:-2)

如何使用Visible属性?

答案 9 :(得分:-2)

这是一个有点迟到的回应。 如果您正在使用视图寻呼机获取片段,请将片段的屏幕外页面限制设置为创建的片段数。

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