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()
}