使用 FragmentStateAdapter 实现 ViewPager2 的问题
Issue on implementing ViewPager2 with FragmentStateAdapter
我来自 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)
}
}
通常我们可以 return 立即从 createFragment
方法中的列表中提取片段,但存在巨大的混乱,因此我尝试扩展它以查看发生了什么。令我惊讶的是,当 createFragment
被调用一 (1) 次而不是有多少片段可用时 return 被 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
}
}
谁能告诉我这是什么荒谬的行为?
P.S。我试着用 getItemCount
做一些日志,它被调用了 23 次只是为了一个 3 片段?
这很奇怪,因为覆盖
getItemId()
containsItem()
在使用不同种类的片段时只会出现这种不良行为class 我有。
最后我只需要一个简单的FragmentStateAdapter
class像这样
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 的回答指出,在 ViewPager2 上添加或删除 Fragment 时需要覆盖 getItemId()
和 containsItem()
,这让我们头疼了将近 2 天。感觉被背叛了。
我来自 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)
}
}
通常我们可以 return 立即从 createFragment
方法中的列表中提取片段,但存在巨大的混乱,因此我尝试扩展它以查看发生了什么。令我惊讶的是,当 createFragment
被调用一 (1) 次而不是有多少片段可用时 return 被 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
}
}
谁能告诉我这是什么荒谬的行为?
P.S。我试着用 getItemCount
做一些日志,它被调用了 23 次只是为了一个 3 片段?
这很奇怪,因为覆盖
getItemId()
containsItem()
在使用不同种类的片段时只会出现这种不良行为class 我有。
最后我只需要一个简单的FragmentStateAdapter
class像这样
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 的回答指出,在 ViewPager2 上添加或删除 Fragment 时需要覆盖 getItemId()
和 containsItem()
,这让我们头疼了将近 2 天。感觉被背叛了。