自定义View的使用:加载动画(二)——贪吃动画
2021-08-25 本文已影响0人
搬码人
引入
应用在申请网络请求到数据现在以及数据显示等需要时间,特别是在网络不稳定的情况下这个时间间隙特别明显。这个时候我们就需要在这个等待间隙中设置加载动画使当前的应用不那么空白。要实现自定义加载动画,其实质就是给自定义VIew添加属性动画。
在这里,小编推荐一个简单的贪吃动画。读者可在代码上加以完善。
动画样板设计图
尺寸设计
大致描述:点击开始按钮,左边的大嘴就不断地张开闭合嘴巴,右边会有小球不断涌进。
具体效果实现步骤
1、创建类继承于VIew并重写构造方法
constructor(context: Context):super(context){}
constructor(context: Context, attrs: AttributeSet?):super(context,attrs){}
constructor(context: Context, attrs: AttributeSet?, style:Int):super(context,attrs,style){}
后续需要的一些变量
//小圆的半径
private var ballRadius = 0f
//嘴的半径
private val mouseRadius get() = ballRadius*3
//嘴的圆心坐标
private var cx = 0f
private var cy = 0f
//张嘴的角度 动画因子
private var mouseAngle = 0f
//小球移动的动画因子
private var ballTranslateX = 0f
//动画对象
private var mAnimators = mutableListOf<ValueAnimator>()
//画笔
private val mPaint = Paint().apply {
style = Paint.Style.FILL
color = context.resources.getColor(R.color.main_purple,null)
}
2、继承onSizeChanged和onDraw方法
onSizeChanged:计算尺寸
onDraw:对图进行绘制
张嘴闭合动画原理:使用drawArc,定义变量mouseAngle,通过在ValueAnimator中改变mouseAngle 的角度值来实现张嘴闭合动画。
小球移动原理:设置动画因子(变量)ballTranslatedX,在绘制小球的时候在其x坐标上加上ballTranslatedX,然后再在ValueAnimator中改变动画因子的值。使在视觉上不断有小球涌进大嘴中。
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (measuredWidth/8.5 <= measuredHeight/6){
ballRadius =measuredWidth/8.5f
cx = 3*ballRadius
cy = (measuredHeight-6f*ballRadius)/2 + 3*ballRadius
}else{
ballRadius =measuredHeight/6f
cx = (measuredWidth - 8.5f*ballRadius)/2 + 3*ballRadius
cy = 3*ballRadius
}
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
//绘制大嘴
canvas?.drawArc(cx-mouseRadius,cy-mouseRadius,cx+mouseRadius,cy+mouseRadius,
mouseAngle,360-2*mouseAngle,true,mPaint)
//绘制小圆
canvas?.drawCircle(cx+ballTranslateX,cy,ballRadius,mPaint)
}
动画部分设计
其实不难看出这里属性动画的设计都会用到ValueAnimator来改变某个变量的值来达到动画效果。
注:
1、动画设计后面千万不要漏掉invalidate(刷新),否则和没设计动画结果一样,也就是没动画效果。
2、将动画添加到动画变量数组时位置别加错。(也就是下方的mAnimators)
private fun createAnim(){
//大嘴动画
ValueAnimator.ofFloat(0f,45f,0f).apply {
duration = 650
repeatCount = ValueAnimator.INFINITE
addUpdateListener {
mouseAngle = it.animatedValue as Float
//刷新界面
invalidate()
}
mAnimators.add(this)
}
//小球动画
ValueAnimator.ofFloat(4.5f*ballRadius,0f).apply {
duration = 650
repeatCount = ValueAnimator.INFINITE
addUpdateListener {
ballTranslateX = it.animatedValue as Float
//刷新界面
invalidate()
}
mAnimators.add(this)
}
}
//启动动画
private fun start(){
for (anim in mAnimators){
anim.start()
}
}
//动画暂停
private fun stop(){
for (anim in mAnimators){
anim.end()
}
}
//提供给外部使用
//显示动画
fun show(){
createAnim()
start()
}
//隐藏动画 暂停
fun hide(){
stop()
}
测试
设置按钮点击事件测试动画效果
mbinding.mStart.setOnClickListener {
mbinding.mouse.show()
}
mbinding.stop.setOnClickListener {
mbinding.mouse.hide()
}