1.View相关的属性设置
1.1 创建View类
直接继承View,多用于作图
继承FrameLayout,多用于xml内部含多个控件的情况
1.1.1 代码创建控件
constructor(context:Context):super(context){}
1.1.2 xml创建文件
constructor(context: Context, attrs: AttributeSet?):super(context,attrs){}
1.1.3 xml里设置了style样式时创建文件
constructor(context: Context, attrs: AttributeSet?, style:Int):super(context, attrs,style){}
1.2 由xml自定义view的特殊操作
init {
// 讲layout布局文件和当前这个类相关联
val layoutInflater = LayoutInflater.from(context) // 使用布局解析器解析出来
val lp = FrameLayout.LayoutParams( // 创建一个布局参数
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
addView(binding.root,lp)//将解析出来的View添加到当前的容器里面 显示出来
}
我先获取好了对应的binding, 这里是获取布局解析器,并得到一个布局参数
1.3 设置View的自定义属性
1.3.1 在value中创建attrs ,resource File,找到对应的自定义View,并规划自定义的属性
注意除了枚举类 enum可以给对应值 ; 其他属性都只给属性名和对应得基本类型
1.2.2 解析自定义属性得方式
找到context和AttributeSet,先将属性设置器与自定义的attrs传进去,再一个一个属性,通过getString等方法设置 属性对应的值
最后记得recycle
2.绘制动画方法
2.1准备工具
2.1.1 懒加载画笔
2.2 测量此控件尺寸
固定的套路哦
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//定义变量保存最终的宽和高
var mWidth = 0
var mHeight = 0
//获取外部设置的模式
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
when (widthMode){
MeasureSpec.EXACTLY -> mWidth = widthSize
MeasureSpec.AT_MOST -> mWidth = textWidth()
}
when(heightMode){
MeasureSpec.EXACTLY -> mHeight = heightSize
MeasureSpec.AT_MOST ->mHeight = textHeight()
}
//告诉外部(父容器) 自己的尺寸
setMeasuredDimension(mWidth,mHeight)
}
2.2.1 封装数据换算方法
dp To px 最重要!!!
fun px2dp(px:Int):Int{
return (px / context.resources.displayMetrics.density).toInt()
}
fun dp2px(dp:Int) = context.resources.displayMetrics.density * dp
fun sp2px(sp:Float):Float{
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,sp,context.resources.displayMetrics)
}
2.3 封装各个绘制部分
2.3.1 静态图像
找到几何关系 ,确定绘制基本要素
2.3.2 动态图像
重写onSizeChanged() ,重新计算整个高度与宽度
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
//重新计算音浪的宽度 高度 间距
if (height != mWaveHeight){
mWaveHeight = height
}
if (width != (mWaveWidth*3+2*mSpace)){
mSpace = width / 10
mWaveWidth = (width - 2*mSpace)/3
}
}
设置属性动画 ,此处为多个属性一起设置 ,设置的类型 补间动画同样有
private val mAnimatorSet: AnimatorSet by lazy {
//保存三个属性动画
val animators = arrayListOf<Animator>()
for (i in 0 until 3)
ValueAnimator.ofFloat(0f,1f).apply {
duration = mWavePase.toLong()
repeatCount = ValueAnimator.INFINITE//重复次数:无限
repeatMode = ValueAnimator.REVERSE//重复模式:到终点即反向
startDelay = i*mDelay//各波浪间隔
animators.add(this)
addUpdateListener {//实时更新 各波浪高度
mHeightRateArray[i] = it.animatedValue as Float
invalidate()
}
}
AnimatorSet().apply {/**所有属性动画一起开启*/
playTogether(animators)
}
}
属性动画开启,暂停与结束
fun startAnimation(){
mAnimatorSet.start()
}
fun pauseAnimation(){
mAnimatorSet.pause()
}
fun stopAnimation(){
mAnimatorSet.cancel()
}