2021-03-24 继承ViewGroup自定义View

这次需求是写基础组件,类似设置项中的item,但是需要有带输入框的。
思路是继承ConstraintLayout,手动生成View并建立约束。

2021-03-24 继承ViewGroup自定义View_第1张图片
预览效果图

1. 如何用代码的方式给子View建立约束关系?

eg: 让子View上下居中,水平靠后布局

  • 使用ConstraintSet

    ConstraintSet().let {
      it.connect(this.id,ConstraintSet.TOP,ConstraintSet.PARENT_ID,ConstraintSet.TOP)
      it.connect(this.id,ConstraintSet.BOTTOM,ConstraintSet.PARENT_ID,ConstraintSet.BOTTOM)
      it.connect(this.id,ConstraintSet.END,ConstraintSet.PARENT_ID,ConstraintSet.END)
      it.applyTo(this@MyView)
    }
    
  • 设置子View的LayoutParams内的参数

    this.layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
      this.endToEnd = ConstraintSet.PARENT_ID
      this.topToTop = ConstraintSet.PARENT_ID
      this.bottomToBottom = ConstraintSet.PARENT_ID
    }
    

2. 约束布局xml里面的类似 topToTopOf="parent"在代码中代表什么?

如 1 中所示,关联的是parent的id。默认是ConstraintSet.PARENT_ID,这个值为0;当然我们可以手动在xml中给约束布局设置id,或者使用View.generateViewId()在代码中设置,然后通过这个id建立约束。

就是这样

   id = View.generateViewId()
   val t = TextView()
   t.layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
   this.endToEnd = [email protected]
   this.topToTop =  [email protected]
   this.bottomToBottom =  [email protected]
 }

3. 红色提示如何优雅展示?

Android给ViewGroup其实内置了默认的动画,只是没有开启,这会在子View属性改变时触发。对应到我们的需求,GONEVISIBLE刚好符合。

  • xml中给ViewGroup设置animateLayoutChanges
     
        ...
    
    
  • 代码设置layoutTransition
    if(layoutTransition == null){
      layoutTransition = LayoutTransition().apply {
        this.setDuration(200L)
    }
    

}
```
效果:


提示动画

4. 重写onDraw()绘制分割线没有效果

ViewGroup没有背景默认不会走绘制自身的方法。

1. Draw the background
2. If necessary, save the canvas' layers to prepare for fading
3. Draw view's content
4. Draw children
5. If necessary, draw the fading edges and restore layers
6. Draw decorations (scrollbars for instance)
7. If necessary, draw the default focus highlight

默认打印 background 为 null,这样它认为不需要绘制自身,上面3. Draw view's content对应的onDraw()不会执行。

    1. 设置setWillNotDraw(false)
    1. 设置一个透明颜色的背景,这样在代码中体现出来是一个android.graphics.drawable.ColorDrawable
    1. 因为item通常都需要点击效果,所以我们统一设置水波纹的背景。
    background = TypedValue().let{
        context.theme.resolveAttribute(android.R.attr.selectableItemBackground,it,true)
        ContextCompat.getDrawable(context,it.resourceId)
    }
    

5. 如何恢复和保存子View的状态(Switch 开关,EditText文本)

安卓默认会给所有设置了id的控件实现保存和恢复状态。
但是它会有个isViewIdGenerated()的判断,如果是通过调用View.generateViewId()生成的,则不会保存。

思路有二

  • values资源文件夹下建立ids.xml并写入几个默认id,然后给每个子View一一赋上id。

    绝对不要这么干,这种写法所有id会被打包加入R文件,R文件中的id可以被findViewById找到,当layout中包含多个自定义View时,因为每个View内的子View的id是一样的,后面的自定义View中的子View会覆盖前面的,这样最终缓存的是最后一个View的所有子View,当缓存重建的时候,前面所有的View都会变成最后一个View的属性。

  • 使用``View.generateViewId()并重写onSaveInstanceState()onRestoreInstanceState()`方法。

    override fun onSaveInstanceState(): Parcelable {
        val superState = super.onSaveInstanceState()
        val contentState: Parcelable? = mContentView.onSaveInstanceState()
        val bundle = Bundle()
        bundle.putParcelable(1.toString(), contentState)
        bundle.putParcelable(null, superState)
        return bundle
    }
    
    override fun onRestoreInstanceState(state: Parcelable?) {
        state?.let {
            if (it is Bundle) {
                 val contentState: Parcelable? = it.getParcelable(1.toString())
                 if (contentState != null)
                    mContentView.onRestoreInstanceState(contentState)
        }
    }
    

你可能感兴趣的:(2021-03-24 继承ViewGroup自定义View)