文章目录
前言
目前主流APP常见首页布局有顶部banner+列表、顶部banner+ViewPager等形式,如果有刷新需求,再通过外层嵌套SwipeRefreshLayout以实现刷新需求。若能掌握这些布局方式,就能应对大部分APP需求。
XML布局
布局用到系统控件,需要添加依赖:
implementation ‘androidx.appcompat:appcompat:1.1.0’
implementation ‘androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha03’
implementation “androidx.viewpager2:viewpager2:1.0.0”
implementation ‘com.google.android.material:material:1.1.0-beta02’
一. SwipeRefreshLayout+顶部banner+RecyclerView
效果预览:
xml实现:
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
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:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NestedScrollViewActivity">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NestedScrollViewActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/banner_view"
android:layout_width="match_parent"
android:layout_height="300dp" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
使用androidx.swiperefreshlayout.widget.SwipeRefreshLayout、androidx.core.widget.NestedScrollView、androidx.recyclerview.widget.RecyclerView按照这种排列布局即可实现,不需要自定义view处理滑动冲突。so easy~
ps:RecyclerView初始化、adapter初始化、模拟数据填充等代码,可以参考NestedRecyclerViewActivity.java。
二. SwipeRefreshLayout+顶部banner+ViewPager
效果预览:
xml实现:
<?xml version="1.0" encoding="utf-8"?>
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
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:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NestedScrollViewActivity">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NestedScrollViewActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/banner_view"
android:layout_width="match_parent"
android:layout_height="300dp" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tablayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<androidx.viewpager2.widget.ViewPager2
android:id="@id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
布局方式和上面类似,只是将RecyclerView替换成TabLayout和ViewPager2。
接下来运行,会发现…有点不对劲,滑动卡壳了。怎么回事,为什么同样是系统控件,TabLayout和ViewPager2就不支持嵌套滑动呢?
点开SwipeRefreshLayout和NestedScrollView源码,发现他们均实现了NestedScrollingParent2、NestedScrollingChild2接口,RecyclerView实现了NestedScrollingChild2接口。而TabLayout和ViewPager2没有实现NestedScrollingParent2、NestedScrollingChild2任一接口。
关于NestedScrollingParent2、NestedScrollingChild2接口
- NestedScrollingParent2官方注释:
This interface should be implemented by {@link android.view.ViewGroup ViewGroup} subclasses that wish to support scrolling operations delegated by a nested child view.
Classes implementing this interface should create a final instance of a {@link NestedScrollingParentHelper} as a field and delegate any View or ViewGroup methods to theNestedScrollingParentHelper
methods of the same signature.
简单说就是希望支持嵌套滑动的父容器需要实现此接口,处理来自子视图传递的滑动事件。可以借助NestedScrollingParentHelper辅助类来执行相应方法。
- NestedScrollingChild2官方注释:
This interface should be implemented by {@link View View} subclasses that wish to support dispatching nested scrolling operations to a cooperating parent {@link android.view.ViewGroup ViewGroup}.
Classes implementing this interface should create a final instance of a {@link NestedScrollingChildHelper} as a field and delegate any View methods to theNestedScrollingChildHelper
methods of the same signature.
简单说就是希望支持嵌套滑动的子视图需要实现此接口,优先将滚动事件向上传递给父容器处理。可以借助NestedScrollingChildHelper辅助类来执行相应方法。
- NestedScrollingParent2接口方法说明:
/**
* 子视图触发滑动时会回调该方法,父容器在该方法中根据子view、滑动方向、触摸类型等判断自己是否支持接收,
* 若接收返回true,否则返回false。(可由NestedScrollingChild2的startNestedScroll方法触发)
*/
boolean onStartNestedScroll(@NonNull View child, @NonNull View target, @ScrollAxis int axes, @NestedScrollType int type);
/**
* onStartNestedScroll返回true后会回调该方法,可在此方法中做一些初始配置操作。
*/
void onNestedScrollAccepted(@NonNull View child, @NonNull View target, @ScrollAxis int axes, @NestedScrollType int type);
/**
* 开始滑动时,子视图会优先回调该方法。父容器可以处理自己的滚动操作,之后将剩余的滚动偏移量
* 传回给子视图。(可由NestedScrollingChild2的dispatchNestedPreScroll方法触发)
*/
void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed, @NestedScrollType int type);
/**
* 子视图处理完剩余的滚动偏移量后,若还有剩余,则将剩余的滚动偏移量再通过该回调传给
* 父容器处理。(可由NestedScrollingChild2的dispatchNestedScroll方法触发)
*/
void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsume