具有 recyclerview 和 tablayout 的动态高度查看器
Posted
技术标签:
【中文标题】具有 recyclerview 和 tablayout 的动态高度查看器【英文标题】:Dynamic height viewpager with recyclerview and tablayout 【发布时间】:2021-03-10 02:02:00 【问题描述】:我有一个 3 页的 viewpager,每个页面可能包含也可能不包含 recyclerview。 recyclerview 中的项目数量因用户而异。我在这个问题之后制作了一个自定义视图寻呼机: How to wrap the height of a ViewPager to the height of its current Fragment?。 我现在面临的问题有两点:
1. 在切换选项卡时,会为每个片段调用 requestLayout()。所以当recyclerview包含很多项目时,切换时会有延迟,因为它是一次又一次地测量选项卡。并且 recyclerview 中的项目是动态的,这意味着它在滚动行为上有加载更多项目。
2. 当recyclerview 没有item 时,会显示一个textview 来指示没有item。因为 viewpager 包装了内容,所以我似乎无法将 textview 在片段中居中。所以当没有recyclerview时,我需要viewpager填满整个高度。
这是我的 CustomViewPager 代码:
public class CustomViewPager extends ViewPager
private int currentPagePosition = 0;
Context context;
public CustomViewPager(@NonNull Context context)
super(context);
this.context = context;
public CustomViewPager(@NonNull Context context, @Nullable AttributeSet attrs)
super(context, attrs);
this.context = context;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
setMeasuredDimension(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
View child = getChildAt(currentPagePosition);
if(child != null)
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
heightMeasureSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY);
if(heightMeasureSpec != 0)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
public void measureCurrentView(int position)
currentPagePosition = position;
requestLayout();
@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
return false;
@Override
public boolean onTouchEvent(MotionEvent ev)
return false;
这是我的 viewpager 布局文件:
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_
android:layout_>
<RelativeLayout
android:layout_
android:layout_
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:background="#F8F8F8"
android:id="@+id/header"/>
<com.google.android.material.tabs.TabLayout
android:layout_
android:layout_
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/tabs">
<com.google.android.material.tabs.TabItem
android:layout_
android:layout_
android:text="1ST"
/>
<com.google.android.material.tabs.TabItem
android:layout_
android:layout_
android:text="2ND"
/>
<com.google.android.material.tabs.TabItem
android:layout_
android:layout_
android:text="3RD"
/>
</com.google.android.material.tabs.TabLayout>
<com.example.smilee.adapters.CustomViewPager
android:layout_
android:layout_
android:id="@+id/items"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabs"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
这是每个片段的布局:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@android:color/white">
<androidx.recyclerview.widget.RecyclerView
android:layout_
android:layout_
android:id="@+id/recyclerview"
android:nestedScrollingEnabled="false"
android:visibility="gone"/>
<TextView
android:layout_
android:layout_
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:text="No items to show."
android:fontFamily="@font/medium"
android:textColor="#777"
android:textSize="15dp"
android:gravity="center"
android:id="@+id/noItemText"
android:includeFontPadding="false"
android:visibility="visible"/>
</androidx.constraintlayout.widget.ConstraintLayout>
如何做到这一点? 无论recyclerview中的项目数如何,如何快速切换选项卡,如果recyclerview不可用,如何通过填充整个高度来居中textview?希望你能帮忙。问候。
【问题讨论】:
您考虑过使用 ViewPager2 吗?它应该与 RecyclerView 一起玩得更好,因为垂直 RecyclerView(ViewPager2 基于 RecyclerView)内的水平 RecyclerView 是一种常见模式,它旨在处理这种情况。 但是它会根据内容改变它的高度吗? 我不确定你的意思。一些图表可能会有所帮助 我的意思是,viewpager 的高度会根据其子项(动态高度)而变化。假设我有 3 个标签。第一个选项卡可能包含一个 recyclerview 和 1000 个项目。第二个可能包含一个 recyclerview 和只有 3 个项目。因此,当我从第一个选项卡切换到第二个选项卡时,第二个选项卡会占用包含 1000 个项目的第一个选项卡的高度,还是只会占用包含 3 个项目的回收视图的高度?希望你能理解。 哦,我明白了。为什么你有一个嵌套的滚动视图?在滚动视图中使用 RecyclerView 通常是个坏主意,因为它会完全禁用所有回收(它必须立即布置列表中的每个项目),因此如果您有多个垂直页面的项目,可能会导致您滞后。在上面的代码中,滚动视图似乎不是很有用 【参考方案1】:您的主要问题是将 RecyclerView 嵌套在 ScrollView 中。这很容易导致性能问题,因为 RecyclerView 不知道它自己的哪一部分当前在屏幕上,所以它一次加载所有项目。添加打印登录onBindViewHolder
以确认这一点。这也意味着 RecyclerView 的高度(以及 viewpager 的高度)现在是任意的,并且不限于屏幕的大小,这将使您的文本无法居中。
为了实现你想要的布局,我会将 NestedScrollView 替换为 CoordinatorLayout。对于我认为完全相同的情况,我已经在我的应用程序中成功地做到了这一点。那么您将不再需要自定义 ViewPager。
这是一个使用您的布局的经过测试的工作示例:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_layout"
android:layout_
android:layout_>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_
android:layout_
app:layout_scrollFlags="scroll|enterAlways">
<RelativeLayout
android:id="@+id/header"
android:layout_
android:layout_
android:background="#F8F8F8"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_
android:layout_
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/header">
</com.google.android.material.tabs.TabLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/items"
android:layout_
android:layout_
app:layout_anchor="@id/app_layout"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
请注意,您必须从 RecyclerView 中删除此行:
android:nestedScrollingEnabled="false"
【讨论】:
谢谢。我还没有使用协调器布局。将研究并让您知道它是否有效。问候。 你能给我一个简单的演示代码吗?我不能让它工作。 对不起,我不能让它工作。我在约束布局内放了一个回收视图,它在固定位置滚动。你能做这样的布局吗: CoordinatorLayout -> ConstraintLayout -> RelativeLayout -> TabLayout -> ViewPager viewpager 必须在约束布局内,在 AppBarLayout 下方 是的,我也是这样做的。但没有帮助。我没有在布局中同时使用 appbarlayout 和工具栏,因为这不是必需的。但是我试过了,还是不行。【参考方案2】:这是工作的 XML 代码。您可以尝试使用此代码。添加此属性使 NestedScrollView 内容全高
android:fillViewport="true"
这是您修改后的主要布局 XML 代码
<androidx.core.widget.NestedScrollView
android:layout_
android:layout_
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_
android:layout_>
<RelativeLayout
android:layout_
android:layout_
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:background="#F8F8F8"
android:id="@+id/header"/>
<com.google.android.material.tabs.TabLayout
android:layout_
android:layout_
app:layout_constraintTop_toBottomOf="@id/header"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:id="@+id/tabs">
<com.google.android.material.tabs.TabItem
android:layout_
android:layout_
android:text="1ST"
/>
<com.google.android.material.tabs.TabItem
android:layout_
android:layout_
android:text="2ND"
/>
<com.google.android.material.tabs.TabItem
android:layout_
android:layout_
android:text="3RD"
/>
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager.widget.ViewPager
android:id="@+id/items"
android:layout_
android:layout_
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/tabs"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
【讨论】:
recyclerview 仍处于固定状态。我需要滚动它。 请确保您的 recyclerview android:layout_ android:nestedScrollingEnabled="false" 的 XML 代码中的这两行,您可以关注此***.com/a/36977637/9158688以上是关于具有 recyclerview 和 tablayout 的动态高度查看器的主要内容,如果未能解决你的问题,请参考以下文章
如何将 DiffUtil.Callback 与具有页眉和页脚的 RecyclerView 一起使用?
具有 RecyclerView 的页面中 Persistent 或 Standard BottomSheet 的奇怪滚动行为和可见性