Android 如何在两个片段之间切换,而无需每次都重新创建片段?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22713128/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I switch between two fragments, without recreating the fragments each time?
提问by Tester101
I'm working on an android application, that uses a navigation drawer to switch between two fragments. However, each time I switch, the fragment is completely recreated.
我正在开发一个 android 应用程序,它使用导航抽屉在两个片段之间切换。但是,每次切换时,都会完全重新创建片段。
Here is the code from my main activity.
这是我的主要活动的代码。
/* 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);
}
I've set up some debug logging, and every time selectItem is called, one fragment is destroyed, while the other is created.
我已经设置了一些调试日志,每次调用 selectItem 时,都会销毁一个片段,而创建另一个片段。
Is there any way to prevent the fragments from being recreated, and just reuse them instead?
有什么办法可以防止片段被重新创建,而只是重用它们?
回答by Tester101
After @meredrica pointed out that replace() destroys the fragments, I went back through the FragmentManager documentation. This is the solution I've come up with, that seems to be working.
在@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);
}
I also added this bit, but I'm not sure if it's necessary or not.
我也添加了这一点,但我不确定是否有必要。
@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();
}
}
回答by Patrick Favre
Use the attach/detachmethod with tags:
Detach will destroy the view hirachy but keeps the state, like if on the backstack; this will let the "not-visible" fragment have a smaller memory footprint. But mind you that you need to correctly implement the fragment lifecycle (which you should do in the first place)
Detach 会破坏视图 hirachy 但保持状态,就像在 backstack 上一样;这将使“不可见”片段具有更小的内存占用。但是请注意,您需要正确实现片段生命周期(首先应该这样做)
Detach the given fragment from the UI. This is the same state as when it is put on the back stack: the fragment is removed from the UI, however its state is still being actively managed by the fragment manager. When going into this state its view hierarchy is destroyed.
从 UI 中分离给定的片段。这与将其放在返回堆栈上时的状态相同:片段已从 UI 中移除,但其状态仍由片段管理器主动管理。当进入这个状态时,它的视图层次结构被破坏。
The first time you add the fragment
第一次添加片段时
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.add(android.R.id.content, new MyFragment(),MyFragment.class.getSimpleName());
t.commit();
then you detach it
然后你把它拆开
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.detach(MyFragment.class.getSimpleName());
t.commit();
and attach it again if switched back, state will be kept
如果切换回来,再次附加它,状态将保持不变
FragmentTransaction t = getSupportFragmentManager().beginTransaction();
t.attach(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getSimpleName()));
t.commit();
But you always have to check if the fragment was added yet, if not then add it, else just attach it:
但是您始终必须检查是否已添加片段,如果没有则添加它,否则只需附加它:
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();
}
回答by meredrica
The replace method destroys your fragments. One workaround is to set them to Visibility.GONE
, another (less easy) method is to hold them in a variable. If you do that, make sure you don't leak memory left and right.
replace 方法会破坏您的片段。一种解决方法是将它们设置为Visibility.GONE
,另一种(不太容易的)方法是将它们保存在变量中。如果这样做,请确保不会左右泄漏内存。
回答by Adam Toth
I did this before like this:
我以前是这样做的:
if (mPrevFrag != fragment) {
// Change
FragmentTransaction ft = fragmentManager.beginTransaction();
if (mPrevFrag != null){
ft.hide(mPrevFrag);
}
ft.show(fragment);
ft.commit();
mPrevFrag = fragment;
}
(you will need to track your pervious fragment in this solution)
(您需要在此解决方案中跟踪您之前的片段)
回答by Drew
I guess you can not directly manipulate the lifecycle mechanisms of your Fragments
. The very fact that you can findFragmentByTag
is not very bad. It means that the Fragment object is not recreated fully, if it is already commited. The existing Fragment
just passes all the lifecycle steps each Fragment
has - that means that only UI is "recreated".
我猜你不能直接操纵你的Fragments
. 事实上,你可以findFragmentByTag
不是很糟糕。这意味着 Fragment 对象没有完全重新创建,如果它已经提交的话。现有的Fragment
只是通过了每个Fragment
具有的所有生命周期步骤- 这意味着只有 UI 被“重新创建”。
It is a very convenient and useful memory management strategy - and appropriate, in most cases. Fragment
which is gone, has the resources which have to be utilized in order to de-allocate memory.
这是一个非常方便和有用的内存管理策略——而且在大多数情况下是合适的。Fragment
它消失了,具有必须利用以取消分配内存的资源。
If you just cease using this strategy, the memory usage of your application could increase badly.
如果您只是停止使用此策略,您的应用程序的内存使用量可能会严重增加。
Nonetheless, there are retained fragments, which lifecycle is a bit different and do not correspond to the Activity they are attached to. Typically, they are used to retain some things you want to save, for example, to manage configuration changes
尽管如此,还是有保留的 Fragment,它们的生命周期有点不同,并且与它们所附加的 Activity 不对应。通常,它们用于保留一些您想要保存的内容,例如,管理配置更改
However, the fragment [re]creation strategy depends on the context - that is, what you would like to solve, and what are the trade-offs that you are willing to accept.
然而,片段[重新]创建策略取决于上下文——也就是说,你想解决什么问题,以及你愿意接受的权衡是什么。
回答by Dmytro Ubogyi
Just find the current fragment calling getFragmentById("id of your container") and then hide it and show needed fragment.
只需找到调用 getFragmentById("id of your container") 的当前片段,然后隐藏它并显示所需的片段。
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();
}
回答by KarteekKumarM
Same idea as Tester101 but this is what I ended up using.
与 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();
回答by Venkatesh Kashyap
this is a little late response. if you're using view pager for fragments, set the off screen page limit of the fragment to the number of fragments created.
这是一个有点晚的回应。如果您对片段使用视图寻呼机,请将片段的屏幕外页面限制设置为创建的片段数量。
mViewPager.setOffscreenPageLimit(3); // number of fragments here is 3
回答by shinjidev
How about playing with the Visible attribute?
使用 Visible 属性怎么样?