java - 使用 FragmentStateAdapter 实现 ViewPager2 的问题
问题描述
我来自 ViewPager 和 FragmentPagerAdapter,它有一个非常直接的实现,就像这样。
public class FragmentAdapters extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public FragmentAdapters(@NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
@NonNull
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
}
父片段
adapter = new FragmentAdapters(getChildFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
CoinsFragment coinsFragment = new AssetFragment();
NewsFragment newsFragment = new NewsFragment();
VideoFragment videoFragment = new VideoFragment();
adapter.addFragment(coinsFragment, getString(R.string.asset));
adapter.addFragment(newsFragment, getString(R.string.news));
adapter.addFragment(videoFragment, getString(R.string.videos));
mViewPager.setAdapter(adapter);
mViewPager.setOffscreenPageLimit(2);
Android 建议远离 ViewPager 并弃用心爱的 FragmentPagerAdapter,现在我正在尝试将 ViewPager2 与 FragmentStateAdapter 一起使用,但在其工作方式方面存在很多困惑和困难。
这是我的实现
class AppFragmentAdapter(private val fragmentList: MutableList<Pair<String, Fragment>>, fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
private val pageIds = fragmentList.map { fragmentList.hashCode().toLong() }
override fun getItemCount(): Int = fragmentList.size
override fun createFragment(position: Int): Fragment = when (position) {
0 -> {
Log.wtf("WTF", fragmentList[position].first + " " + position)
fragmentList[position].second
}
1 -> {
Log.wtf("WTF", fragmentList[position].first + " " + position)
fragmentList[position].second
}
2 -> {
Log.wtf("WTF", fragmentList[position].first + " " + position)
fragmentList[position].second
}
else -> throw IllegalStateException("Invalid adapter position")
}
fun getFragmentName(position: Int) = fragmentList[position].first
// fun addFragment(fragment: Pair<String, Fragment>) {
// fragmentList.add(fragment)
// notifyItemInserted(fragmentList.size)
// notifyItemRangeChanged(fragmentList.size, fragmentList.size)
// notifyDataSetChanged()
// }
//
// fun removeFragment(position: Int) {
// fragmentList.removeAt(position)
// notifyItemRemoved(position)
// notifyItemRangeChanged(fragmentList.size, fragmentList.size)
// notifyDataSetChanged()
// }
override fun getItemId(position: Int): Long {
return pageIds[position] // Make sure notifyDataSetChanged() works
}
override fun containsItem(itemId: Long): Boolean {
return pageIds.contains(itemId)
}
}
通常我们可以立即从createFragment
方法中的列表中返回片段,但是有一个巨大的混乱,所以我试图扩展它以查看发生了什么。令我感到惊讶的是,何时createFragment
被调用一 (1) 次,而不是有多少可用片段返回,getItemCount
因此只显示一个片段,并且更令人困惑,我所谓的第一个片段 (AssetFragment) 放在最后位置和前两个片段是空屏幕,我不知道这两个片段来自哪里。
父片段
val fragmentList : MutableList<Pair<String,Fragment>> = ArrayList()
fragmentList.add(Pair(getString(R.string.assets), AssetFragment.newInstance()))
fragmentList.add(Pair(getString(R.string.news), NewsFragment.newInstance()))
fragmentList.add(Pair(getString(R.string.videos), VideosFragment.newInstance()))
val adapter = AppFragmentAdapter(fragmentList, requireActivity())
viewPager.adapter = adapter
viewPager.offscreenPageLimit = 2
这就是我的 Fragment 的样子,基本上没什么特别的
class AssetFragment : BaseFragment() {
companion object {
fun newInstance() = AssetFragment()
}
private lateinit var viewModel: AssetViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.asset_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(this).get(AssetViewModel::class.java)
// TODO: Use the ViewModel
}
}
有人可以告诉我这种荒谬的行为是什么吗?PS我试图做一些日志,getItemCount
它被调用了23次只是为了一个3片段?
解决方案
这很奇怪,因为覆盖
getItemId()
containsItem()
当使用我拥有的不同类型的 Fragments 类时,只会给出这种不良行为。
最后我只需要一个FragmentStateAdapter
像这样的简单类
class AppFragmentAdapter(private val fragmentList: MutableList<Pair<String, Fragment>>, fragment: Fragment) : FragmentStateAdapter(fragment) {
// private var pageIds = fragmentList.map { fragmentList.hashCode().toLong() }
override fun getItemCount(): Int = fragmentList.size
override fun createFragment(position: Int): Fragment {
return fragmentList[position].second
}
// override fun getItemId(position: Int): Long = pageIds[position] // Make sure notifyDataSetChanged() works
// override fun containsItem(itemId: Long): Boolean = pageIds.contains(itemId)
fun getFragmentName(position: Int) = fragmentList[position].first
fun addFragment(fragment: Pair<String, Fragment>) {
fragmentList.add(fragment)
notifyDataSetChanged()
}
fun removeFragment(position: Int) {
fragmentList.removeAt(position)
notifyDataSetChanged()
}
}
太糟糕了,我正在寻找关于如何使 ViewPager2 动态化的直接答案,而无需先尝试我能想到的最简单的方法。SO上的许多回答指出,getItemId()
在containsItem()
ViewPager2上添加或删除片段时需要覆盖,这让人头疼了近2天。感觉被背叛了。
推荐阅读
- mongodb - MongoDB count() 查询在 mongo shell 中返回过时的结果
- excel - 在“Workbook_Open”中合并两个宏
- python - 使用 Tkinter OptionMenu 小部件的数据库查询
- mysql - Typeorm groupBy 按 orderBy 另一列获取最后一行
- c# - 多个对象引用一个中心数据对象的设计模式是什么?
- javascript - Odoo 显示通知
- python - 考虑到OpenCV在使用WarpAffine旋转时向图像添加黑色边框,如何旋转坐标?
- java - 假设列大小相同,如何在没有数组副本的情况下在 Java 中附加和预先添加二维数组?
- c++ - 在 C++ 中将图形插入 HTML 文件
- c++ - 有没有办法只循环 jpg 文件?