【问题标题】:Android ViewPager2 not dynamically wrapping height of different sized fragmentsAndroid ViewPager2不会动态包装不同大小片段的高度
【发布时间】:2020-04-21 22:52:34
【问题描述】:

我有一个 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 回到较小的片段,则没有任何效果可以调整其大小,它只是保持与最高片段相同的高度

【问题讨论】:

  • 您到底尝试了什么?我的第一个想法是在页面更改时在 viewpager 上调用 requestLayout(),但我不知道它会如何工作。
  • 请看here
  • 当页面更改时,我确实在 viewpager 上尝试了 requestLayout(),但没有效果

标签: android


【解决方案1】:

当片段的高度发生变化时,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

【讨论】:

    【解决方案2】:

    你需要一点点自定义 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) {
        }
    });
    

    【讨论】:

    猜你喜欢
    • 2020-05-25
    • 1970-01-01
    • 2020-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多