1、前言

本文围绕着实现粒子放大效果,着重讲解android中涉及到动画缩放以及动画集的使用,并且会将讲解一些插值器相关的知识。阅读本文需要读者有一定的自定义View的基础知识,本文将不再讲解自定义View的相关知识,读者需要可以自行去学习,也可以阅读笔者的文章,

自定义View的基本知识。

2、着色器

为了让效果的色彩比较的绚丽,需要让粒子(这里其实就是用小圆点代替)有一个色彩的过渡,所以需要用到着色器。比较常用到的主要有线性渐变的着色器,它可以让粒子整体上看上去具有一个按照线性排列的色彩过渡。了解线性渐变的着色器之前,先了解shader。在paint里面有一个setshader方法,用于设置着色器,着色器的作用就是用于给颜色实现一个跨度的平衡感,如果paint里面包含了shader对象,那么用此paint绘画的线,圆圈,图像之类的图案,都会从shader里面获取颜色,因此,它是实现绚丽色彩的基础。

2.1LinearGradient

笔者更倾向与叫它线性画笔,它是shader的子类,他实现的颜色平衡感是通过线性渐变来实现的。使用它,很简单,只需要知道它的构造函数就行。

public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,

TileMode tile)

其中x0表示起始点x坐标,x1表示终止点x坐标,color0是起始颜色,color1是终止颜色,tile是平铺模式,它主要有三个值分别是TileMode.CLAMP,TileMode.REPEAT,TileMode.MIRROR。分表表示复制模式,重复模式,镜像模式。假如有如下代码:

LinearGradient linearGradient=new LinearGradient(0,0,canvas.getWidth(),canvas.getHeight(), Color.RED,Color.BLUE, Shader.TileMode.REPEAT);

Paint paint=new Paint();

paint.setShader(linearGradient);

canvas.drawCircle(canvas.getWidth()/2,canvas.getHeight()/2,canvas.getWidth()/2,paint);

那么它的效果如下:

可以看到线性渐变画笔可以让颜色有一个过渡,这种感觉是很美妙的。如果我们再加一些动画,一些透明度变化,就可以做出很绚丽的色彩效果了。

3.animator

animator是android3.0之后提供的特性,在这之前使用的是animation,区别在于,前者是可以真正改变属性值的,后者只是改变了视图的位置,但是视图的属性并没有得到任何改变,类型障眼法。animator是一个抽象类,是valueAnimator,objectAnimator等的父类,它主要鉴定了一些属性动画的基本操作,比如启动,暂停设置动画监听器等等。

启动动画:

public void start()

public void end()

上述方法分别用于启动动画和终止动画,start方法由哪个线程启动则运行在哪个线程,end方法则用于终止动画的运行,它会让动画停止并且迅速到结尾的值(属性动画一般会有插值器,默认的是先加速后减速的插值器,用于从一个初始值到结尾制的过渡)。

暂停,继续动画:

public void pause()

public void resume()

pause用于暂停动画,如果动画尚未start,或者已经end,则此调用会被忽略。注意此方法必须和start同一线程被调用。如果要继续动画,则调用resume方法。

设置动画时长:

public abstract Animator setDuration(long duration)

用于给动画从开始到结束设置一个时间长度,单位是毫秒。

设置时间插值器:

public abstract void setInterpolator(TimeInterpolator value);

此方法可以设置时间插值器,它的作用是,可以让动画不处于线性变化的效果,默认的话是先加速后减速的插值器。即动画效果从起始值到终止值得过渡,会经历一个加速减速的过程。

设置监听器:

public void addListener(AnimatorListener listener)

如果希望监听动画是否完成结束或者重复的动作,就可以设置一个动画监听器。animatorListener的原型如下:

3.1 animatorListener

public static interface AnimatorListener {

void onAnimationStart(Animator animation);

void onAnimationEnd(Animator animation);

void onAnimationCancel(Animator animation);

void onAnimationRepeat(Animator animation);

}

当调用start时候,onAnimationStart就会被回调,当end被调用的时候,onAnimationEnd被回调。当调用cancel停止动画的时候,onAnimatonCancel被回调。

以上是animator的基本要点。有了这些基本知识,现在就可以学习ValueAnimator了。

4.valueAnimator

valueAnimator是一个值动画,怎么理解呢?它的作用在于,能够把你设置的初始值,和终止值作为起始点,然后通过插值器,在一段时间内,按照插值器的的计算来设置当前动画的值,因为这些值一般都会影响动画的效果,所以叫做值动画。对于插值器,可能大家比较模糊,插值器主要是用来加速度来改变值的,比如:

AccelerateDecelerateInterpolator

public float getInterpolation(float input) {

return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;

}

这是一个先加速后减速的插值器,它的计算过程是通过getInterpolation来计算返回值的。再细心一点,大家可以研究一下cos函数的数值变化,就知道为什么是先加速后减速了。

4.1常用的初始化valueAnimator的方法

既然valueAnimator是值动画,那么自然包含可以设置值的方法,valueAnimator对一些常用的数值类型提供了支持。比如:

public static ValueAnimator ofInt(int... values)

public static ValueAnimator ofArgb(int... values)

public static ValueAnimator ofFloat(float... values)

它们都可用于设置多个值,注意,千万不要只设置一个值,最少都要两个值,一个代表起始值,一个代表终止值。这三个方法分表表示int数据类型的变化值动画,float,argb颜色的值动画。到这里大家可能迷惑,这些值是除了作为起始点和终止点之外,有什么用处。在valueAnimator中,有一个方法是用于获取属性值的,如下:

public Object getAnimatedValue()

这个方法用于获取动画当前处于的值,这个值必定是介于起始值和终止值之间的。比如我们用ofInt获得了一个valueAnimator,然后监听动画,在每次动画更新的时候,就可以调用getAnimatedValue方法获取当前的int值,这个值可以用来重新设置画笔的粗细,或者圆的半径,矩形的宽和高......

除了这些数据类型之外,也允许自定义数据类型,但是因为是自定义的类型,valueAnimator并不知道你所需要的变化规则是什么,所以你需要给他们提供变化规则。使用自定义数据类型获取valueAnimator的方法如下:

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values)

在讲述这个方法之前,先了解一下TypeEvaluator,从字面值上看,它是用来评估值的。它确实也是这样的。

4.2TypeEvaluator

TypeEvaluator是一个接口,用于开发者自定义值变化的规则,可通过ValueAnimator.setEvaluator方法给值动画设置自定义的值变化规则。在这个接口中,最重要也是为一个的一个方法是:

public T evaluate(float fraction, T startValue, T endValue);

其中fraction是一个插值器提供的值,就是我们前面讲的插值器,通常我们实现这个方法,用result=x0+t*(x1-x0)这个规则就好,x0表示startValue,x1表示endValue,t表示fraction。这样就可以通过getAnimatedValue方法获取到计算结果。

4.3 oFObject方法

现在回到我们刚刚那个方法来。说到,这个方法可以获取一个自定义值变化规则的valueAnimator动画。那么我们需要怎么使用它呢。给出一个例子:

首先需要一个TypeEvaluator提供变化规则。假设现在有一个Circle实体,里面含有x,y,radius分别表示中心点和半径。我们定义一个circle规则的TypeEvaluator

public class CircleEvaluate implements TypeEvaluator {

/**

* 估算值的表达式 result = x0 + t * (x1 - x0)

* x0是startValue,x1是endValue,t是fraction

*

* @param fraction

* @param startValue

* @param endValue

* @return

*/

@Override

public Circle evaluate(float fraction, Circle startValue, Circle endValue) {

float circleX = startValue.getCenterX() + fraction * (endValue.getCenterX() - startValue.getCenterX());

float circleY = startValue.getCenterY() + fraction * (endValue.getCenterY() - startValue.getCenterY());

float radius = startValue.getRadius() + fraction * (endValue.getRadius() - startValue.getRadius());

return new Circle(circleX, circleY, radius);

}

接着,就可以使用它定义我们的valueAnimator了。如下:

ValueAnimator animator = ValueAnimator.ofObject(evaluate, circleGroup[i][j],minCircleGroup[i][j]);

后面两个其实是circle对象。对于valueAnimator,因为是animator的子类,所以包含有基本的start,end等方法,它也可以设置插值器setInterpolator,设置Evaluator.......

5、监听valueAnimator的变更

在valueAnimator里面,我们必须得知道当前的值随着插值器和typeEvaluator的估值,处于什么养的一个状态,所以我们必须监听值得变化,因为值得变化一定伴随着动画的变化,不然怎么叫值动画呢?所以我们需要监听动画的变化来获取估值,然后刷新View重新根据新的值来绘制界面。

public static interface AnimatorUpdateListener {

void onAnimationUpdate(ValueAnimator animation);

}

只要我们监听了这个对象,那么就可以从animation.getAnimatedValue获取当前的估值。

大部分时候,仅仅对一个对象进行动画,并不能满足我们的需求,我们很经常需要对很多对象进行动画的变化,这就涉及到集体的变换了。在android里面,提供了AnimatorSet,来解决这个问题。

6、animatorSet

animatorSet是一个动画集合,里面的动画都保存在一个列表里面,它们可以一起播放,也可以按次序播放,或者指定一定的时间间隔播放。总的来说,对它的操作就相当于对集合中所有的动画的操作。比如调用animatorSet的start方法,等同于调用动画集合所有的动画的start方法。其中调用的次序是可以设置的。知道了这些,下面我们详细了解一下这个类带给了我们什么。

6.1设置动画集合的播放规则

这个播放规则不是动画的播放规则(TypeEvaluator和interpolarter决定),而是动画间的播放规则。主要有

public void playTogether(Animator... items)

public void playTogether(Collection items)

public void playSequentially(Animator... items)

public void playSequentially(List items)

总共提供了两种规则,一种是所有的动画一起播放,另外一种是等前面一个播放完毕再接着播放下一个。

前面提到AnimatorSet只是一个动画的集合,因此,Animator有的它都有,这里就不再说了。

7、使用Animator实现粒子缩放效果

接下来,通过实例讲解animator的使用。例子实现了一个同时对圆点集合进行缩放的效果,在缩放过程中,通过改变圆点的中心坐标和半径,来改变圆点的位置和大小,从而达到粒子缩放效果。

首先定义一个attrs.xml文件。

然后定义小圆点的实体:

package cn.com.chinaweal.mypartical;

/**

* Created by Myy on 2016/8/31.

*/

public class Circle {

private float centerX;

private float centerY;

private float radius;

public Circle(float centerX, float centerY, float radius) {

this.centerX = centerX;

this.centerY = centerY;

this.radius = radius;

}

public float getCenterX() {

return centerX;

}

public void setCenterX(float centerX) {

this.centerX = centerX;

}

public float getCenterY() {

return centerY;

}

public void setCenterY(float centerY) {

this.centerY = centerY;

}

public float getRadius() {

return radius;

}

public void setRadius(float radius) {

this.radius = radius;

}

}

设置圆点的变化规律:

package cn.com.chinaweal.mypartical;

import android.animation.TypeEvaluator;

/**

* 圆点的属性变化评估值,用于动画过程获取值

* Created by Myy on 2016/8/31.

*/

public class CircleEvaluate implements TypeEvaluator {

/**

* 估算值的表达式 result = x0 + t * (x1 - x0)

* x0是startValue,x1是endValue,t是fraction

*

* @param fraction

* @param startValue

* @param endValue

* @return

*/

@Override

public Circle evaluate(float fraction, Circle startValue, Circle endValue) {

float circleX = startValue.getCenterX() + fraction * (endValue.getCenterX() - startValue.getCenterX());

float circleY = startValue.getCenterY() + fraction * (endValue.getCenterY() - startValue.getCenterY());

float radius = startValue.getRadius() + fraction * (endValue.getRadius() - startValue.getRadius());

return new Circle(circleX, circleY, radius);

}

}

接着自定义View实现可缩放的粒子效果:

package cn.com.chinaweal.mypartical;

import android.animation.Animator;

import android.animation.AnimatorSet;

import android.animation.ValueAnimator;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.LinearGradient;

import android.graphics.Paint;

import android.graphics.Shader;

import android.util.AttributeSet;

import android.util.DisplayMetrics;

import android.view.View;

import android.view.WindowManager;

import java.util.ArrayList;

import java.util.List;

/**

* Created by Myy on 2016/8/31.

*/

public class ParticleView extends View {

private Paint circlePaint;//小圆点画笔

private Context context;

private int circleStartColor, circleEndColor;//小圆点起始颜色,终止颜色

private int row = 10, col = 10;

private Circle circleGroup[][],minCircleGroup[][];//圆点数组,最小圆点数组,这个可以用来在圆点动画过程中指定最终值

private float startWidth, startHeight, circleWidth, circleHeight,minCircleBound;

private float circlePadding;//圆点的间距

private float density;//像素密度

private float radius;//圆点半径

private float firstCircleX, firstCircleY;//第一个小圆点的中心坐标

private final static int START=0,ANIMATION_CIRCLE=1;//初始窗台,圆点动画状态

private int status=START;

public ParticleView(Context context) {

super(context);

init(context);

}

public ParticleView(Context context, AttributeSet attrs) {

super(context, attrs);

TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.ParticleView);

circleStartColor = typeArray.getColor(R.styleable.ParticleView_circleStartColor, Color.CYAN);

circleEndColor = typeArray.getColor(R.styleable.ParticleView_circleEndColor, Color.RED);

row = typeArray.getInt(R.styleable.ParticleView_row, 10);

col = typeArray.getInt(R.styleable.ParticleView_col, 10);

init(context);

}

private void init(Context context) {

this.context=context;

circlePaint = new Paint();

circlePaint.setColor(circleStartColor);

circleGroup = new Circle[row][col];

minCircleGroup=new Circle[row][col];

getDensity();

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if(status==START) {

initCanvas(canvas);

}

if(status==ANIMATION_CIRCLE)

{

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

canvas.drawCircle(circleGroup[i][j].getCenterX(), circleGroup[i][j].getCenterY(), circleGroup[i][j].getRadius(), circlePaint);

}

}

}

}

/**

* 初始化布局

* @param canvas

*/

private void initCanvas(Canvas canvas) {

startWidth = canvas.getWidth() - getPaddingLeft() - getPaddingRight();

startHeight = canvas.getHeight() - getPaddingTop() - getPaddingBottom();

circlePadding = 5 * density;

circleWidth = (startWidth - circlePadding * (col - 1)) / col;

circleHeight = (startHeight - circlePadding * (row - 1)) / row;

minCircleBound=Math.min(circleWidth,circleHeight);//以最小的值为直径,这样才可以完全显示所有的圆点

radius = minCircleBound / 2;//获取半径

firstCircleX = minCircleBound / 2;

firstCircleY = circleHeight / 2;

//设置渐变画笔效果

LinearGradient linearGradient = new LinearGradient(0, 0, startWidth, startHeight, circleStartColor, circleEndColor, Shader.TileMode.MIRROR);

circlePaint.setShader(linearGradient);

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

circleGroup[i][j] = new Circle(firstCircleX + (minCircleBound + circlePadding) * i, firstCircleY + (minCircleBound + circlePadding) * j, radius);

minCircleGroup[i][j] = new Circle(firstCircleX + (minCircleBound + circlePadding) * i+circlePadding*3, firstCircleY + (minCircleBound + circlePadding) * j+circlePadding*3, circlePadding);

canvas.drawCircle(circleGroup[i][j].getCenterX(), circleGroup[i][j].getCenterY(), radius, circlePaint);

}

}

}

/**

* 启动动画

*/

public void startAnimation() {

//false表示每个动画都是用自己的插值器否则就是用共有的插值器

status=ANIMATION_CIRCLE;

final AnimatorSet animatorSet = new AnimatorSet();

List animatorList = new ArrayList<>();

for (int i = 0; i < row; i++) {

for (int j = 0; j < col; j++) {

//valueAnimator可以设置动画属性的值在某个区间内以某种方式进行加速或者减速

final int tempRow=i;

final int tempCol=j;

final CircleEvaluate evaluate=new CircleEvaluate();

ValueAnimator animator = ValueAnimator.ofObject(evaluate, circleGroup[i][j],minCircleGroup[i][j]);

animator.setDuration(3000);

animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

Circle circle=(Circle)animation.getAnimatedValue();

circleGroup[tempRow][tempCol]=circle;

invalidate();

}

});

animatorList.add(animator);

}

}

animatorSet.playTogether(animatorList);

animatorSet.start();

animatorSet.addListener(new Animator.AnimatorListener() {

@Override

public void onAnimationStart(Animator animation) {

}

@Override

public void onAnimationEnd(Animator animation) {

}

@Override

public void onAnimationCancel(Animator animation) {

}

@Override

public void onAnimationRepeat(Animator animation) {

}

});

}

/**

* 获取屏幕密度

*/

private void getDensity() {

DisplayMetrics displayMetrics = new DisplayMetrics();

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

windowManager.getDefaultDisplay().getMetrics(displayMetrics);

density = displayMetrics.density;

}

}

读者在看这个例子的时候,关注点不要在哪些半径直径的东西,而是关注valueAnimator和circle之间的变化,以及这些变化如何引起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"

android:gravity="center"

android:weightSum="2"

android:orientation="vertical"

tools:context="cn.com.chinaweal.mypartical.MainActivity">

android:id="@+id/particleView"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_weight="1"

app:circleStartColor="#efefaa"

app:circleEndColor="#aa11aa"

android:background="#efefef"

app:col="8"

app:row="8" />

android:id="@+id/particleView1"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_weight="1"

app:circleStartColor="#eaaefa"

app:circleEndColor="#1133aa"

app:col="10"

app:row="10" />

acitivtiy:

package cn.com.chinaweal.mypartical;

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

private ParticleView particleView;

private ParticleView particleView1;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.shader_layout);

particleView = (ParticleView) findViewById(R.id.particleView);

particleView1=(ParticleView)findViewById(R.id.particleView1);

}

@Override

protected void onStart() {

super.onStart();

particleView.postDelayed(new Runnable() {

@Override

public void run() {

particleView.startAnimation();

particleView1.startAnimation();

}

}, 1000);

}

}

效果图:

开始:

进过一段时间:

如果读者了解了,可以继续研究文字的拖拽,矩形的变化,图片的旋转.......各种效果,综合起来就可以达到很炫的动画,可以用作起始页或者引导页作为欢迎动画......

---------文章写自:HyHarden---------

--------博客地址:http://blog.csdn.net/qq_25722767-----------

android 粒子动画,使用animator实现粒子动画效果相关推荐

  1. android 矢量粒子动画,iOS CAEmitterLayer实现粒子发射动画效果

    iOS实现粒子发射动画效果图 动画效果用 CAEmitterLayer 实现.CAEmitterLayer 显示粒子发射动画,具体的粒子由 CAEmitterCell 封装.代码示例是展示 CAEmi ...

  2. Android源码解析(一)动画篇-- Animator属性动画系统

    Android源码解析-动画篇 Android源码解析(一)动画篇-- Animator属性动画系统 Android源码解析(二)动画篇-- ObjectAnimator Android在3.0版本中 ...

  3. 【游戏开发阅读列表2】动画(Anima2D、粒子、物理等)

    游戏中动画的实现有很多不同方法,帧动画.骨骼动画.基于物理的动画.基于Shader的动画.粒子等. 在这篇文章中,列出了我最近读到过的不同种类动画入门级的文章.视频.关于Unity动画状态机这一类太常 ...

  4. Android进阶学习-属性动画(使用Animator封装特效工具类1)

    其实关于Animation动画还是有点不足的,动画过后容易造成事件丢失,测试下面的例子 TranslateAnimation =new TranslateAnimation(0, 0, 100, 10 ...

  5. Android Activity和Fragment的转场动画

    Activity转场动画 Activity的转场动画是通过overridePendingTransition(int enterAnim, int exitAnim)实现的. 这个方法是API Lev ...

  6. Android View体系(三)属性动画

    上一篇文章讲了View滑动的六种方法,其中一种是使用动画,这篇文章我们来讲一讲动画的其中一种:属性动画. 1.android视图动画和属性动画 视图动画我们都了解,它提供了AlphaAnimation ...

  7. android动画封装,Android属性动画封装,快速构建动画

    Android实现动画效果的方式主要有帧动画.补间动画.属性动画.关于安桌动画的基础知识可以查看这篇文章Android属性动画完全解析 这里我要讲的是如何快速构建出一个动画效果,如下图: 如果我们用属 ...

  8. android弹球动画,Android动画之自定义Evaluator实现弹球效果

    前言 今天给大家带来的是自定义Evaluator实现弹球效果,我们先给大家来个效果图. 下面我们介绍具体代码流程 1. 自定义Point类 public class Point { private i ...

  9. android 颜色过渡动画效果,Android buttom textview 颜色平滑过渡的动画效果

    1.TransitionDrawable.例如,在文件夹中绘制一个xml文件,你可以这样写: 然后,在你的xml的实际检视你都引用这个TransitionDrawable在android:backgr ...

最新文章

  1. Mybatis 针对ORACLE和MYSQL的批量插入与多参数批量删除
  2. Webwork 学习之路【08】结合实战简析Controller 配置
  3. P2151-[SDOI2009]HH去散步【矩阵乘法】
  4. jmeter添加html,Jmeter 报告可视化 —— 配置生成测试报告仪表板,Jmeter + Jenkins 自动化构建生成 HTML 报告...
  5. vs中项目解决方案和项目的关系
  6. Python创建并且打开一个mat文件
  7. 一个老程序员对数据库的一点纠结
  8. 生成器(generator)
  9. C# 设置Excel中的数字字符串格式
  10. ImportError: dlopen: cannot load any more object with static TLS 解决
  11. Atititjs javascript异常处理机制与java异常的转换.js exception process
  12. Windows xp 安装的屏幕保护程序
  13. 几个支持SCORM的免费平台
  14. Xweibo2.0nbsp;游客可以访问任何页面【…
  15. 青出于蓝而胜于蓝!他在大学期间用Python开发APP-MMUBee
  16. 面对突如其来的新冠,“AI”可以做点什么?
  17. 【科普】Scrum——从橄榄球争球到敏捷开发
  18. 「短篇小说」灵囚 540 天
  19. linux系统tfs安装,Jenkins使用TFS部署
  20. UE4 UMG多行文本

热门文章

  1. 给app用的 小程序wgt包,调用uni.openLocation 页面空白
  2. 全文检索框架Lucene——原理
  3. java 文件传输 多客户端 传输多文件_java 文件传输 多客户端 传输多文件
  4. 笔记本电脑(laptop)通常具备使用USB设备的功能.实现接口回调(使用面向对象思想编程:接口,多态等).
  5. 可能是全网唯一一个基于windows和java的关于selenium webDriver绕过网站反爬服务的方法
  6. 疑难杂症篇(十)--Catia软件出现“没有合适的许可证来实现xx的请求”解决方案
  7. CAD怎么转换版本?两个办法解决
  8. 【ZZULIOJ】1091: 童年生活二三事(多实例测试)
  9. Zeno节点系统中的C++最佳实践
  10. VERI-ZEXE: Decentralized Private Computation with Universal Setup