HeaderLayout继承自FrameLayout,且并没有改写FrameLayout的测量和布局逻辑,所以子控件的布局方式和FrameLayout相同即可,我们只需要关注HeaderLayout新增的几个属性。这里以效果图为例。
<androidx.coordinatorlayout.widget.CoordinatorLayout
…>
<com.zzx.headerlayout_kotlin.HeaderLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
//新增属性
app:extend_height=“30%”>
<androidx.appcompat.widget.AppCompatImageView
android:layout_width=“match_parent”
android:layout_height=“300dp”
android:src=“@drawable/singer”
android:scaleType=“centerCrop”
//新增属性
app:transformation=“scroll|extend_scale”
/>
…
</com.zzx.headerlayout_kotlin.HeaderLayout>
<androidx.viewpager.widget.ViewPager
android:id=“@+id/viewPager”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
//配置依赖布局的layout_behavior
app:layout_behavior=“@string/header_layout_scrolling_view_behavior”/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
如上所示,HeaderLayout工作在CoordinatorLayout中并且是其直接子View。ViewPager由于需要根据HeaderLayout的滑动做出界面的调整,所以需要配置layout_behavior,并且其值为@string/header_layout_scrolling_view_behavior,这里和AppBarLayout的使用方式一致。
我们的工作重点是头部控件的联动效果,因此咱们聚焦于HeaderLayout和其子View。我们看AppCompatImageView,它用来展示效果图中的歌手。仔细分析效果图中AppCompatImageView的变换方式,可以发现它是根据父控件HeaderLayout的滑动而做出的相应的变化效果,HeaderLayout向上滑动,其跟随向上,HeaderLayout向下滑动,则跟着向下。并且,在HeaderLayout滑动到底部继续向下拓展时,AppCompatImageView做了一个收缩的变换。
这一切的一切都需要归功于app:transformation属性,可以在代码中看见其值为"scroll|extend_scale",那么其含义是什么呢?对此,我们引出了一个概念----Transformation,它是一个接口,其意在为根据HeaderLayout的滑动及状态而做出相应的变化行为。在介绍Transformation之前,有必要介绍一下HeaderLayout滑动中的几种状态。
HeaderLayout的滑动实际上是HeaderLayout高度的动态变化,所以需要了解图中三种高度的含义。maxHeight是HeaderLayout第一次加载测量后的高度,minHeight是设置了app:sticky_until_exit="true"属性的子View的高度之和,此属性表示子View不随着HeaderLayout而滑出屏幕,形成一种粘连在屏幕顶部的效果,且子View是按照顺序排列的。extendHeight则是拓展的高度,展示在效果图中就是图片收缩scale时下滑的高度,extendHeight可以在xml中为HeaderLayout设置,其值可以为dimension,百分数,或者float比例,百分数和float比例是按照maxHeight而计算的。
而图中五种状态用来表示HeaderLayout高度变化过程中的滑动状态,Transformation就是根据这五种状态而生,Transformation作用于HeaderLayout的直接子View或者间接子View(间接子View需要自己进行处理,可以参考CommonToolbarTransformation),一个子View可以同时拥有多个Transformation,HeaderLayout在其状态变化时,则会遍历子View的所有Transformation,通知其做出改变。
XML中作用于AppCompatImageView的app:transformation="scroll|extend_scale"属性,scroll 和 extend_scale则是内置的两种Transformation,如下表所示。
transformation表示内置的几中Transformation,但是想要自定义Transformation应该如何做呢?
自定义Transformation
custom_transformation属性则是专为自定义Transformation而服务,其值为自己实现的Transformation类的全路径。
自定义Transformation有两种方式,其一是实现Transformation接口,另一种方式是继承TransformationAdapter类,TransformationAdapter是Transformation是Transformation接口的空实现,继承于此则不需要实现所有的方法。
interface Transformation {
/**
-
@see [HeaderLayout.scrollState]为STATE_MIN_HEIGHT, 这个方法回调表示[HeaderLayout]的Bottom已经收缩到了最小高度
-
@param child 当前需要做变换的view
-
@param parent [HeaderLayout]
-
@param unConsumedDy 由其他状态到此状态未消耗完的dy
*/
fun onStateMinHeight(child: V, parent: HeaderLayout, unConsumedDy: Int)
/**
-
@see [HeaderLayout.scrollState]为STATE_NORMAL_PROCESS, 在STATE_MIN_HEIGHT和STATE_MAX_HEIGHT之间
-
这个方法回调表示[HeaderLayout]的Bottom正在最小高度与最大高度之间
-
@param child 当前需要做变换的view
-
@param parent [HeaderLayout]
-
@param percent 0<percent<1, 值为([HeaderLayout.getBottom] - [HeaderLayout.minHeight]) / ([HeaderLayout.maxHeight] - [HeaderLayout.minHeight]])
-
且值不会为0或者1, 为0相当于是回调了[onStateMinHeight], 为1相当于回调了[onStateMaxHeight], 由于值不会为0或1,
-
所以在回调[onStateMinHeight]和[onStateMaxHeight]时会有一个未消耗的dy
-
@param dy 滑动的距离
*/
fun onStateNormalProcess(child: V, parent: HeaderLayout, percent: Float, dy: Int)
/**
-
@see [HeaderLayout.scrollState]为STATE_MAX_HEIGHT
-
这个方法回调表示[HeaderLayout]的Bottom正处于[HeaderLayout.maxHeight]
-
@param child 当前需要做变换的view
-
@param parent [HeaderLayout]
-
@param unConsumedDy 由其他状态到此状态未消耗完的dy
*/
fun onStateMaxHeight(child: V, parent: HeaderLayout, unConsumedDy: Int)
/**
-
@see [HeaderLayout.scrollState]为STATE_EXTEND_PROCESS, 在STATE_MAX_HEIGHT和STATE_EXTEND_MAX_END之间
-
这个方法回调表示[HeaderLayout]的Bottom正处于[HeaderLayout.maxHeight] 和 [HeaderLayout.maxHeight] + [HeaderLayout.extendHeight]之间
-
@param child 当前需要做变换的view
-
@param parent [HeaderLayout]
-
@param percent 0<percent<1, 值为([HeaderLayout.getBottom] - [HeaderLayout.maxHeight]) / [HeaderLayout.extendHeight]
-
且值不会为0或者1, 为0相当于是回调了[onStateMaxHeight], 为1相当于回调了[onStateExtendMaxEnd], 由于值不会为0或1,
-
所以在回调[onStateMaxHeight]和[onStateExtendMaxEnd]时会有一个未消耗的dy
*/
fun onStateExtendProcess(child: V, parent: HeaderLayout, percent: Float, dy: Int)
/**
-
@see [HeaderLayout.scrollState]为STATE_EXTEND_MAX_END,
-
这个方法回调表示[HeaderLayout]的Bottom正处于[HeaderLayout.maxHeight] + [HeaderLayout.extendHeight]
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
如果你进阶的路上缺乏方向,可以加入我们的圈子和安卓开发者们一起学习交流!
-
Android进阶学习全套手册
-
Android对标阿里P7学习视频
-
BATJ大厂Android高频面试题
最后,借用我最喜欢的乔布斯语录,作为本文的结尾:
人这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。
你的时间有限,所以不要为别人而活。不要被教条所限,不要活在别人的观念里。不要让别人的意见左右自己内心的声音。
最重要的是,勇敢的去追随自己的心灵和直觉,只有自己的心灵和直觉才知道你自己的真实想法,其他一切都是次要。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。
你的时间有限,所以不要为别人而活。不要被教条所限,不要活在别人的观念里。不要让别人的意见左右自己内心的声音。
最重要的是,勇敢的去追随自己的心灵和直觉,只有自己的心灵和直觉才知道你自己的真实想法,其他一切都是次要。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!