自定义View属性动画和硬件加速

安卓中的三种动画:
一、Drawable Animation也就是所谓的帧动画,Frame动画。指通过指定每一帧的图片和播放时间,有序的进行播放而形成动画效果。
二、View Animation视图动画,也就是所谓补间动画,Tween动画。指通过指定View的初始状态、变化时间、方式,通过一系列的算法去进行图形变换,从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果。注意:只是在视图层实现了动画效果,并没有真正改变View的属性。三、Property Animation属性动画,通过不断的改变View的属性,不断的重绘而形成动画效果。相比于视图动画,View的属性是真正改变了。注意:Android 3.0(API 11)以上才支持。
Tip:view动画不改变view的真实位置,就是肉眼看上去,view位置发生了变化,但是它的点击区域还是在原来的位置。
https://segmentfault.com/a/1190000011933148

属性动画

1.ViewPropertyAnimator

View.animate()方法调用后返回一个ViewPropertyAnimator对象


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        view.animate().translationX(100f)
            .translationY(100f)
            .scaleX(0.5f)
            .scaleY(0.5f)
            .rotation(270f)
            .setDuration(1000)
            .setStartDelay(1000)
    }
}

2.ObjectAnimator

使⽤ ObjectAnimator.ofXxx() 来创建对象,以及使⽤ObjectAnimator.start() 来主动启动动画。它的优势在于,可以为⾃定义属性设置动画。
CircleView

class CircleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
  private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
  var radius = 50f
    set(value) {
      field = value
      invalidate()
    }

  init {
    paint.color = Color.parseColor("#00796B")
  }

  override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    canvas.drawCircle(width / 2f, height / 2f, radius, paint)
  }
}

MainActivity

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val anim = ObjectAnimator.ofFloat(view,"radius",150f)
        anim.startDelay = 1000
        anim.start()
    }
}

效果

2.1.Interpolator插值器

⽤于设置时间完成度到动画完成度的计算公式,直⽩地说即设置动画的速度曲线,通过 setInterpolator(Interpolator) ⽅法来设置。
常⽤的有 AccelerateDecelerateInterpolator(先加速再减速)、AccelerateInterpolator(一直加速)、DecelerateInterpolator(一直减速)、LinearInterpolator (匀速)

        val anim = ObjectAnimator.ofFloat(view,"translationX",500f)
        anim.startDelay = 1000
        anim.duration = 1500
        anim.interpolator = AccelerateDecelerateInterpolator()
        anim.start()
动画.gif

2.2.AnimatorSet

将多个 Animator 合并在⼀起使⽤,先后顺序或并列顺序都可以

AnimatorSet animatorSet = new AnimatorSet();
 animatorSet.playTogether(animator1, animator2);
 animatorSet.start();

2.3PropertyValuesHolder

⽤于设置更加详细的动画

1.例如多个属性应⽤于同⼀个对象(类似于AnimatorSet,同时做):

        val anim = PropertyValuesHolder.ofFloat("radius",150f)
        val anim2 = PropertyValuesHolder.ofFloat("translationX",300f)
        val animator = ofPropertyValuesHolder(view, anim,anim2)
        animator.startDelay = 1000
        animator.start()
效果

2.配合使⽤ Keyframe ,所有的关键帧对⼀个属性分多个段,会根据关键帧的fraction(进度百分比)和value来决定动画效果

        val length = 500f
        val keyframe1 = Keyframe.ofFloat(0f,0f)
        val keyframe2 = Keyframe.ofFloat(0.2f,0.3f * length)
        val keyframe3 = Keyframe.ofFloat(0.8f,0.5f * length)
        val keyframe4 = Keyframe.ofFloat(1f,length)
        val keyframeHolder = PropertyValuesHolder.ofKeyframe("translationX",keyframe1,keyframe2,keyframe3,keyframe4)
        val animator = ofPropertyValuesHolder(view,keyframeHolder)
        animator.startDelay = 1000
        animator.duration = 3000
        animator.start()

动画.gif

3.TypeEvaluator


⽤于设置动画完成度到属性具体值的计算公式。默认的 ofInt() ofFloat() 已经有了⾃带的 IntEvaluator FloatEvaluator ,但有的时候需要⾃⼰设置Evaluator。例如,对于颜⾊,需要为 int 类型的颜⾊设置 ArgbEvaluator,⽽不是让它们使⽤ IntEvaluator
eg:用evaluator实现字符串的变化
ProvinceView

private val provinces = listOf("北京市",
  "天津市",
  "上海市",
  "重庆市",
  "河北省",
  "山西省",
  "辽宁省",
  "吉林省",
  "黑龙江省",
  "江苏省",
  "浙江省",
  "安徽省",
  "福建省",
  "江西省",
  "山东省",
  "河南省",
  "湖北省",
  "湖南省",
  "广东省",
  "海南省",
  "四川省",
  "贵州省",
  "云南省",
  "陕西省",
  "甘肃省",
  "青海省",
  "台湾省",
  "内蒙古自治区",
  "广西壮族自治区",
  "西藏自治区",
  "宁夏回族自治区",
  "新疆维吾尔自治区",
  "香港特别行政区",
  "澳门特别行政区")

class ProvinceView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
  private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    textSize = 80f
    textAlign = Paint.Align.CENTER
  }
  var province = "北京市"
    set(value) {
      field = value
      invalidate()
      val drawable = ColorDrawable()
      drawable.toBitmap().toDrawable(resources)
    }

  override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    canvas.drawText(province, width / 2f, height / 2f, paint)
  }
}

class ProvinceEvaluator: TypeEvaluator<String>{
  override fun evaluate(fraction: Float, startValue: String?, endValue: String?): String {
    val startIndex = provinces.indexOf(startValue)
    val endIndex = provinces.indexOf(endValue)
    val currentIndex = startIndex + ((endIndex - startIndex) * fraction).toInt()
    return provinces[currentIndex]
  }

}

MainActivity

        val anim = ObjectAnimator.ofObject(view,"province",ProvinceEvaluator(),"澳门特别行政区")
        anim.startDelay = 1000
        anim.duration = 10000
        anim.interpolator = AccelerateDecelerateInterpolator()
        anim.start()
动画.gif

4.Listeners

和 View 的点击、⻓按监听器⼀样,Animator 也可以使⽤ setXxxListener()
addXxxListener() 来设置监听器。

5.ValueAnimator

是最基本的 Animator,它不和具体的某个对象联动,⽽是直接对两个数值进⾏渐变计算。

硬件加速

  • 使⽤ CPU 绘制到 Bitmap,然后把 Bitmap 贴到屏幕,就是软件绘制;
  • 使⽤ CPU 把绘制内容(onDraw())转换成 GPU 操作,交给 GPU,由 GPU 负责真正的绘制,
    就叫硬件绘制;
  • 使⽤ GPU 绘制就叫做硬件加速

1.原理?

  • GPU 分摊了⼯作
  • GPU 绘制简单图形(例如⽅形、圆形、直线)在硬件设计上具有先天优势,会
    更快
  • 流程得到优化(重绘流程涉及的内容更少)

2.缺点?

兼容性

3.离屏缓冲

是什么?
内存中抽出一个单独的⼀个绘制 View(或 View 的⼀部分)的区域,然后贴到屏幕

3.1 setLayerType() 和 saveLayer()

  • saveLayer() 是针对 Canvas 的,所以在 onDraw() ⾥可以使⽤ saveLayer()来圈出具体哪部分绘制要⽤离屏缓冲(太重了,所以最好不使用)
  • setLayerType() 是对整个 View,不能针对 onDraw() ⾥⾯的某⼀具体过程
  init {
    setLayerType(LAYER_TYPE_HARDWARE,null)
    setLayerType(LAYER_TYPE_SOFTWARE,null)
    setLayerType(LAYER_TYPE_NONE,null)
  }

LAYER_TYPE_HARDWARE表示开启View的离屏缓冲并使用硬件绘制
LAYER_TYPE_SOFTWARE表示开启View的离屏缓冲并使用软件绘制
(这个⽅法常⽤来关闭硬件加速,但它的定位和定义都不只是⼀个「硬件加速开关」。它的作⽤是为绘制设置⼀个离屏缓冲,让后⾯的绘制都单独写在这个离屏缓冲内。如果参数填写 LAYER_TYPE_SOFTWARE ,会把离屏缓冲设置为⼀个 Bitmap ,即使⽤软件绘制来进⾏缓冲,这样就导致在设置离屏缓冲的同时,将硬件加速关闭了。)
LAYER_TYPE_NONE表示关闭View的离屏缓冲

3.2 对于系统自带的属性动画


可使用如下代码配合LAYER_TYPE_HARDWARE、LAYER_TYPE_NONE提高动画性能:

view.animate()
    .translationY(500f)
    .withLayer()

开启离屏缓冲,GPU会自动渲染改变后的位置,而不会重绘

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351