【问题标题】:How to wrap height of Android ViewPager2 to height of current item?如何将 Android ViewPager2 的高度包装到当前项目的高度?
【发布时间】:2020-01-01 17:53:27
【问题描述】:

这个问题是针对新的 ViewPager2 类的。

有一个similar question for the old ViewPager,但解决方案需要扩展ViewPager。但是,ViewPager2 是最终版本,因此无法扩展。

在我的情况下,我有一个包含三个不同片段的 ViewPager2。其中一个片段比其他两个片段高得多,这意味着当您滑动到其中一个较短的片段时,会有很多空白空间。那么如何有效地将 ViewPager2 包裹到当前 Fragment 的高度呢?

【问题讨论】:

标签: android android-fragments android-viewpager android-viewpager2


【解决方案1】:

解决方案是在属于ViewPager2 的每个片段中添加以下内容:

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

binding.root 来自 ViewBinding/DataBinding 但它是布局的主要容器,即ConstraintLayoutLinearLayout

【讨论】:

  • 你能解释一下这段代码到底是做什么的吗..它解决了我的问题,但我不明白为什么
【解决方案2】:

我对这种解决方法一点也不满意,但它确实解决了我在尝试将 ViewPager2 与不同高度的片段一起使用时遇到的渲染问题。它显然会减慢渲染速度并消耗更多内存。

myStupidViewPager2.offscreenPageLimit = fragments.size

【讨论】:

  • viewPager.setOffscreenPageLimit(3); 对我不起作用。但是,我的页面现在是视图(而不是片段),因此您的答案可能对其他人有用。
  • 你实际上在哪里称呼这个?
  • 好主意,但不适用于延迟加载的项目(如分页)
【解决方案3】:

我遇到了同样的问题,ViewPager2 在 ScrollView 中,在 TabLayout 下。浏览选项卡后,ScrollView 的高度等于最大片段高度的高度。 通过将 ScrollView 转移到 Fragment 解决了问题。

【讨论】:

    【解决方案4】:

    我找到了这个问题的答案,我通过它的适配器访问孩子,因为当您在任何给定位置访问孩子而不是 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
        }
    }
    

    【讨论】:

    • 这不起作用,什么是 getViewAtPosition 和 pagerAdapter ?代码真的很糟糕。
    • getViewAtPosition 作为方法添加到您的 pagerAdapter(查看 pager2 适配器)
    • @AssemMahrous 你能否更新示例代码以显示 getViewAtPosition 和 pagerAdapter 因为我也对你从哪里得到这些感到困惑。
    • 我已经编辑了代码并添加了寻呼机适配器@sphrak
    • 为什么又在onDestroy:pager.unregisterOnPageChangeCallback(ViewPager2PageChangeCallback())创建回调?哈,这很有趣。
    【解决方案5】:

    花了很多时间后这对我有用,我的 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
                        }
                }
            }
        }
    

    【讨论】:

      【解决方案6】:

      我有一个动态页面高度问题,它可能会在运行时发生变化。换页后,最高页的部分内容被截掉。

      我能够通过重新测量寻呼机内容(即页面)和寻呼机本身来解决这个问题。

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

      【讨论】:

      • 这个对我很有魅力。
      【解决方案7】:

      基于Lucas Nobile,我发现了另一种实现方式。而不是将以下代码放在每个受影响片段的 OnResume 方法上:

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

      只需将此覆盖添加到 viewPager2 的适配器:

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

      Obs:用 kotlin 编写的代码。

      【讨论】:

        【解决方案8】:

        对我来说,设置 viewpager2 的 recyclerview 布局参数可以解决这个问题。

        试试

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

        并设置recyclerview布局参数

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

        【讨论】:

        • 谢谢,但这对我不起作用。我所有的 ViewPager2 页面都随着这段代码消失了。也就是说,我的页面现在是视图(而不是片段),所以也许你的答案可能对其他人有用。
        【解决方案9】:

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

        这对我有用;

        感谢我的这项工作,当我们想将 viewpager 用作 Book 的页面时。有不同种类的页面高度。有时,当页面太长时,我们需要滚动操作:3 那时,只需用 NestedScroll View 包裹您的 viewpager 的项目布局..

        感谢@safaorhan

        /** * 禁用子附加侦听器,以便具有 wrap_content 高度的膨胀子可以通过。 * * 这非常脆弱,取决于 [ViewPager2] 的实现细节。 * * @see ViewPager2.enforceChildFillListener(被移除的监听器) */

        // 用你的viewpager调用这个方法...

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

        //查看寻呼机

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

        //您的孩子 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>
        

        【讨论】:

          【解决方案10】:

          我可以通过重写 onPageSelected 回调并重新测量当前视图的高度来做到这一点。

          看起来像这样:

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

          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>
          

          另外,请确保所有 Fragment 的布局高度都是包裹内容。

          【讨论】:

            猜你喜欢
            • 2016-04-01
            • 2020-02-17
            • 1970-01-01
            • 2018-03-02
            • 1970-01-01
            • 1970-01-01
            • 2013-11-07
            • 1970-01-01
            • 2011-07-24
            相关资源
            最近更新 更多