Android自定义控件 -- 仿Tim个人主页布局

作者:opLW

目录

1.ChainLayout – 简介
2.构建整体静态布局
3.加入动态元素

1.ChainLayout – 简介

  • 整体效果
    在这里插入图片描述在这里插入图片描述
    如图所示,将黑色边框以内的部分称为头部黑色边框以下称为可扩展部分
  • 功能列表
    • 头部:头部的高度、头部隐藏部分的高度(隐藏部分主要供下拉使用)。
    • 背景:颜色、图片以及图片的模糊程度。
    • 底部遮罩(蓝色部分):颜色、高度、倾斜方向。
    • 圆形头像:大小、Z轴高度、水平方向的Margin、底部的Margin。
    • 动效 放大背景时的变化速率、松开时背景恢复至初始状态的变化速率及时间。

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。
  • 搭建头部固定控件 添加固定控件部分比较简单,唯一需要需要注意的是添加的时机和自定义底部遮罩。
    • 添加头部控件的时机 都知道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.总结

万水千山总是情,麻烦手下别留情。
如若讲得有不妥,文末留言告知我,
如若觉得还可以,收藏点赞要一起。

opLW原创七言律诗,转载请注明出处

发布了22 篇原创文章 · 获赞 33 · 访问量 8620

猜你喜欢

转载自blog.csdn.net/qq_36518248/article/details/103931927
今日推荐