简体   繁体   English

如何将 Android ViewPager2 的高度包装到当前项目的高度?

[英]How to wrap height of Android ViewPager2 to height of current item?

This question is for the new ViewPager2 class.这个问题是针对新的ViewPager2类的。

There is a similar question for the old ViewPager , but the solution requires extending ViewPager . 旧的 ViewPager也有类似的问题,但解决方案需要扩展ViewPager However, ViewPager2 is final so cannot be extended.但是,ViewPager2 是最终版本,因此无法扩展。

In my situation, I have a ViewPager2 that contains three different fragments.在我的情况下,我有一个包含三个不同片段的 ViewPager2。 One of these fragments is much taller than the other two, which means when you swipe to one of the shorter fragments, there is a lot of empty space.其中一个片段比其他两个片段高得多,这意味着当你滑动到较短的片段之一时,会有很多空白空间。 So how do I effectively wrap the ViewPager2 to the height of the current fragment?那么如何有效地将 ViewPager2 包裹到当前片段的高度呢?

The solution is to add the following in each fragment that is part of the ViewPager2 :解决方案是在属于ViewPager2每个片段中添加以下ViewPager2

override fun onResume() {
   super.onResume()
   binding.root.requestLayout()
}

binding.root is from ViewBinding/DataBinding but it's is the main container of your layout, ie ConstraintLayout , LinearLayout , etc. binding.root是ViewBinding /数据绑定,但它是你的布局,即主容器ConstraintLayoutLinearLayout ,等

I'm not at all happy with this workaround, but it does solve the rendering problems I had when trying to use ViewPager2 with fragments of different heights.我对这个解决方法一点也不满意,但它确实解决了我在尝试使用具有不同高度片段的 ViewPager2 时遇到的渲染问题。 It will obviously slow down rendering and consume more memory.它显然会减慢渲染速度并消耗更多内存。

myStupidViewPager2.offscreenPageLimit = fragments.size

I have found an answer to this question where i access the child through it's adapter as when you access the child at any given position rather than Zer0 index the view is null.我找到了这个问题的答案,我通过它的适配器访问孩子,因为当您在任何给定位置而不是 Zer0 索引访问孩子时,视图为空。

    class ViewPager2PageChangeCallback() : ViewPager2.OnPageChangeCallback() {
        override fun onPageSelected(position: Int) {
            val view = (pager.adapter as pagerAdapter).getViewAtPosition(position)
            view?.let {
                updatePagerHeightForChild(view, pager)
            }
        }
    }

    private 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 ->
                        lp.height = view.measuredHeight
                    }
            }
        }
    }

   pager.offscreenPageLimit = 1            
   pagerpager.registerOnPageChangeCallback(ViewPager2PageChangeCallback())

     override fun onDestroy() {
        super.onDestroy()
        pager.unregisterOnPageChangeCallback(ViewPager2PageChangeCallback())
    }

 

    class OrderDetailsPager(
    private val arrayList: ArrayList<Fragment>,
    fragmentManger: FragmentManager,
    lifecycle: Lifecycle
    ) : FragmentStateAdapter(fragmentManger, lifecycle) {
    override fun createFragment(position: Int): Fragment {
        return arrayList[position]
    }

    override fun getItemCount(): Int {
        return arrayList.size
    }

    fun getViewAtPosition(position: Int): View? {
        return arrayList[position].view
    }
}

I had the same problem, ViewPager2 was in ScrollView, which is under TabLayout.我有同样的问题,ViewPager2 在 ScrollView 中,它在 TabLayout 下。 After navigating through the tabs, the height of the ScrollView became equal to the height of the maximum fragment height.浏览选项卡后,ScrollView 的高度变得等于最大片段高度的高度。 The problem was solved by transferring the ScrollView to the fragment.问题是通过将 ScrollView 转移到片段来解决的。

This worked for me after spending a lot of time, My viewpager2 is inside a Nesterscrollview花了很多时间后,这对我Nesterscrollview ,我的viewpager2Nesterscrollview

binding.viewpager.offscreenPageLimit = 2
binding.viewpager.isUserInputEnabled = false
binding.viewpager.registerOnPageChangeCallback(object :ViewPager2.OnPageChangeCallback(){
     override fun onPageSelected(position: Int) {
           super.onPageSelected(position)
           val myFragment = childFragmentManager.findFragmentByTag("f$position")
           myFragment?.view?.let { updatePagerHeightForChild(it,binding.viewpager) }
     }
})

private 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 ->
                        lp.height = view.measuredHeight
                    }
            }
        }
    }

Based on Lucas Nobile I found out another way of implementing.基于Lucas Nobile,我发现了另一种实现方式。 Instead of putting the following code on the OnResume method of each affected fragment:而不是将以下代码放在每个受影响片段的 OnResume 方法上:

override fun onResume() {
   super.onResume()
   binding.root.requestLayout()
}

Just add this override to the adapter of the viewPager2:只需将此覆盖添加到 viewPager2 的适配器:

override fun onBindViewHolder(
   holder: FragmentViewHolder,
   position: Int,
   payloads: MutableList<Any>
){
   holder.itemView.requestLayout()
   super.onBindViewHolder(holder, position, payloads)
}

Obs: Code made in kotlin. Obs:用 kotlin 编写的代码。

For me set viewpager2's recyclerview layout params resolve this problem.对我来说,设置 viewpager2 的 recyclerview 布局参数可以解决这个问题。

Try尝试

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

and set recyclerview layout params并设置 recyclerview 布局参数

RecyclerView recyclerView = (RecyclerView) viewPager.getChildAt(0);
recyclerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));

https://gist.github.com/safaorhan/1a541af729c7657426138d18b87d5bd4 https://gist.github.com/safaorhan/1a541af729c7657426138d18b87d5bd4

this work for me;这对我有用;

Thanks this work for me ,when we want to use viewpager as Book ' pages.感谢这项工作,当我们想使用 viewpager 作为 Book 页面时。 There are different kinds of page height.有不同种类的页面高度。 Sometimes , we need to scroll action when page is too long :3 At that time, just wrap your viewpager's item layout with NestedScroll View..有时,当页面太长时,我们需要滚动操作:3 那时,只需使用 NestedScroll View 包裹您的 viewpager 的项目布局即可。

Thanks to @safaorhan感谢@safaorhan

/** * Disables the child attach listener so that inflated children with wrap_content heights can pass. /** * 禁用子附加侦听器,以便具有 wrap_content 高度的膨胀子项可以通过。 * * This is very fragile and depends on the implementation details of [ViewPager2]. * * 这很脆弱,取决于[ViewPager2]的实现细节。 * * @see ViewPager2.enforceChildFillListener (the removed listener) */ * * @see ViewPager2.enforceChildFillListener(移除的监听器)*/

// call this method with your viewpager... // 用你的 viewpager 调用这个方法...

private fun ViewPager2.hackMatchParentCheckInViewPager() {
    (getChildAt(0) as RecyclerView).clearOnChildAttachStateChangeListeners()
}

//View Pager //查看寻呼机

<androidx.viewpager2.widget.ViewPager2
        android:id="@+id/page_view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp">

//Your Child Viewpager item //您的子Viewpager项目

<androidx.core.widget.NestedScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
      ....
    </LinearLayout>
    </androidx.core.widget.NestedScrollView>

I was able to do so by overriding onPageSelected callback and remeasuring the current view's height.我能够通过覆盖 onPageSelected 回调并重新测量当前视图的高度来做到这一点。

It would look something like this:它看起来像这样:

mViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                mTabLayout.selectTab(mTabLayout.getTabAt(position));

 View view = mViewPagerAdapter.getViewAtPosition(position); // this is a method i have in the adapter that returns the fragment's view, by calling fragment.getview()
                if (view != null) {
                    view.post(() -> {
                        int wMeasureSpec = View.MeasureSpec.makeMeasureSpec(view.getWidth(), View.MeasureSpec.EXACTLY);
                        int hMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
                        view.measure(wMeasureSpec, hMeasureSpec);
                        viewPager.getLayoutParams().height = view.getMeasuredHeight();
                        mViewPagerAdapter.notifyDataSetChanged();
                    });
                }
            }
        });
        mViewPager.setOffscreenPageLimit(mTabLayout.getTabCount());

The layout of TabLayout and Viewpager2 looks the following (inside a NestedScrollView) TabLayout 和 Viewpager2 的布局如下(在 NestedScrollView 内)

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

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

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

                    </LinearLayout>

Also, please make sure all the Fragment's layout height it's wrap content.另外,请确保所有 Fragment 的布局高度都是环绕内容。

I have an issue with dynamic pages height which could change in the runtime.我有动态页面高度的问题,这可能会在运行时发生变化。 After page change, some of the content on the highest page is cut off.换页后,最高页的部分内容被截断。

I was able to fix that by re-measuring pager content (ie page) and pager itself.我能够通过重新测量寻呼机内容(即页面)和寻呼机本身来解决这个问题。

         viewPager2.setPageTransformer { page, _ ->
                val wMeasureSpec =
                    View.MeasureSpec.makeMeasureSpec(page.width, View.MeasureSpec.EXACTLY)
                val hMeasureSpec =
                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
                page.measure(wMeasureSpec, hMeasureSpec)
                measure(wMeasureSpec, hMeasureSpec)
                post {
                    adapter?.notifyDataSetChanged()
                }
            }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM