文档章节

自定义动画效果的Drawable

SunnyTime
 SunnyTime
发布于 2016/07/21 15:53
字数 1635
阅读 26
收藏 0
点赞 0
评论 0

Android提供的的动画系统有属性动画(Property Animation)、补间动画(View Animation)和帧动画(Drawable Animation)。现在比较常用的是属性动画,因为功能比较强大,正常我们直接对视图控件(View)进行属性动画的情况比较多,下面来介绍下动画效果Drawable的实现,相比View来讲Drawable更简单,使用起来也很方便。

一. 自定义Drawable

自定义动画Drawable只要继承Drawable并实现以下4个方法,同时实现Animatable接口:

public class CircleDrawable extends Drawable implements Animatable {
    @Override
    public void draw(Canvas canvas) {
        // 绘图
    }

    @Override
    public void setAlpha(int alpha) {
        // 设置透明度
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        // 设置颜色过滤
    }

    @Override
    public int getOpacity() {
        // 设置颜色格式
        return PixelFormat.RGBA_8888;
    }

    @Override
    public void start() {
        // 启动动画
    }

    @Override
    public void stop() {
        // 停止动画
    }

    @Override
    public boolean isRunning() {
        // 判断动画是否运行
        return false;
    }
}
在这几个方法中我们主要来处理Drawable的绘制,即 draw()方法,和自定义View一样。我们要实现动画效果,也实现Animatable接口,它的3个方法都是和动画相关,方法意图也很明显。下面来自定义一个圆圈逐渐扩散消失的效果:

/**
 * Created by long on 2016/7/2.
 * 圆圈Drawable
 */
public class CircleDrawable extends Drawable implements Animatable {

    private Paint mPaint;
    // 动画控制
    private ValueAnimator mValueAnimator;
    // 扩散半径
    private int mRadius;
    // 绘制的矩形框
    private RectF mRect = new RectF();
    // 动画启动延迟时间
    private int mStartDelay;

    // 自定义一个扩散半径属性
    Property<CircleDrawable, Integer> mRadiusProperty = new Property<CircleDrawable, Integer>(Integer.class, "radius") {
        @Override
        public void set(CircleDrawable object, Integer value) {
            object.setRadius(value);
        }

        @Override
        public Integer get(CircleDrawable object) {
            return object.getRadius();
        }
    };
    public int getRadius() {
        return mRadius;
    }
    public void setRadius(int radius) {
        mRadius = radius;
    }


    public CircleDrawable() {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.WHITE);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
    }


    @Override
    public void draw(Canvas canvas) {
        // 绘制圆圈
        canvas.drawCircle(mRect.centerX(), mRect.centerY(), mRadius, mPaint);
    }

    @Override
    public void setAlpha(int alpha) {
        mPaint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.RGBA_8888;
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        super.onBoundsChange(bounds);
        mRect.set(_clipSquare(bounds));
        if (isRunning()) {
            stop();
        }
        // 计算最大半径
        int maxRadius = (int) ((mRect.right - mRect.left) / 2);
        // 控制扩散半径的属性变化
        PropertyValuesHolder radiusHolder = PropertyValuesHolder.ofInt(mRadiusProperty, 0, maxRadius);
        // 控制透明度的属性变化
        PropertyValuesHolder alphaHolder = PropertyValuesHolder.ofInt("alpha", 255, 0);
        mValueAnimator = ObjectAnimator.ofPropertyValuesHolder(this, radiusHolder, alphaHolder);
        mValueAnimator.setStartDelay(mStartDelay);
        mValueAnimator.setDuration(1200);
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 监听属性动画并进行重绘
                invalidateSelf();
            }
        });
        // 设置动画无限循环
        mValueAnimator.setRepeatMode(ValueAnimator.RESTART);
        mValueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        start();
    }

    /**
     * 裁剪Rect为正方形
     * @param rect
     * @return
     */
    private Rect _clipSquare(Rect rect) {
        int w = rect.width();
        int h = rect.height();
        int min = Math.min(w, h);
        int cx = rect.centerX();
        int cy = rect.centerY();
        int r = min / 2;
        return new Rect(
                cx - r,
                cy - r,
                cx + r,
                cy + r
        );
    }

    /************************************************************/

    @Override
    public void start() {
        mValueAnimator.start();
    }

    @Override
    public void stop() {
        mValueAnimator.end();
    }

    @Override
    public boolean isRunning() {
        return mValueAnimator != null && mValueAnimator.isRunning();
    }

    public void setAnimatorDelay(int startDelay) {
        mStartDelay = startDelay;
    }
}
整个流程还是比较简单,在构造函数里对画笔进行了初始化操作,复写onBoundsChange(Rect bounds)接口来获取图形边框参数,比如将Drawable设置给ImageView时,这里就能获取到ImageView的边框大小。在这方法里将边框裁剪为正方形,因为我们要做圆圈Drawable嘛。然后剩下的就是属性动画的处理了,这里自定义了一个扩散半径属性 mRadiusProperty,用来控制绘制圆圈的半径,除了对半径的控制外还有对透明度的控制。如果对自定义属性Property和PropertyValuesHolder不清楚可以看下这个: Androids属性动画PropertyValuesHolder的使用

来看下给ImageView设置我们自定义的CircleDrawable的效果:


二. 包含多个动画Drawable

同理,实现包含多个动画的自定义Drawable也需要继承Drawable并实现Animatable接口,同时还要实现Drawable.Callback接口。先来看下Drawable.Callback的定义:

/*如果你想实现一个扩展子Drawable的动画drawable,那么你可以通过setCallBack(android.graphics.drawable.Drawable.Callback)
 *来把你实现的该接口注册到动画drawable中。可以实现对动画的调度和执行 
 */   
public static interface Callback {  
    /** 
     * 当drawable重画时触发,这个点上drawable将被置为不可用
     * @param 要求重画的drawable 
     */  
    public void invalidateDrawable(Drawable who);  

    /** 
     * drawable可以通过该方法来安排动画的下一帧。可以仅仅简单的调用postAtTime(Runnable, Object, long) 
     * 来实现该方法。参数分别与方法的参数对应 
     * @param who The drawable being scheduled. 
     * @param what The action to execute. 
     * @param when The time (in milliseconds) to run 
     */  
    public void scheduleDrawable(Drawable who, Runnable what, long when);  

    /** 
     *可以用于取消先前通过scheduleDrawable(Drawable who, Runnable what, long when)调度的某一帧。
     *可以通过调用removeCallbacks(Runnable,Object)来实现 
     * @param who The drawable being unscheduled. 
     * @param what The action being unscheduled. 
     */  
    public void unscheduleDrawable(Drawable who, Runnable what);  
}
当我们需要重绘Drawable时,会调用 invalidateSelf()接口,来看下它是怎么操作的:

/**
 * Use the current {@link Callback} implementation to have this Drawable
 * redrawn.  Does nothing if there is no Callback attached to the
 * Drawable.
 *
 * @see Callback#invalidateDrawable
 * @see #getCallback()
 * @see #setCallback(android.graphics.drawable.Drawable.Callback)
 */
public void invalidateSelf() {
    final Callback callback = getCallback();
    if (callback != null) {
        callback.invalidateDrawable(this);
    }
}
需要如果给Drawable设置了 Drawable.Callback回调,就可以监听这个Drawable的重绘操作,并回调invalidateDrawable(Drawable who)方法。

好了,下面就可以来开始自定义带多个动画的Drawable,直接复用上面写的CircleDrawable,让多个CircleDrawable动画按顺序执行:

/**
 * Created by long on 2016/7/2.
 * 复数Circle的Drawable,需要实现Drawable.Callback接口
 */
public class MultiCircleDrawable extends Drawable implements Animatable, Drawable.Callback {

    // 每个Drawable动画启动的间隔
    private static final int EACH_CIRCLE_SPACE = 200;
    // CircleDrawable数组
    private CircleDrawable[] mCircleDrawables;


    public MultiCircleDrawable() {
        mCircleDrawables = new CircleDrawable[] {
                new CircleDrawable(),
                new CircleDrawable(),
                new CircleDrawable()
        };
        for (int i = 0; i < mCircleDrawables.length; i++) {
            // 设置动画启动延迟
            mCircleDrawables[i].setAnimatorDelay(EACH_CIRCLE_SPACE * i);
            // 设置回调监听,当CircleDrawable发生重绘时就会调用 invalidateDrawable(Drawable who) 方法
            mCircleDrawables[i].setCallback(this);
        }
    }


    @Override
    public void draw(Canvas canvas) {
        for (CircleDrawable drawable : mCircleDrawables) {
            // 分层绘制每个CircleDrawable
            int count = canvas.save();
            drawable.draw(canvas);
            canvas.restoreToCount(count);
        }
    }

    @Override
    public void setAlpha(int alpha) {
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
    }

    @Override
    public int getOpacity() {
        return PixelFormat.RGBA_8888;
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        super.onBoundsChange(bounds);
        for (CircleDrawable drawable : mCircleDrawables) {
            drawable.onBoundsChange(bounds);
        }
    }
    /************************************************************/

    @Override
    public void start() {
        for (CircleDrawable drawable : mCircleDrawables) {
            drawable.start();
        }
    }

    @Override
    public void stop() {
        for (CircleDrawable drawable : mCircleDrawables) {
            drawable.stop();
        }
    }

    @Override
    public boolean isRunning() {
        for (CircleDrawable drawable : mCircleDrawables) {
            if (drawable.isRunning()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void invalidateDrawable(Drawable who) {
        // 需要重绘,子Drawable发生重绘会调用这个方法通知父Drawable,如果有设置Callback回调监听的话
        invalidateSelf();
    }

    @Override
    public void scheduleDrawable(Drawable who, Runnable what, long when) {
    }

    @Override
    public void unscheduleDrawable(Drawable who, Runnable what) {
    }
}
可以看到上面复用了三个CircleDrawable,并给它们设置了 动画启动延迟和 Drawable.Callback回调,并在回调方法invalidateDrawable(Drawable who)里也对当前的MultiCircleDrawable进行重绘,即调用invalidateSelf()。这样就完成了多个动画Drawable的定义,来看下使用效果:

这只是简单介绍自定义Drawable的使用,可以自己定义更多好看的动画效果,以上的源代码:DrawableSample

自定义Drawable的运用可以参考这个:实现360手机助手TabHost的波纹效果

本文转载自:http://blog.csdn.net/github_35180164/article/details/51814279#comments

共有 人打赏支持
SunnyTime
粉丝 5
博文 26
码字总数 17058
作品 0
静安
程序员
android Drawble、Shape实现图像适配和优化

一、屏幕适配说明 在android开发过程中,屏幕适配是一件非常重要的工作,主要原则有以下几点 对于控件而言,尽量不要使用固定的宽度和高度,但推荐使用固定的外边距局和内边距。 对于drawabl...

IamOkay
2014/11/19
0
0
Android实用视图动画及工具系列之一:简单的载入视图和载入动画

实现效果 功能说明 简单的载入视图和载入动画,相信大家一听名字就知道是些什么功能了,本Demo主要实现了安卓逐帧动画的开始播放,暂停和停止功能,适用于新手及新学习Android的码友们,老玩...

jaikydota163
2016/08/02
0
0
Android实用视图动画及工具系列之三:表情加载动画和失败加载动画,人物加载动画

实现效果 功能说明 网速慢时,加载网络数据时,界面怎么处理才美观?载入失败或网络丢包时,如何让界面显得更和谐?这一直是开发人员和美工人员不绝于耳的问题,为了达到功能和UI的完美交互,...

jaikydota163
2016/08/02
0
0
Android实用视图动画及工具系列之二:Toast对话框和加载载入对话框

实现效果 功能说明 类似Toast底色的弹出对话框和加载对话框,主要实现弹出和提示消息的功能,对话框可以实现不被取消,主要功能原理利用了安卓逐帧动画和继承对话框接口来实现,适用于新手及...

jaikydota163
2016/08/02
0
0
StarRatingBar星星切换动画《IT蓝豹》

StarRatingBar星星切换动画 StarRatingBar星星切换动画,很久没有学习一下这个RatingBar了,今天来看看这个RatingBar的动画切换效果, 本例子主要是RatingBar移动效果处理动画,本项目自定义...

抉择很难
2015/10/22
53
0
自定义View时,用到Paint Canvas的一些温故,简单的帧动画(动画一 ,"掏粪男孩Gif"顺便再提提onWindowFocusChanged)

转载请注明出处:王亟亟的大牛之路 之前在绘画的过程中提到了静态的旋转啊,缩放啊,平移等一些效果。那么自定义的View当然也有动态的效果也就是我们的Animation,常用的有三种 View Animati...

ddwhan0123
2016/01/04
0
0
Android开发中基础动画技巧的应用

Android开发中基础动画技巧的应用 一、引言 我是先入门iOS的移动开发者,提到动画开发,iOS开发者很容易联想到3种方式,UIImageView的帧动画,UIView层的属性动画和CoreAnimation动画。Andro...

珲少
2016/08/17
78
0
Android XML文件使用

一、布局文件:在layout目录下,使用比较广泛; 我们可以为应用定义两套或多套布局,例如:可以新建目录layoutland(代表手机横屏布局),layoutport(代表手机竖屏布局),系统会根据不同情况自...

迷途d书童
2012/01/10
0
0
andorid 动画

一、view Animation(视图动画也称作补间动画) 只能设置view的动画,效果有:透明度、旋转、平移、伸缩 java类名 xml关键字 描述信息 AlphaAnimation 放置在res/anim/目录下 渐变透明度动画效...

席道坤
2016/08/08
15
0
一窥 Android 动画

作者:不洗碗工作室 - catango 文章出处: Android动画 版权归作者所有,转载请注明出处 前言 动画效果一直是人机交互的一个非常重要的部分,动画效果的引入,会让交互变得更加友好,让用户获...

不洗碗工作室
2017/11/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

第十四章NFS服务搭建与配置

14.1 NFS介绍 NFS介绍 NFS是Network File System的缩写;这个文件系统是基于网路层面,通过网络层面实现数据同步 NFS最早由Sun公司开发,分2,3,4三个版本,2和3由Sun起草开发,4.0开始Netap...

Linux学习笔记
23分钟前
1
0
流利阅读笔记27-20180716待学习

生了娃照样能打,两位母亲温网会师 Lala 2018-07-16 1.今日导读 现今在生儿育女后回归事业的母亲们已经非常多见,但是很少有人想到,以高强度运动与竞争激烈为特色的竞技体育项目也会有 work...

aibinxiao
23分钟前
1
0
Guava 源码分析(Cache 原理【二阶段】)

前言 在上文「Guava 源码分析(Cache 原理)」中分析了 Guava Cache 的相关原理。 文末提到了回收机制、移除时间通知等内容,许多朋友也挺感兴趣,这次就这两个内容再来分析分析。 在开始之前...

crossoverJie
36分钟前
0
0
OSChina 周一乱弹 —— 如果是你喜欢的女同学找你借钱

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @guanglun :分享Michael Learns To Rock的单曲《Fairy Tale》 《Fairy Tale》- Michael Learns To Rock 手机党少年们想听歌,请使劲儿戳(这...

小小编辑
今天
328
14
NNS域名系统之域名竞拍

0x00 前言 其实在官方文档中已经对域名竞拍的过程有详细的描述,感兴趣的可以移步http://doc.neons.name/zh_CN/latest/nns_protocol.html#id30 此处查阅。 我这里主要对轻钱包开发中会用到的...

暖冰
今天
0
0
32.filter表案例 nat表应用 (iptables)

10.15 iptables filter表案例 10.16/10.17/10.18 iptables nat表应用 10.15 iptables filter表案例: ~1. 写一个具体的iptables小案例,需求是把80端口、22端口、21 端口放行。但是,22端口我...

王鑫linux
今天
0
0
shell中的函数&shell中的数组&告警系统需求分析

20.16/20.17 shell中的函数 20.18 shell中的数组 20.19 告警系统需求分析

影夜Linux
今天
0
0
Linux网络基础、Linux防火墙

Linux网络基础 ip addr 命令 :查看网口信息 ifconfig命令:查看网口信息,要比ip addr更明了一些 centos 7默认没安装ifconfig命令,可以使用yum install -y net-tools命令来安装。 ifconfig...

李超小牛子
今天
1
0
[机器学习]回归--Decision Tree Regression

CART决策树又称分类回归树,当数据集的因变量为连续性数值时,该树算法就是一个回归树,可以用叶节点观察的均值作为预测值;当数据集的因变量为离散型数值时,该树算法就是一个分类树,可以很...

wangxuwei
昨天
1
0
Redis做分布式无锁CAS的问题

因为Redis本身是单线程的,具备原子性,所以可以用来做分布式无锁的操作,但会有一点小问题。 public interface OrderService { public String getOrderNo();} public class OrderRe...

算法之名
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部