Android 使用ViewPager2获取当前片段

Android 使用ViewPager2获取当前片段,android,android-viewpager,androidx,Android,Android Viewpager,Androidx,我正在将我的ViewPager迁移到ViewPager2,因为后者应该可以解决前者的所有问题。不幸的是,当将它与FragmentStateAdapter一起使用时,我找不到任何方法来获取当前显示的片段 viewPager.getCurrentItem()提供当前显示的索引和适配器。getItem(索引)通常为当前索引创建一个新的片段。除非在getItem()中保留对所有已创建片段的引用,否则我不知道如何访问当前显示的片段 对于旧的ViewPager,一个解决方案是调用adapter.instan

我正在将我的
ViewPager
迁移到
ViewPager2
,因为后者应该可以解决前者的所有问题。不幸的是,当将它与
FragmentStateAdapter
一起使用时,我找不到任何方法来获取当前显示的片段

viewPager.getCurrentItem()
提供当前显示的索引和适配器。getItem(索引)通常为当前索引创建一个新的
片段。除非在
getItem()
中保留对所有已创建片段的引用,否则我不知道如何访问当前显示的片段

对于旧的
ViewPager
,一个解决方案是调用
adapter.instantiateItem(index)
,它将返回所需索引处的片段


我是否遗漏了ViewPager2的一些内容?

ViewPagerAdapter旨在隐藏所有这些实现细节,这就是为什么没有直接的方法来实现它的原因


getItem()
中实例化片段时,您可以尝试对其进行设置和id或标记,然后使用
fragmentManager.findFragmentById()
fragmentManager.findFragmentByTag()
进行检索

然而,这样做有点代码味道。这对我来说意味着,当应该在片段(或其他地方)中完成时,活动中正在做一些事情


也许有另一种方法可以实现您想要的,但是如果不知道为什么需要获取当前片段,就很难给出建议。

我在迁移到
ViewPager2
时遇到了类似的问题

在我的例子中,我决定使用
parentFragment
属性(我认为您可以使它也适用于活动),并希望
ViewPager2
只保留当前片段。(即,最后恢复的页面片段是当前片段。)

因此,在包含
ViewPager2
视图的主片段(
HostFragment
)中,我创建了以下属性:

private var _currentPage: WeakReference<MyPageFragment>? = null
val currentPage
    get() = _currentPage?.get()

fun setCurrentPage(page: MyPageFragment) {
    _currentPage = WeakReference(page)
}
我还使用该类定义分页片段的公共接口:

abstract fun someOperation1()

abstract fun someOperation2()
然后我可以从
HostFragment
调用它们,如下所示:

currentPage?.someOperation1()

我并不是说这是一个很好的解决方案,但我认为它比依赖
ViewPager的
适配器的内部以及我们以前必须使用的
instantiateItem
方法更优雅。

我能够使用反射访问FragmentStateAdapter中的当前片段

Kotlin中的扩展功能:

fun FragmentStateAdapter.getItem(position: Int): Fragment? {
    return this::class.superclasses.find { it == FragmentStateAdapter::class }
        ?.java?.getDeclaredField("mFragments")
        ?.let { field ->
            field.isAccessible = true
            val mFragments = field.get(this) as LongSparseArray<Fragment>
            return@let mFragments[getItemId(position)]
        }
}
示例调用:

val tabsAdapter = viewpager.adapter as FragmentStateAdapter
val currentFragment = tabsAdapter.getItem(viewpager.currentItem)
使用
FragmentStateAdapter
中的
placeFragmentInViewHolder(@NonNull final FragmentViewHolder)
添加片段

mFragmentManager.beginTransaction()
                    .add(fragment, "f" + holder.getItemId())
                    .setMaxLifecycle(fragment, STARTED)
                    .commitNow()

通过在适配器中添加一个对象,它现在也面临同样的问题

class MyViewPager2Adapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {


    private val FRAGMENTS_SIZE = 2

    var currentFragmentWeakReference: WeakReference<Fragment>? = null

    override fun getItemCount(): Int {
        return this.FRAGMENTS_SIZE
    }

    override fun createFragment(position: Int): Fragment {

        when (position) {
            0 -> {
                currentFragmentWeakReference= MyFirstFragment()
                return MyFirstFragment()
            }
            1 -> {
                currentFragmentWeakReference= MySecondFragment()
                return MySecondFragment()
            }
        }

        return MyFirstFragment() /for default view

    }

}
我在ViewPager2中只测试了2个片段


干杯,伙计们,希望这能对你们有所帮助

ViewPager2
中,默认情况下碎片管理器为碎片分配了如下标记:

currentPage?.someOperation1()
第一个位置的碎片有一个标签
“f0”

第2个位置的碎片有一个标签
“f1”

第三个位置的片段有一个标记为“f2”
,以此类推。。。因此,您可以通过将“f”与片段的位置连接起来来获取片段的标记。要获取当前片段,您可以从viewPager2位置获取当前位置,并将标记如下(对于Kotlin):

对于某个位置的碎片

val myFragment = supportFragmentManager.findFragmentByTag("f" + position)
如果使用这种技术,您可以强制转换片段并始终检查它是否为null

如果在片段中托管ViewPager2,请改用
childFragmentManager

记住

如果您已覆盖适配器中的
getItemId(位置:Int)
。那么你的情况就不同了。应该是:

val myFragment = supportFragmentManager.findFragmentByTag("f" + your_id_at_that_position)
或者简单地说:

val myFragment = supportFragmentManager.findFragmentByTag("f" + adapter.getItemId(position))

如果在片段中托管ViewPager2,请使用
childFragmentManager
而不是
supportFragmentManager

通过其标记查找当前片段的解决方案似乎最适合我。我为此创建了以下扩展函数:

fun ViewPager2.findCurrentFragment(fragmentManager: FragmentManager): Fragment? {
    return fragmentManager.findFragmentByTag("f$currentItem")
}

fun ViewPager2.findFragmentAtPosition(
    fragmentManager: FragmentManager,
    position: Int
): Fragment? {
    return fragmentManager.findFragmentByTag("f$position")
}
  • 如果您的ViewPager2主机是
    活动
    ,请使用
    supportFragmentManager
    fragmentManager
  • 如果您的ViewPager2主机是
    Fragment
    ,请使用
    childFragmentManager
请注意:

  • findFragmentPosition
    仅适用于在ViewPager2的RecyclerView中初始化的片段。因此,您只能获得+1可见的位置
  • Lint将建议您从
    fun ViewPager2.findfracentaposition
    中删除
    ViewPager2.
    ,因为您不使用ViewPager2类中的任何内容。我认为它应该留在那里,因为这种解决方法只适用于ViewPager2

与这里的其他一些答案类似,这是我目前使用的答案。

我不清楚它有多可靠,但至少其中一个肯定会起作用不妨发布我的解决方案-这与@全能者的基本方法相同,只是我将
片段
弱引用保留在
PagerAdapter
的查找表中:

private class PagerAdapter(fm: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fm, lifecycle) {

    // only store as weak references, so you're not holding discarded fragments in memory
    private val fragmentCache = mutableMapOf<Int, WeakReference<Fragment>>()

    override fun getItemCount(): Int = tabList.size
    
    override fun createFragment(position: Int): Fragment {
        // return the cached fragment if there is one
        fragmentCache[position]?.get()?.let { return it }

        // no fragment found, time to make one - instantiate one however you
        // like and add it to the cache
        return tabList[position].fragment.newInstance()
            .also { fragmentCache[position] = WeakReference(it) }
            .also { Timber.d("Created a fragment! $it") }
    }

    // not necessary, but I think the code reads better if you
    // can use a getter when you want to... try to get an existing thing
    fun getFragment(position: Int) = createFragment(position)
}
私有类PagerAdapter(fm:FragmentManager,生命周期:lifecycle):FragmentStateAdapter(fm,生命周期){
//只存储为弱引用,这样就不会在内存中保存丢弃的片段
private val fragmentCache=mutableMapOf()
重写getItemCount():Int=tabList.size
重写片段(位置:Int):片段{
//如果存在缓存片段,则返回缓存片段
fragmentCache[position]?.get()?.let{返回它}
//没有找到片段,是时候做一个了-实例化一个
//喜欢并将其添加到缓存中
返回tabList[position].fragment.newInstance()
.另外{fragmentCache[position]=WeakReference(it)}
.另外{Timber.d(“创建了一个片段!$it”)}
}
val myFragment = supportFragmentManager.findFragmentByTag("f" + your_id_at_that_position)
val myFragment = supportFragmentManager.findFragmentByTag("f" + adapter.getItemId(position))
fun ViewPager2.findCurrentFragment(fragmentManager: FragmentManager): Fragment? {
    return fragmentManager.findFragmentByTag("f$currentItem")
}

fun ViewPager2.findFragmentAtPosition(
    fragmentManager: FragmentManager,
    position: Int
): Fragment? {
    return fragmentManager.findFragmentByTag("f$position")
}
private class PagerAdapter(fm: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fm, lifecycle) {

    // only store as weak references, so you're not holding discarded fragments in memory
    private val fragmentCache = mutableMapOf<Int, WeakReference<Fragment>>()

    override fun getItemCount(): Int = tabList.size
    
    override fun createFragment(position: Int): Fragment {
        // return the cached fragment if there is one
        fragmentCache[position]?.get()?.let { return it }

        // no fragment found, time to make one - instantiate one however you
        // like and add it to the cache
        return tabList[position].fragment.newInstance()
            .also { fragmentCache[position] = WeakReference(it) }
            .also { Timber.d("Created a fragment! $it") }
    }

    // not necessary, but I think the code reads better if you
    // can use a getter when you want to... try to get an existing thing
    fun getFragment(position: Int) = createFragment(position)
}
    public interface DetailFragmentShownListener {
        // Allows classes that extend this to update visual items after shown
        void onDetailFragmentShown(DetailFragment me);
    }
    public void onResume() {
        super.onResume();
        View v = getView();
        if (v!=null && v.getViewTreeObserver().isAlive()) {
            v.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    v.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    // Let our parent know we are laid out
                    if ( getActivity() instanceof DetailFragmentShownListener ) {
                        ((DetailFragmentShownListener) getActivity()).onDetailFragmentShown(DetailFragment.this);
                    }
                }
            });
        }
    }
    @Override
    public void onDetailFragmentShown(DetailFragment me) {
        mCurrentFragment = me;
        updateToolbarTitle();
    }
class ClubPhotoAdapter(
    private val dataList: List<MyData>,
    fm: FragmentManager,
    lifecycle: Lifecycle
) : FragmentStateAdapter(fm, lifecycle) {

    private val fragmentMap = mutableMapOf<Int, WeakReference<MyFragment>>()

    override fun getItemCount(): Int = dataList.size

    override fun createFragment(position: Int): Fragment {
        val fragment = MyFragment[dataList.getOrNull(position)]

        fragmentMap[position] = WeakReference(fragment)

        return fragment
    }

    fun getItem(position: Int): MyFragment? = fragmentMap[position]?.get()
}