Android ViewPager2无法动态包裹不同大小片段的高度

3

我有一个ViewPager2,其中包含不同高度的片段作为子项,子项的高度在加载数据后会发生变化。

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:expandedTitleGravity="top"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <com.google.android.material.tabs.TabLayout
                    android:id="@+id/tabs_header"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">

                    <com.google.android.material.tabs.TabItem
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:text="@string/prospect_detail_tab_prospect" />

                    <com.google.android.material.tabs.TabItem
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:text="@string/prospect_detail_tab_influencers" />

                    <com.google.android.material.tabs.TabItem
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:text="@string/prospect_detail_tab_sales" />

                </com.google.android.material.tabs.TabLayout>

                <androidx.viewpager2.widget.ViewPager2
                    android:id="@+id/pager_header"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />

            </LinearLayout>
        </com.google.android.material.appbar.CollapsingToolbarLayout>

我尝试了几种不同的解决方案,但如果viewpager2返回到较小的片段,则无法调整大小,它仍然保持与最高片段相同的高度。


你具体尝试了什么?我的第一个想法是在页面更改时调用requestLayout()来更新ViewPager,但我不确定它是否有效。 - Nicolas
请查看这里 - Zain
我尝试在页面更改时对ViewPager使用requestLayout(),但没有任何效果。 - Derek Hannah
2个回答

14

问题在于ViewPager2在您的片段高度更改时(例如,如果您在其中使用RecyclerView),它不会刷新自己的高度。

为了解决这个问题,我们需要在用户选择选项卡时计算高度。这可以通过registerOnPageChangeCallbackonPageSelected来完成

registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
                override fun onPageSelected(position: Int) {
                    super.onPageSelected(position)

                    // Here we need to calculate height
                }
            })

现在,我们需要计算片段的高度。为此,我们需要访问片段及其根视图。可以使用提供给您的ViewPagerAdapter的fragmentManager来完成此操作,例如:我们在这里使用childFragmentManager: PagerViewAdapter(childFragmentManager, lifecycle)


registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
    override fun onPageSelected(position: Int) {
        super.onPageSelected(position)
    
        // Because the fragment might or might not be created yet, 
        // we need to check for the size of the fragmentManager 
        // before accessing it.
        if (childFragmentManager.fragments.size > position) {
            val fragment = childFragmentManager.fragments.get(position)
            fragment.view?.let {
                // Now we've got access to the fragment Root View
                // we will use it to calculate the height and 
                // apply it to the ViewPager2
                updatePagerHeightForChild(it, binding.viewPager)
            }
        }
    }
})
// This function can sit in an Helper file, so it can be shared across your project.
fun updatePagerHeightForChild(view: View, pager: ViewPager2) {
    view.post {
        val wMeasureSpec =
                View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY)
        val hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
        view.measure(wMeasureSpec, hMeasureSpec)

        if (pager.layoutParams.height != view.measuredHeight) {
            pager.layoutParams = (pager.layoutParams)
                    .also { lp ->
                        // applying Fragment Root View Height to
                        // the pager LayoutParams, so they match
                        lp.height = view.measuredHeight
                    }
        }
    }
}

注意:我们使用绑定来访问 viewPager,如果您不使用绑定,请按照自己的方式提供 viewPager,可能需要使用 findViewById()

限制:异步加载内容

如果您有一些动态加载的内容,您可能需要与 ViewTreeObserver.OnGlobalLayoutListener 解决方案耦合,我还没有进行测试。

参考:https://issuetracker.google.com/u/0/issues/143095219


很好的答案,但如果我在ViewPager2中没有使用Fragments怎么办?我只是使用了一个RecyclerView.Adapter、一个布局和ViewHolder... - ProjectDelta
1
相似的原则适用。在onPageSelected中,您需要计算新的高度。请注意视图(fragment.view?.let { ... })如何用于计算,您应该能够使用自己的布局完成相同的操作。 - sonique
1
谢谢您的帮助,我还需要额外执行一些操作,当提交内部列表时,在我的pagerAdapter中也要调用updatePagerHeightForChild函数,使页大小随着添加的项而增长。非常感谢! :D - ProjectDelta

0

你需要稍微定制一下ViewPager:

public class WrapHeightViewPager extends ViewPager {
    private int currentPosition = 0;

    public WrapHeightViewPager(@NonNull Context context) {
        super(context);
    }

    public WrapHeightViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (currentPosition < getChildCount()) {
            View child = getChildAt(currentPosition);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int height = child.getMeasuredHeight();
            if (height != 0) {
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
            }
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    public void measureViewPager(int currentPosition) {
        this.currentPosition = currentPosition;
        requestLayout();
    }
}

接下来,在初始化tabLayout时,只需在onTabSelected函数中调用measureViewPager(currentPosition)即可。

tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        viewPager.measureViewPager(tab.getPosition());
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {

    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {
    }
});

1
这不起作用,因为您正在使用ViewPager(而不是ViewPager2),而ViewPager2类是final的(无法扩展):https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-collection-release/viewpager2/src/main/java/androidx/viewpager2/widget/ViewPager2.java - sonique

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接