属性动画及硬件加速-自定义view(三)

属性动画

属性动画是通过逐步改变像素值从而达到动画的效果。例如把view从左移到右,通过transformX逐步修改横向坐标值。其他如缩放,旋转,渐变同理。

ViewPropertyAnimator

viewPropertyAnimator属性动画是对于view来说的。任何其他的view都是继承view,包括viewGroup。view内有一个方法animate通过链式调用就可以达到属性动画的效果。它也是属性动画中比较简单实用的方法,但在自定义属性动画中,这种方法就不适合用了,原因就在于该链式调用的api有局限性,也就是不能对自定义属性进行动画,也就不能够满足我们自定义属性动画开发的需求。从而在自定义view中使用到的属性动画就不是选择ViewPropertyAnimator,而是选择另外的属性动画,后面会讲到。

view.animate()
                .translationX(DisplayUtils.dpToPx(200))
                .translationY(DisplayUtils.dpToPx(200))
                .alpha(100)
                .rotationX(100)
                .rotationY(100)
                .scaleX(100)
                .scaleY(100)
                .withStartAction(new Runnable() {
                    @Override
                    public void run() {

                    }
                })
                .withEndAction(new Runnable() {
                    @Override
                    public void run() {
                        
                    }
                })
                .setStartDelay(1000)
                .rotation(360)
                .start();

ViewPropertyAnimator使用的都是些固定的动画和对动画进行监听等等,都能够看下源码就知道如何使用,大家可以查看下,我这里就不赘述了。

ObjectAnimator

ObjectAnimator属性动画可以通过自定义属性达到我们想要的属性动画效果,比如说我这里自定义一个圆,通过改变圆的半径来对圆进行缩放,我们就以这样的效果来举个简单的例子,让大家能够很清晰的对ObjectAnimator属性动画有一个清晰的认识。
第一步:自定义circleView继承view
第二步:初始化画笔和定义变量radius:
private float radius=DisplayUtils.dpToPx(50)
第三步:在ondraw方法中画圆:canvas.drawCircle(getWidth()/2,getHeight()/2,radius,paint);
简单的一个自定义view就算完了,接下来我们再来使用ObjectAnimator


image.png

这里大家可以看到我们定义的属性为radius,然而通过上面的截图,我们能够看到是有问题的,就是提示错误,提示的错误就是我们没有发现setRadius(float radius)这样的方法。我们再回到自定义CircleView处设置该方法,这里的错误提示就没有了。

public void setRadius(float radius){
        this.radius = radius;
    }
public float getRadius(){
        return radius;
    }

但是这里要注意,1.get和set方法都要写,2.虽然这里解决了错误提示的问题,但是当我们运行代码,这里是没有效果的,这又是为什么呢?原因就是我们没有刷新view,也就是在setRadius方法块内在设置完值后要进行刷新view的操作。也就是调用invalidate(),再次运行程序,属性动画效果就有了。

public void setRadius(float radius){
        this.radius = radius;
        invalidate();
    }

    public float getRadius(){
        return radius;
    }

这里是单个属性的使用案例,但在实际的开发过程中,一个view可能会涉及到多个属性动画。这里就要讲到自定义属性的另外一个针对多个属性动画的动画集合AnimatorSet

animatorSet

animatorSet是动画集合,比如一个view有多个动画,比如说有旋转动画,位移动画,缩放动画,以及各类不同参数下对应的动画的一个集合,我们这里可以选择使用animatorSet,同样为了更好的理解属性动画animatorset,我们可以举一个上篇文章最后讲到的camera几何变换中的例子展开,更深入的了解android中动画的使用。
定义三个属性变量分别对应camera的旋转角度,以及上下折叠的关于x轴方向的旋转角度flipRotation ,topFlip ,bottomFlip,并替换掉对应固定的值。然后根据上面讲解的,对这三个变量进行封装成get和set方法并在set方法赋完值后调用invalidate()
我们先来看下效果,这里我们先以bottomFlip属性为例,代码如下:

ObjectAnimator bottomFlipAnimator = ObjectAnimator.ofFloat(view,"bottomFlip",135);
        bottomFlipAnimator.setStartDelay(1000);
        bottomFlipAnimator.setDuration(1000);
        bottomFlipAnimator.start();

由于我这里是使用的markdown,貌似不能插入视频,那么我们就以运行的结果来显示下。顺便提下,如果有读者知道如何在markdown下插入视频,看到这篇文章希望能够提供下解决方案。
运行结果如下:


image.png

接下来我们在把剩下的属性动画设置下并把三种不同的属性动画通过animatorSet.playSequentially添加到动画集合animatorSet中:

ObjectAnimator bottomFlipAnimator = ObjectAnimator.ofFloat(view,"bottomFlip",30);
        bottomFlipAnimator.setDuration(1000);

        ObjectAnimator TopFlipAnimator = ObjectAnimator.ofFloat(view,"topFlip",-30);
        TopFlipAnimator.setDuration(1000);

        ObjectAnimator flipRotationAnimator = ObjectAnimator.ofFloat(view,"flipRotation",270);
        flipRotationAnimator.setDuration(1000);

        AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playSequentially(bottomFlipAnimator,flipRotationAnimator,TopFlipAnimator);
        animatorSet.setStartDelay(1000);
        animatorSet.start();

propertyValuelueHolder

animatorSet动画集合就是这样完成的,当然了除了animatorSet之外,其实ObjectAnimator也同样的提供了类似的api,比如说propertyValueHolder,接下来我们就以例子来看下propertyValuelueHolder的使用,其实也很好理解,第一个参数就是属性名称,第二个参数就是属性值。

PropertyValuesHolder bottomFlipHolder = PropertyValuesHolder.ofFloat("bottomFlip",30);
        PropertyValuesHolder topFlipHolder = PropertyValuesHolder.ofFloat("topFlip",-30);
        PropertyValuesHolder flipRotationHolder = PropertyValuesHolder.ofFloat("flipRotation",270);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view,bottomFlipHolder,flipRotationHolder,topFlipHolder);
        objectAnimator.setStartDelay(1000);
        objectAnimator.setDuration(1000);
        objectAnimator.start();

Keyframe

keyframe叫做关键帧,keyframe想必大家有可能没有接触过,但keyframe是相当好用的,比如你可以对某一个属性动画的效果能够更加符合你理想的效果,比如阻尼系数,平移动画每个时间段不同的运动效果,比如说在100s实现加速过后100s实现匀速最后100s实现减速等等都可以使用keyframe。同样举个简单例子,比如做一个平移动画,先加速然后减速最后加速的效果

float distance = DisplayUtils.dpToPx(300);
        Keyframe keyframe1 = Keyframe.ofFloat(0,0);
        Keyframe keyframe2 = Keyframe.ofFloat(0.1f,0.4f*distance);
        Keyframe keyframe3 = Keyframe.ofFloat(0.9f,0.6f*distance);
        Keyframe keyframe4 = Keyframe.ofFloat(1,distance);
        PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("translationX",
                keyframe1,keyframe2,keyframe3,keyframe4);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(imageView,holder);
        animator.setStartDelay(1000);
        animator.setDuration(1000);
        animator.start();

interpolator

interpolator插值器我之前有一篇文章是专门讲解插值器的如果读者有兴趣的,可以转到我的另外一篇文章android动画(一)之插值器,这里我就不赘述了。

TypeEvaluator

上面讲解到的int或者float类型的属性动画,它们变化过程的每一个值都要被计算出来的,而帮我们计算的就是这里要讲到的TypeEvaluator。TypeEvaluator就是一个计算器,他会对指定类型的属性,精确的计算动画里面每一个属性值,这里的每一个属性值不是指的时间,而是指的动画完成度。
接下来我们实现一个类似于美团点餐的贝塞尔曲线效果,点击左上方的菜单,右下方就是购物车,由于没有图片这里用圆点和正方形表示下,布局文件如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TypeEvaluatorActivity">
    <com.zhaofan.property_animator.view.PointView
        android:id="@+id/view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onStartAnimationClick"/>

    <View
        android:id="@+id/targetView"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignParentBottom="true"
        android:layout_alignParentEnd="true"
        android:background="#000000"/>
</RelativeLayout>
image.png

由于我这里是讲解属性动画相关的知识点,对于贝塞尔曲线大家可以到百度搜索相关的文章。我这里是使用的二阶贝塞尔曲线,公式如下:


image

p0,p1,p2分别表示的是起始点,控制点和结束点,这里的t表示的是完成度

public void onStartAnimationClick(View view) {
        //终点
        int[] endPointLocation = new int[2];
        targetView.getLocationOnScreen(endPointLocation);
        Log.d(TAG, "outLocationX:" + endPointLocation[0] + "-----outLocationY" + endPointLocation[1]);
        PointF endPointF = new PointF(endPointLocation[0], endPointLocation[1]);

        controlPoint.set(endPointLocation[0] / 2f + offsetControlX, endPointLocation[1] / 2f - offsetControlY);
        //起始点
        PointF startPointF = new PointF(0, 0);
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointFEvaluator(), startPointF, endPointF);
        valueAnimator.setStartDelay(1000);
        valueAnimator.setDuration(3000);
        valueAnimator.start();
    }

上面的代码主要就是定义起始点和终点以及控制点,接下来我们使用贝塞尔公式来计算view绘制的轨迹

private static class PointFEvaluator implements TypeEvaluator<PointF> {

        @Override
        public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
            //二阶贝塞尔曲线
            Log.d(TAG, "startValueX:" + startValue.x + "---startValueY:" + startValue.y);
            float x = (1 - fraction) * (1 - fraction) * startValue.x + 2 * fraction * (1 - fraction) * controlPoint.x + fraction * fraction * endValue.x;
            float y = (1 - fraction) * (1 - fraction) * startValue.y + 2 * fraction * (1 - fraction) * controlPoint.y + fraction * fraction * endValue.y;
            return new PointF(x, y);
        }
    }

然后通过实现addUpdateListener监听实现真正的动画操作。

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                PointF pointF = (PointF) animation.getAnimatedValue();
                pointView.setX(pointF.x);
                pointView.setY(pointF.y);
            }
        });

硬件加速

软件绘制:用cpu来使用绘制代码,在软件层面就已经把所有像素都绘制好了,然后所有的像素数据都在你和屏幕交互前都已经准备好了,这种绘制方法叫软件绘制。
硬件绘制:使用cpu调用canvas相关的api,它们并不是绘制出来,而是把它们做初步的处理,转换成GPU操作。屏幕显示的时候,让GPU来绘制转换成实际的像素。
硬件加速也就是使用GPU绘制

GPU如何实现硬件加速

原因一:GPU分担一部分cpu工作任务,同步完成一些绘制工作,从而实现了硬件加速
原因二:跟GPU设计有关,它对绘制简单图形有优势。
原因三:跟软件流程有关。
但是硬件加速也存在一些问题,比较常见的就是兼容性问题
在动画中使用setLayerType如下可以提高性能,这个也是google推荐的


image.png

属性动画的知识点差不多都涉及到了,还有一个就是文本相关的动画,这里就不写出来,如果大家需要看的话,代码demo已经上传到github。目录在property-animator下。

如果写的不对,还望斧正,如有关于属性动画还有遗漏的地方,大家可以在留言中补上,一起学习,一起成长。

property-animator

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

推荐阅读更多精彩内容