我正在开发一个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时,都会销毁一个片段,而另一个片段会被创建。
有没有办法阻止重新创建片段,而只是重用它们?
答案 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)
分离将破坏视图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