FragmentTransaction 隐藏/显示有时不起作用
Posted
技术标签:
【中文标题】FragmentTransaction 隐藏/显示有时不起作用【英文标题】:FragmentTransaction hide/show doesn't work sometimes 【发布时间】:2017-08-05 08:33:44 【问题描述】:我有一个带有底部导航选项卡的活动,这些选项卡正在更改其中的片段。当我在这些选项卡上来回单击时,它有时会停止工作。当我在其中放入一些日志时,代码执行得很好。但是片段没有被切换。
代码在 kotlin 中,但相当简单
fun showTabFragment(tag: String)
val currentFragment: Fragment? = supportFragmentManager.fragments?.lastOrNull()
var fragment = supportFragmentManager.findFragmentByTag(tag)
val fragmentExists = fragment != null
if (fragment == null)
when (tag)
TAG_LOGBOOK -> fragment = LogbookFragment()
TAG_RECIPES -> fragment = RecipesFragment()
TAG_PROFILE -> fragment = ProfileFragment()
else -> fragment = MeetingPlacesFragment()
val transaction = supportFragmentManager.beginTransaction()
if (currentFragment != null)
Log.i("jacek", "hiding " + currentFragment.javaClass.simpleName)
transaction.hide(currentFragment)
if (fragmentExists)
Log.i("jacek", "showing " + fragment.javaClass.simpleName)
transaction.show(fragment)
else
Log.i("jacek", "adding " + fragment.javaClass.simpleName)
transaction.add(R.id.container, fragment, tag)
transaction.commit()
碎片很重。我会尝试一些轻量级的,但在我看来这仍然不是问题。还有什么我可以尝试的吗?
我正在使用最新的支持库 - 25.2.0 另外我对替换片段不感兴趣,因为重点是添加交叉淡入淡出动画而不重新创建它们
【问题讨论】:
当你在同一个事务的同一个片段上调用hide()
然后show()
时可能有问题?就像currentFragment
指代与fragment
相同的片段时,这里肯定会发生这种情况。说到它,我认为声称supportFragmentManager.fragments
列表中的最后一个片段将是最后显示的片段(而不是最近添加的片段)是错误的。您应该遍历所有 fragments
并搜索 isVisible()
为 true 的一个,或者只存储最后显示的片段标签并稍后找到它。
【参考方案1】:
您需要重复使用您想要隐藏或显示的片段的同一实例。
private fun replaceFragment(fragment: Fragment)
supportFragmentManager.beginTransaction().apply
if (fragment.isAdded)
show(fragment)
else
add(R.id.fmFragmentContainer, fragment)
supportFragmentManager.fragments.forEach
if (it != fragment && it.isAdded)
hide(it)
.commit()
【讨论】:
【参考方案2】:@Ali 的回答很好,但想象一下如果你有 5 个片段。这是显示/隐藏片段的另一种方式:
// in BaseFragment
public abstract String getTAG();
//in FragmentA, FragmentB and FragmentC
public String getTAG()
return TAG;
//Activity containing the fragments
//android.support.v4.app.Fragment;
private FragmentA fragmentA; //inherited BaseFragment
private FragmentB fragmentB; //inherited BaseFragment
private FragmentC fragmentC; //inherited BaseFragment
private ConcurrentHashMap<String,BaseFragment> mapOfAddedFragments = new ConcurrentHashMap<>();
/**
* Displays fragment A
*/
private void displayFragmentA()
displayFragment(fragmentA)
/**
* Displays fragment B
*/
private void displayFragmentB()
displayFragment(fragmentB)
/**
* Displays fragment C
*/
private void displayFragmentC()
displayFragment(fragmentC)
/**
* Loads a fragment using show a fragment
* @param fragment
*/
private void displayFragment(BaseFragment fragment)
if(!mapOfAddedFragments.containsKey(fragment.getTAG()))
mapOfAddedFragments.put(fragment.getTAG(), fragment);
showFragment(fragment.getTAG(), R.id.containerBody);
/**
* Displays a fragment and hides all the other ones
* @param fragmentTag is the tag of the fragment we want to display
*/
private void showFragment(String fragmentTag, @IdRes int containerViewId)
FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
BaseFragment fragment = null;
fragment = mapOfAddedFragments.get(fragmentTag);
if(fragment != null)
if (fragment.isAdded())
ft.show(fragment);
else //fragment needs to be added to the frame container
ft.add(containerViewId, fragment, fragment.getTAG());
else //the chosen fragment doesn't exist
return;
//we hide the other fragments
for (ConcurrentHashMap.Entry<String, BaseFragment> entry : mapOfAddedFragments.entrySet())
if(!entry.getKey().equals(fragmentTag))
BaseFragment fragmentTemp = entry.getValue();
// Hide the other fragments
if(fragmentTemp != null)
if(fragmentTemp.isAdded())
ft.hide(fragmentTemp);
//commit changes
ft.commit();
要实例化它们,您可以在活动的 onCreate() 方法中执行此操作:
//don't forget to get the .TAG elsewhere before using them here
//never call them directly
private void instantiateFragments(Bundle inState)
if (inState != null)
fragmentA = inState.containsKey(FragmentA.TAG) ?
(FragmentA) getSupportFragmentManager().getFragment(inState, FragmentA.TAG):
FragmentA.newInstance(FragmentA.TAG,"0");
fragmentB = inState.containsKey(FragmentB.TAG) ?
(FragmentB) getSupportFragmentManager().getFragment(inState, FragmentB.TAG):
FragmentB.newInstance(FragmentB.TAG,"1");
fragmentc = inState.containsKey(FragmentC.TAG) ?
(FragmentC) getSupportFragmentManager().getFragment(inState, FragmentC.TAG):
FragmentC.newInstance(FragmentC.TAG,"2");
else
fragmentA = FragmentA.newInstance(FragmentA.TAG,"0");
fragmentB = FragmentB.newInstance(FragmentB.TAG,"1");
fragmentc = FragmentC.newInstance(FragmentC.TAG,"2");
根据 Shujaat Ali Khan 的问题进行编辑:
BaseFragment 扩展了 support4 片段:
public abstract class BaseFragment extends Fragment
public abstract String getTAG();
//whatever we can add to be inherited
例如片段A:
public class FragmentA extends BaseFragment
// Store instance variables
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
public static final String TAG = "FragmentA";
// newInstance constructor for creating fragment with arguments
public static FragmentA newInstance(String param1, String param2)
FragmentA fragment = new FragmentA();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
if (getArguments() != null)
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
View view = inflater.inflate(R.layout.fragmentA, container, false);
return view;
//other lifecycle methods
@Override
public String getTAG()
return TAG;
最后,R.id.containerBody
是 FrameLayout 的 id,其中包含包含这些片段的 Activity 中的片段。
【讨论】:
您能至少展示一下 FragmentA 类的实现吗?由于我是初学者,所以我很困惑如何定义 NewInstance() 方法。 @ShujaatAliKhan 我刚刚编辑了答案以添加示例。【参考方案3】:这里的问题是,即使您隐藏了“当前”片段,内存中也会加载其他片段,这会导致不一致的行为。
您应该能够通过隐藏除要显示的片段之外的所有片段来解决此问题。
感谢这个答案。 Show hide fragment in android
例如:
private FragmentA fragmentA;
private FragmentB fragmentB;
private FragmentC fragmentC;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentA = FragmentA.newInstance();
fragmentB = FragmentB.newInstance();
fragmentC = FragmentC.newInstance();
protected void displayFragmentA()
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (fragmentA.isAdded())
ft.show(fragmentA);
else
ft.add(R.id.fragement_container, fragmentA);
if (fragmentB.isAdded()) ft.hide(fragmentB);
if (fragmentC.isAdded()) ft.hide(fragmentC);
ft.commit();
同样,您必须为 displayFragmentB() 和 displayFragmentC() 编写函数
【讨论】:
以上是关于FragmentTransaction 隐藏/显示有时不起作用的主要内容,如果未能解决你的问题,请参考以下文章
FragmentTransaction.replace() 淡入过渡显示“幽灵”片段
安卓x。 FragmentTransaction 动画在 backpress 上不起作用
Android中,FragmentTransaction类的replace()方法的作用是啥?
操作栏在 FragmentTransaction replace() 和 FragmentTransaction add() 上的行为方式