背景
实现
基于AppBarLayout+CollapsingToolbarLayout实现
相关基础能力
�CoordinatorLayout�
他是一个高级版的FrameLayout容器,他可以通过Behavior处理内部View之间的嵌套滑动
Behavior
他通过关联联动View和被联动View,在联动View发生滑动时,通知Behavior从而触发被联动View的响应,他主要包含以下几个方法:
onStartNestedScroll
:嵌套滑动开始,一般通过parent view的类型确定behavior是否要监听滑动onNestedPreScroll
:嵌套滑动中,要监听的parentView 即将开始滑动,滑动事件还未消费onNestedScroll
:嵌套滑动中,要监听的parentView 即将开始滑动,滑动事件已消费onStopNestedScroll
:嵌套滑动结束
AppBarLayout
AppBarlayout默认实现了AppBarLayout.Behavior,当与其组合的滚动布局(RecycleView、NestedScrollView等)设置了app:layout_behavior="@string/appbar_scrolling_view_behavior"
,他就根据设置的滚动标识(app:layout_scrollFlags
)响应滚动布局的滚动做出响应。
滚动标识有:
scroll
:布局的滚动与滚动事件直接相关。需要设置此标志才能使下面的其他标志生效。exitUntilCollapsed
:当你给布局定义了一个minHeight
,此布局将在滚动到达这个最小高度的时候折叠。enterAlways
:任何的向下滚动都会让view出现,这个标识通常用于“快速返回”模式enterAlwaysCollapsed
:当你给布局定义了一个minHeight
并且设置了enterAlways
,那么在向下滑动过程中view会展示为最小高度,直到划到顶部在展开- �
snap
:当滚动结束的时候,如果布局是部分可见的,那么他会滚动到完全收缩或者展开,可以和exitUntilCollapsed
共同使用收缩到一定高度
CollapsingToolbarLayout
CollapsingToolbarLayout 是 Toolbar 的一个包装器,它实现了一个折叠的应用栏。它旨在用作 AppBarLayout 的直接子级。
折叠方式有:
**off**
:这个是默认属性,布局将正常显示,没有折叠的行为。**pin**
:CollapsingToolbarLayout折叠后,此布局将固定在顶部。**parallax**
:CollapsingToolbarLayout折叠时,此布局将会以视差方式移动
布局
通过CoordinatorLayout实现顶部的AppbarLayout和底部的RecycleView联合滑动,滑动的过程中RecycleView通知AppbarLayout滑动的距离,appbarLayout操纵其中的ImageView进行缩放,大致页面结构如下
实现
<?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_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/background_light"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:minHeight="100dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
app:layout_collapseMode="pin" />
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:src="@drawable/xiana"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="1" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
</androidx.recyclerview.widget.RecyclerView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
- 监听appbarLyaout的滑动偏移量,控制ImageView进行缩放
private void initAppBarLayout() {
AppBarLayout appBarLayout = findViewById(R.id.appbar);
ImageView imageView = findViewById(R.id.image);
// 把锚点放到左上角
imageView.setPivotX(0);
imageView.setPivotY(0);
//显示的调用invalidate
imageView.invalidate();
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: ");
}
});
appBarLayout.addOnOffsetChangedListener(new OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
// 第一次滑动的时候记录图片的原始高度
if (imageOriginHeight == 0) {
imageOriginHeight = imageView.getMeasuredHeight();
}
// 根据滑动的距离缩放图片
float newHeight = imageOriginHeight + verticalOffset;
float scale = newHeight / imageOriginHeight;
ViewCompat.setScaleY(imageView, scale);
ViewCompat.setScaleX(imageView, scale);
}
});
}
基于BottomSheetBehavior实现
相关基础能力
BottomSheetBehavior
他是CoordinatorLayout子View使用的Behavior的一种,用作底部工作表,主要支持了多段的手势下滑关闭,在BottomSheetDialog和BottomSheetDialogFragment都有使用。
他比较重要的几个属性有
- state:bottomSheet的状态,包含以下几种(默认初始状态为STATE_COLLAPSED)
STATE_COLLAPSED
:折叠状态STATE_HALF_EXPANDED
:半展开状态STATE_EXPANDED
:展开状态STATE_HIDDEN
:隐藏状态STATE_DRAGGING
:滑动中状态STATE_SETTLING
:停止状态
peekHeight
:当bottomSheet为STATE_COLLAPSED时的高度hideable
:bottomSheet是否从STATE_COLLAPSED状态滑动到完全隐藏fitToContents
:- true:bottomSheet根据内容自适应展开和折叠状态的高度
- false:bottomSheet根据用户自己来配置展开,半展开,折叠的高度
halfExpandedRatio
:需要fitToContents为false才能使用,设置bottomSheet HALF_EXPANDED状态下的比例,默认为0.5expandedOffset
:需要fitToContents为false才能使用,设置bottomSheet EXPANDED状态下距离顶部的偏移量(注意:这个高度会包含Android的状态栏,所以设置时要手动加上状态栏高度才能正确)
布局
实现
<?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_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/background_light"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="@dimen/image_max_height"
android:scaleType="fitCenter"
android:src="@drawable/xiana" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
</androidx.recyclerview.widget.RecyclerView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
- 设置bottomSheet的基础属性并且监听滑动回调实现图片缩放
private void initBottomSheet() {
ImageView imageView = findViewById(R.id.image);
// 把锚点放到左上角
imageView.setPivotX(0);
imageView.setPivotY(0);
//显示的调用invalidate
imageView.invalidate();
float imageMinHeight = getResources().getDimension(R.dimen.image_mini_height);
float imageMaxHeight = getResources().getDimension(R.dimen.image_max_height);
int screenHeight = ScreenTools.getScreenHeight(this);
BottomSheetBehavior behavior = BottomSheetBehavior.from(findViewById(R.id.recyclerView));
// 设置折叠状态下的高度
behavior.setPeekHeight((int)(screenHeight - imageMaxHeight));
// 设置展开状态下的高度
behavior.setExpandedOffset((int)imageMinHeight + ScreenTools.getStatusBarHeight(this));
behavior.setFitToContents(false);
// 监听bottomSheet的滑动
behavior.addBottomSheetCallback(new BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
ViewCompat.setScaleY(imageView, 1 - slideOffset * 0.5f);
ViewCompat.setScaleX(imageView, 1 - slideOffset * 0.5f);
}
});
}
- 代码详见:BottomSheetActivity
踩坑时发现tips
- 手动展开/收起appbarLayout
// 第一个参数true为展开,false为收起,第二个参数为是否需要有动画
appBarLayout.setExpanded(true, true);
- 判断RecycleView是否滑动到顶部
// true为可滑动,fasle为滑动到顶部
view.canScrollVertically(-1)
其他
git仓库地址
Android技能树点亮计划Git库
Android技能树点亮计划-语雀文档库
稀土掘金:悠二
Github:PettyWing