关于使用FragmentStateAdapter实现ViewPager2的问题 [英] Issue on implementing ViewPager2 with FragmentStateAdapter

查看:312
本文介绍了关于使用FragmentStateAdapter实现ViewPager2的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我来自带有PagePageAdapter的ViewPager,它具有非常简单的实现方式.

I am from ViewPager with FragmentPagerAdapter which has a very straight forward implementation like this.

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,现在我正尝试将FragmentStateAdapter与ViewPager2一起使用,但是在工作方式上存在很多困惑和困难.

Android recommends moving away from ViewPager and deprecating the beloved FragmentPagerAdapter, now I am trying to work with ViewPager2 with FragmentStateAdapter but having a lot of confusion and difficulty in terms of how it works.

这是我的实现方式

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)放在最后一个位置,而前两个片段是空白屏幕,我不知道这两个片段的来源.

Normally we could just return immediately the fragment from the list in createFragment method but there is a huge confusion so I tried to expand it to see what is happening. What surprised me is when createFragment is getting called one (1) time and not by how many fragments is available return by getItemCount thus only one fragment is being shown and to make it even more confusing my supposedly first fragment (AssetFragment) was put at the very last position and the first two fragment is empty screen in which I do not know where that two fragment even came from.

父母片段

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

这是我的片段的外观,基本上没有什么特别之处

This is what my Fragment looks like which basically nothing special

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 制作一些日志,而只是为3个片段而被调用了23次?

May someone enlighten me what is this ridiculous behavior? P.S. I tried to make some log with getItemCount and it was being called 23 times just for a 3 Fragment?

推荐答案

这很不可思议

getItemId()
containsItem()

在使用我拥有的不同种类的Fragments类时,只会给出这种不良的行为.

will just give this undesirable behavior when using different kinds of Fragments class I have.

最后,我只需要一个像这样的简单 FragmentStateAdapter

In the end all I need was a simple FragmentStateAdapter class like this

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()天.感觉被出卖了.

Too bad I search for an immediate answer on how to make ViewPager2 dynamic without giving a shot first on the simplest approach I could come up. Many answer here on SO pointing out that getItemId() and containsItem() needs to be override when adding or removing Fragment(s) on ViewPager2 which gives some headache for almost 2 days. Felt betrayed.

这篇关于关于使用FragmentStateAdapter实现ViewPager2的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆