作者:opLW
目录
1.ChainLayout – 简介
2.构建整体静态布局
3.加入动态元素
1.ChainLayout – 简介
- 整体效果
如图所示,将黑色边框以内的部分称为头部,黑色边框以下称为可扩展部分。 - 功能列表
2.整体静态布局的构建
- 搭建整体控件结构
- 自定义方向
- 要求 目标控件需要由两部分组成。第一部分是固定的头部,这一部分主要显示背景墙,头像以及装饰作用的遮罩。第二部分是可扩展部分,这一部分主要留出来供用户添加自己想要的内容。
- 父类的选择 显然有这种容器类作用的控件不可以继承View来实现,所以我们考虑继承ViewGroup。由于可扩展部分的高度可能很大,所以一个屏幕可能会显示不全。因此为了保证所有的内容可以显示,我们需要使用可以滚动的控件。可以滚动的控件有很多,如ListView、RecyclerView等,但这些定制化程度都比较高,我们并不需要使用到那么复杂的功能,只需要一个可以滚动的容器即可,所以我们考虑使用ScrollView作为父类。
- LinearLayout作为ChainLayout的子View ChainLayout由两部分组成:固定的头部和可扩展部分。ChainLayout继承自ScrollView,由于ScrollView只允许有一个子View,所以向ChainLayout添加一个LinearLayout来装固定的头部和可扩展的部分,同时将LinearLayout的高度设置为WRAP_CONTENT。
private fun initMainContainer() { mainContainer = LinearLayout(context) mainContainer.orientation = LinearLayout.VERTICAL val layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT ) // 调用ScrollView的addView方法,添加唯一子View:mainContainer super.addView(mainContainer, -1, layoutParams) ...... }
- 构建可扩展部分
- 重写ChainLayout的所有addView方法 我们都知道通过addView方法添加子View。但此时ChainLayout已经拥有一个子View:LinearLayout了,所以我们需要重写ChainLayout的所有addView方法,将所有要添加到ChainLayout的控件转而添加到该LinearLayout中。
override fun addView(child: View?) { mainContainer.addView(child) } override fun addView(child: View?, index: Int) { mainContainer.addView(child, index) } ......省略其他addView方法
- 添加LinearLayout的注意点 由于需要重写ChainLayout所有的addView方法。所以第一个代码段中添加LinearLayout时,调用的是super.addView。
- 重写ChainLayout的所有addView方法 我们都知道通过addView方法添加子View。但此时ChainLayout已经拥有一个子View:LinearLayout了,所以我们需要重写ChainLayout的所有addView方法,将所有要添加到ChainLayout的控件转而添加到该LinearLayout中。
- 自定义方向
- 搭建头部固定控件 添加固定控件部分比较简单,唯一需要需要注意的是添加的时机和自定义底部遮罩。
- 添加头部控件的时机 都知道View会经历onMeasure、onLayout、onDraw三个步骤。而ViewGroup在执行自己的onMeasure方法时,会先执行子View的onMeasure方法。所以我们需要在ChainLayout初始化时将所有子View添加进去,保证所有子View得到测量。Java的初始化时在构造方法中,而Kotlin是在init代码块中。
- 制作底部遮罩 底部遮罩只是一个简单的不规则图形,所以我们只需要自定义View,继承自View并重写其onDraw方法即可。详细代码见ChainLayout的内部类Mask
3.加入动态元素
- 滑动控件产生的动效 简单介绍注意点,详细解释见ChainLayout的onTouchEvent方法
- 下拉 如图,下拉主要经历两个阶段的变化。第一阶段:头部的隐藏部分渐渐下滑。第二阶段:头部的隐藏部分继续下滑同时背景放大。
虽然有两个阶段的变化,但是其中头部持续下滑是一直存在的。所以我们只需要让头部下滑并在合适的时间点,让图片开始放大。最后在头部的可见部分和隐藏部分全部显示完时,需要停止下滑。 - 上滑 上滑有两种情况。第一种:头部没有发生过变化。第二种:头部由于下拉产生过变化,此时上滑需要先恢复头部。
- 下拉 如图,下拉主要经历两个阶段的变化。第一阶段:头部的隐藏部分渐渐下滑。第二阶段:头部的隐藏部分继续下滑同时背景放大。
- 下拉松开产生的动效 下拉松开其实和前面的上滑原理是相似的,都是让背景慢慢的恢复至初始状态。但我们可以加入动画来让变化更好看。
由于这里需要修改控件的属性,所以只能用属性动画。而这里我们不使用ObjectAnimator,
因为其是通过反射的方法来修改控件的属性值,这种方法比较消耗性能。同时由于我们是在控件的内部进行操作,可以方便的修改各项属性值,我们需要的只是一个控制动画变化的因素。因此在自定义控件时,我更加喜欢使用ValueAnimator来产生动画效果。
4.总结
- 隐藏部分头部的思路来源于SwipeLayout,其实很多可以下拉显示隐藏部分的控件都是利用TranslationY值来达到隐藏的目的。
- 详细代码可见ChainLayout,欢迎赐教❤。
- Chainlayout中用到的部分资源:图片模糊工具、模拟自然动画的数学公式以及在线调试动画网站
万水千山总是情,麻烦手下别留情。
如若讲得有不妥,文末留言告知我,
如若觉得还可以,收藏点赞要一起。
opLW原创七言律诗,转载请注明出处