属性动画和硬件加速
日常杂谈
06月07日
首先,我感觉这部分内容真的挺无聊的,没有什么让人新奇的感觉。不过为了博客的整体性,我还是想随便整理一下相关的知识和内容。
一如既往,聊聊我的日常生活,最近可能比较忙,所以可能没有那么多大段的时间来写博客。只能用那种零碎的时间来一点一点拼凑。虽然时间少不影响我追番,毕竟这个是我最大的爱好,也是我最大的解压和放松娱乐的方式,这点我不否认和排斥。
今天,停更一周的《我推的孩子》终于又要更新了,感觉非常非常的开心,说实话上一话的神结尾真的是震撼到我了,瞬间的场景变化,人物气场和表情的变化,女配光是通过肢体的不协调就看出了星野爱的未成年性体验经历。综上,期待今天晚上的番剧更新吧~
之前有人在我的博客下,说我的博客废话太多,然后另外一个人回复他说,他倒是挺喜欢我的这种博客方式的。然后那人就在那里阴阳怪气起来了,说什么你是懂反讽的。说实话,我挺惊讶的,没想到程序员圈子里还有这种货色,你是工作不饱和吗?来这里闲着还不如去多学习学习!
06月08日
随着工作强度的增加和要写的东西的复杂性增加,日常杂谈的篇幅可能要会分为好几部分了。正经人谁写日记啊,对,我就是那个不正经人。
前言
其实我感觉这部分的内容挺无趣的,不过我会尽量用比较有趣的案例来讲述吧。想了很久,到底用什么方式来讲述相关的内容会比较有趣,而不是单纯的在读教案。这点其实困惑了我挺久的,非常久,非常久,大概有10分钟左右吧,一直在想一直在想。然后我决定搞一个骚操作,弄个相对来说比较有挑战性的效果:变速旋转入场。
正文
其实想实现同一个动画效果存在非常非常多的方式,唯一的区别就是具体的实现是简单还是困难,性能高还是性能低而已。
首先,我们定义一个视图,在视图的正中间显示一张图片。
在我们的XML布局中定义我们所需要显示的图片信息,并将其居中显示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/root_view"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".CiruyMainActivity"><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/targetView"android:layout_margin="30dp"android:src="@drawable/avatar"android:layout_width="match_parent"android:layout_height="match_parent"/>
</androidx.appcompat.widget.LinearLayoutCompat>
我们先来看一下怎么实现变速旋转入场,这里介绍两种可以实现对应效果的方法:
一、使用官方的动画API
因为我们需要实现的动画效果,实际上非常简单,单纯调用官方API就已经足够能够实现我们的需要。
在Activity中我们获取到XML文件中显示的视图,对其进行动画效果:
val targetView = findViewById<FlipView>(R.id.targetView)//旋转缩放入场targetView.animate().rotation(360f)//原地旋转360度.scaleXBy(-0.5f)//缩小到原来的一半大小.scaleYBy(-0.5f)//缩小到原来的一半大小.setDuration(1000)//设置动画的时长.setStartDelay(1500)//设置动画开始的等待时间.start()
通过如上代码,我们就能实现视图的旋转和同步缩放效果。
二、通过自定义属性动画的方式来实现对应的效果
这个相对于前面的方式可能会稍微复杂一些,但是优点在于这种方式更加灵活,当然也有缺点,这个在之后会进行提及。
这个方式主要分为如下几个步骤:
- 定义一个自定义视图,在自定义视图中定义一个参数,然后我们视图绘制就根据这个参数来进行绘制。
这里使用了kotlin中的重载操作符,让后续进行进度计算时,代码更加清晰。没办法,代码强迫症,不太喜欢让代码变得太过于复杂。
class ObjectAnimatorView(context: Context, attributeSet: AttributeSet) :View(context, attributeSet) {private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG)var mViewParam = ViewParam(0f, 1f)set(value) {field = valueinvalidate()//在接收到属性更新的时候,让视图在下次刷新页面的时候刷新状态}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)//获取到图片文件,根据具体的scale值来动态决定到底获取多大的视图信息val bitmap = R.drawable.avatar.getBitmap(context, (width*mViewParam.scale).toInt())val bWidth = bitmap.width//图片宽度val bHeight = bitmap.height//图片高度canvas.save()//旋转图片canvas.translate(width/2f,height/2f)canvas.rotate(mViewParam.rotate)canvas.translate(-width/2f,-height/2f)canvas.drawBitmap(bitmap, width / 2f - bWidth / 2, height / 2f - bHeight / 2, mPaint)canvas.restore()}}
//自定属性存储bean,重载操作符
data class ViewParam(val rotate: Float, val scale: Float){operator fun plus(other: ViewParam) = ViewParam(rotate+other.rotate,scale+other.scale)operator fun minus(other: ViewParam) = ViewParam(rotate-other.rotate,scale-other.scale)operator fun times(time:Float) = ViewParam(rotate*time,scale*time)
}
- 在Activity中获取到这个视图,自定义一个ObjectAnimator,启动动画。
val targetView: ObjectAnimatorView = findViewById<ObjectAnimatorView>(R.id.targetView)ObjectAnimator.ofObject(targetView,"mViewParam",typeEvaluator,ViewParam(360f, 0.5f)//原地旋转360度,同时缩小宽高到原来的一半).apply { startDelay = 1000duration = 1000}.start()
//自定义每一个时刻的动画播放进度val typeEvaluator:TypeEvaluator<ViewParam> =TypeEvaluator<ViewParam> { fraction, startValue, endValue -> startValue + (endValue-startValue)*fraction }
另外再谈谈硬件加速吧,什么是硬件加速?设备中其实有一个叫GPU的好东西,它相对于CPU更擅长处理简单的动画变换效果。而将这部分所谓的简单的动画效果交给GPU来处理,
开启视图硬件加速的方式:在视图初始化代码中添加如下方法,
init {setLayerType(LAYER_TYPE_HARDWARE, null)}
另外还需要再动画方法里面添加离屏缓存,即withLayer方法:
//旋转缩放入场
targetView.animate().rotation(360f).scaleXBy(-0.5f).scaleYBy(-0.5f).setDuration(1000).setStartDelay(1500).withLayer().start()
注意:Android硬件加速只对如上这样官方定义的简单效果有性能提升效果,具体哪些有优化效果请参考官方文档。对于自定义的动画效果,反而会影响性能。