最近项目中嵌套的控件比较多,遇到了不少问题,昨天解决了,赶紧记录下
场景是ViewPager2嵌套NestedScrollView嵌套RecyclerView瀑布流
效果图:
没办法,压缩太严重,凑合看
主要问题如下:
- NestedScrollView / ScrollView嵌套RecyclerView冲突,不滚动、显示不完整、或者不显示问题
- NestedScrollView / ScrollView嵌套RecyclerView瀑布流双重滑动问题
- 打开页面后定位到RecyclerView问题
主要就这三个问题,下面按照顺序分别解决,NestedScrollView和ScrollView我试过了,效果没区别
问题一:NestedScrollView / ScrollView嵌套RecyclerView冲突,不滚动、显示不完整、或者不显示问题
关于该问题,是最主要的问题,CSDN上说了很多都是要修改layout_height match_parent为wrap_content,其实这个没什么影响,因为问题本质是我们需要给RecyclerView一个固定的高度来解决的。
如果你设置为match_parent,就会出现不显示的效果,我们只要给RecyclerView设置一个固定的高度。或者动态计算控件高度,然后设置上,就可以解决该问题了。
我们可以在XML中给RecyclerView的layout_height设置一个固定高度,1000dp之类的,这样可以解决。但是我们没办法控制瀑布流的高度。但是实际开发中我们不能确定数据需要加载多高,所以写死高度可能不太好。所以布局代码就不贴了
换种思路就是动态计算出所需的高度,然后设置给他就可以了。
我这里是根据瀑布流数据的长度来处理的。可以根据具体的业务计算高度。
// 计算需要高度
ViewGroup.LayoutParams layoutParams = menuHomeBinding.rvWaterFallsFlow.getLayoutParams();
int height = 0;
for (int i = 0; i < waterFallsFlowBeanArrayList.size(); i++) {
if (i % 2 == 0) {
height += 400;
} else if (i % 3 == 0) {
height += 360;
} else {
height += 440;
}
}
// 只要把业务处理后的高度赋值给layoutParams.height就可以了
layoutParams.height = height - waterFallsFlowBeanArrayList.size() * 100;
menuHomeBinding.rvWaterFallsFlow.setLayoutParams(layoutParams);
这样就可以了
我也有想过在Adapter中计算高度,然后得到后处理,但是试了下,获取的一直是0,如果有思路的可以告知下。
问题二:NestedScrollView / ScrollView嵌套RecyclerView瀑布流双重滑动问题
在正常显示后,会出现NestedScrollView / ScrollView滑动,里面的RecyclerView也可以滑动的效果。
这里我们需要处理一下,不让里面的RecyclerView滑动,而是随着NestedScrollView / ScrollView一起滑动。
这里呢,官方API中有一个属性,可以帮助我们解决这个问题。就是这个setNestedScrollingEnabled,这是启动嵌套滚动的开关。默认是true。
当然也可以在布局文件中设置,如果是在布局文件中设置,是要在RecyclerView上设置的。我这里是在代码中设置的。
menuHomeBinding.rvWaterFallsFlow.setNestedScrollingEnabled(false);
问题三:打开页面后定位到RecyclerView问题
在上面的基础上,效果已经可以正常显示,但是打开App后就自动定位到RecyclerView的位置 了。
这是因为RecyclerView是默认自动获取焦点的,所以我们需要处理一下。有两种方式
XML方式和代码中设置的方式,先说一下区别:
在XML中设置,切换其他页面后回来,焦点总是在顶部(设置焦点的位置);
如果在代码中设置,切换页面后回来,位置会停留在之前浏览的位置。
下面是具体的解决方案。
-
XML方式
在NestedScrollView / ScrollView的第一个子控件上添加获取焦点的属性
android:focusable="true"
android:focusableInTouchMode="true"
这里需要说明一下,NestedScrollView和ScrollView都是只允许有一个子控件,这里说的第一个子控件是唯一子控件里面的第一个子控件。
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- 第一个子控件-->
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_home_menu_banner"
android:layout_width="match_parent"
android:layout_height="160dp"
android:focusable="true"
android:focusableInTouchMode="true"
android:layout_marginStart="10dp"/>
-
代码中处理
第二中是在代码中处理。我采用的这种方式,也很简单。
既然是RecyclerView默认自动获取焦点,那么我们只要在代码中设置不让他默认获取焦点就可以解决了。
menuHomeBinding.rvWaterFallsFlow.setFocusable(false);
总结
基本上用到的属性就
menuHomeBinding.rvWaterFallsFlow.setHasFixedSize(true);
menuHomeBinding.rvWaterFallsFlow.setNestedScrollingEnabled(false);
menuHomeBinding.rvWaterFallsFlow.setFocusable(false);
要点:确定RecyclerView的高度
完整代码:
XML:请忽略无用的代码,只要看最外层NestedScrollView和最下面的RecyclerView就OK
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
</data>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Banner -->
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_home_menu_banner"
android:layout_width="match_parent"
android:layout_height="160dp"
android:layout_marginStart="10dp"/>
<!-- 功能菜单 -->
<LinearLayout
android:id="@+id/ll_home_menu"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/ll_home_menu_reservation"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginEnd="16dp"
android:layout_weight="1"
android:background="@drawable/style_constraintlayout_purple_gradient_bg"
android:gravity="center"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="@+id/cv_menu_home_reservation"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_marginHorizontal="6dp"
android:layout_marginTop="4dp"
app:cardCornerRadius="8dp"
app:cardElevation="0dp">
<ImageView
android:layout_width="60dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:scaleType="centerInside"
android:src="@drawable/home_menu_reservation" />
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="match_parent"
android:layout_height="20dp"