这次需求是写基础组件,类似设置项中的item,但是需要有带输入框的。
预览效果图
思路是继承ConstraintLayout
,手动生成View并建立约束。
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 = this@MyView.id
this.topToTop = this@MyView.id
this.bottomToBottom = this@MyView.id
}
3. 红色提示如何优雅展示?
Android给ViewGroup其实内置了默认的动画,只是没有开启,这会在子View属性改变时触发。对应到我们的需求,GONE
和VISIBLE
刚好符合。
- xml中给ViewGroup设置
animateLayoutChanges
<LinearLayout ... android:animateLayoutChanges="true"> ... </LinearLayout>
- 代码设置
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()
不会执行。
- 设置
setWillNotDraw(false)
- 设置
- 设置一个透明颜色的背景,这样在代码中体现出来是一个
android.graphics.drawable.ColorDrawable
。
- 设置一个透明颜色的背景,这样在代码中体现出来是一个
-
- 因为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) } }
网友评论