android 路径动画制作

1、前言

今天项目要用到一个类似微信发送么么哒,那种屏幕飘表情的功能,所以分析研究了一下,用到的技术应该是路径动画,不知道这样就正不正确,反正就是画一个路径线,然后对象根据这个路径去运动。所以就叫他路径动画了。

路径动画要首先要解决的问题就是怎么画这个路径?然后路径画出来后怎么取路径上的所有点的坐标值?

这里解决这两个问题就看一个类PathMeasure 这个类接收一个path对象,然后可以根据pathMeasure.getPosTan()可以得到长度比例的坐标值。这两个问题就直接搞定了。用path画一个路径然后取点,动态移动对象,就变成了路径动画了。是不是很简单。

2、看效果

3、核心代码

/**
 * 撒花
 *
 * @author yd
 *

 用到的知识点:
 	1、android属性动画
 	2、Path路径绘制
 	3、贝塞尔曲线
 *
 *
 *
 *
 *
 *
 *
 *
 */
public class FllowerAnimation extends View implements AnimatorUpdateListener {

	/**
	 * 动画改变的属性值
	 */
	private float phase1 = 0f;
	private float phase2 = 0f;
	private float phase3 = 0f;

	/**
	 * 小球集合
	 */
	private List<Fllower> fllowers1 = new ArrayList<Fllower>();
	private List<Fllower> fllowers2 = new ArrayList<Fllower>();
	private List<Fllower> fllowers3 = new ArrayList<Fllower>();

	/**
	 * 动画播放的时间
	 */
	private int time = 4000;
	/**
	 * 动画间隔
	 */
	private int delay = 500;

	/**
	 * 资源ID
	 */
//	private int resId = R.drawable.fllower_love;

	public FllowerAnimation(Context context) {
		super(context);
		init(context);
//		this.resId = resId;
	}

	@SuppressWarnings("deprecation")
	private void init(Context context) {
		WindowManager wm = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		width = wm.getDefaultDisplay().getWidth();
		height = (int) (wm.getDefaultDisplay().getHeight() * 3 / 2f);

		mPaint = new Paint();
		mPaint.setAntiAlias(true);
		mPaint.setStrokeWidth(2);
		mPaint.setColor(Color.BLUE);
		mPaint.setStyle(Style.STROKE);

		pathMeasure = new PathMeasure();

		builderFollower(fllowerCount, fllowers1);
		builderFollower(fllowerCount , fllowers2);
		builderFollower(fllowerCount , fllowers3);

	}

	/**
	 * 宽度
	 */
	private int width = 0;
	/**
	 * 高度
	 */
	private int height = 0;

	/**
	 * 曲线高度个数分割
	 */
	private int quadCount = 10;
	/**
	 * 曲度
	 */
	private float intensity = 0.2f;

	/**
	 * 第一批个数
	 */
	private int fllowerCount = 4;

	/**
	 * 创建花
	 */
	private void builderFollower(int count, List<Fllower> fllowers) {

		int max = (int) (width * 3 / 4f);
		int min = (int) (width / 4f);
		Random random = new Random();
		for (int i = 0; i < count; i++) {
			int s = random.nextInt(max) % (max - min + 1) + min;
			Path path = new Path();
			CPoint CPoint = new CPoint(s, 0);
			List<CPoint> points = builderPath(CPoint);
			drawFllowerPath(path, points);
			Fllower fllower = new Fllower();
			fllower.setPath(path);
			fllowers.add(fllower);
		}

	}

	/**
	 * 画曲线
	 * @param path
	 * @param points
	 */
	private void drawFllowerPath(Path path, List<CPoint> points) {
		if (points.size() > 1) {
			for (int j = 0; j < points.size(); j++) {

				CPoint point = points.get(j);

				if (j == 0) {
					CPoint next = points.get(j + 1);
					point.dx = ((next.x - point.x) * intensity);
					point.dy = ((next.y - point.y) * intensity);
				} else if (j == points.size() - 1) {
					CPoint prev = points.get(j - 1);
					point.dx = ((point.x - prev.x) * intensity);
					point.dy = ((point.y - prev.y) * intensity);
				} else {
					CPoint next = points.get(j + 1);
					CPoint prev = points.get(j - 1);
					point.dx = ((next.x - prev.x) * intensity);
					point.dy = ((next.y - prev.y) * intensity);
				}

				// create the cubic-spline path
				if (j == 0) {
					path.moveTo(point.x, point.y);
				} else {
					CPoint prev = points.get(j - 1);
					path.cubicTo(prev.x + prev.dx, (prev.y + prev.dy),
							point.x - point.dx, (point.y - point.dy),
							point.x, point.y);
				}
			}
		}
	}

	/**
	 * 曲线摇摆的幅度
	 */
	private int range = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 70, getResources().getDisplayMetrics());

	/**
	 * 画路径
	 *
	 * @param point
	 * @return
	 */
	private List<CPoint> builderPath(CPoint point) {
		List<CPoint> points = new ArrayList<CPoint>();
		Random random = new Random();
		for (int i = 0; i < quadCount; i++) {
			if (i == 0) {
				points.add(point);
			} else {
				CPoint tmp = new CPoint(0, 0);
				if (random.nextInt(100) % 2 == 0) {
					tmp.x = point.x + random.nextInt(range);
				} else {
					tmp.x = point.x - random.nextInt(range);
				}
				tmp.y = (int) (height / (float) quadCount * i);
				points.add(tmp);
			}
		}
		return points;
	}

	/**
	 * 画笔
	 */
	private Paint mPaint;

	/**
	 * 测量路径的坐标位置
	 */
	private PathMeasure pathMeasure = null;

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);

		drawFllower(canvas, fllowers1);
		drawFllower(canvas, fllowers2);
		drawFllower(canvas, fllowers3);

	}

	/**
	 * 高度往上偏移量,把开始点移出屏幕顶部
	 */
	private float dy = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,40, getResources().getDisplayMetrics());

	/**
	 *
	 * @param canvas
	 * @param fllowers
	 */
	private void drawFllower(Canvas canvas, List<Fllower> fllowers) {
		for (Fllower fllower : fllowers) {
			float[] pos = new float[2];
			canvas.drawPath(fllower.getPath(),mPaint);
			pathMeasure.setPath(fllower.getPath(), false);
			pathMeasure.getPosTan(height * fllower.getValue(), pos, null);
			canvas.drawCircle(pos[0], pos[1], 10, mPaint);
//			Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId);
//			canvas.drawBitmap(bitmap, pos[0], pos[1] - dy, null);
//			bitmap.recycle();
		}
	}

	public void startAnimation() {
		ObjectAnimator mAnimator1 = ObjectAnimator.ofFloat(this, "phase1", 0f,
				1f);
		mAnimator1.setDuration(time);
		mAnimator1.addUpdateListener(this);
		mAnimator1.start();
		mAnimator1.setInterpolator(new AccelerateInterpolator(1f));

		ObjectAnimator mAnimator2 = ObjectAnimator.ofFloat(this, "phase2", 0f,
				1f);
		mAnimator2.setDuration(time);
		mAnimator2.addUpdateListener(this);
		mAnimator2.start();
		mAnimator2.setInterpolator(new AccelerateInterpolator(1f));
		mAnimator2.setStartDelay(delay);

		ObjectAnimator mAnimator3 = ObjectAnimator.ofFloat(this, "phase3", 0f,
				1f);
		mAnimator3.setDuration(time);
		mAnimator3.addUpdateListener(this);
		mAnimator3.start();
		mAnimator3.setInterpolator(new AccelerateInterpolator(1f));
		mAnimator3.setStartDelay(delay * 2);
	}

	/**
	 * 跟新小球的位置
	 *
	 * @param value
	 * @param fllowers
	 */
	private void updateValue(float value, List<Fllower> fllowers) {
		for (Fllower fllower : fllowers) {
			fllower.setValue(value);
		}
	}

	/**
	 * 动画改变回调
	 */
	@Override
	public void onAnimationUpdate(ValueAnimator arg0) {

		updateValue(getPhase1(), fllowers1);
		updateValue(getPhase2(), fllowers2);
		updateValue(getPhase3(), fllowers3);
		Log.i(tag, getPhase1() + "");
		invalidate();
	}

	public float getPhase1() {
		return phase1;
	}

	public void setPhase1(float phase1) {
		this.phase1 = phase1;
	}

	public float getPhase2() {
		return phase2;
	}

	public void setPhase2(float phase2) {
		this.phase2 = phase2;
	}

	public float getPhase3() {
		return phase3;
	}

	public void setPhase3(float phase3) {
		this.phase3 = phase3;
	}

	private String tag = this.getClass().getSimpleName();

	private class CPoint {

		public float x = 0f;
		public float y = 0f;

		/** x-axis distance */
		public float dx = 0f;

		/** y-axis distance */
		public float dy = 0f;

		public CPoint(float x, float y) {
			this.x = x;
			this.y = y;
		}
	}

}

4、项目地址

http://download.csdn.net/detail/hxc2008q/8473053

时间: 2024-10-14 13:53:34

android 路径动画制作的相关文章

Android开机动画 (boot animation)制作

前言 Rom Porting第一部可能就是开机动画的移植,这个移植过程还是相当简单的,简要介绍一下Android 开机动画的制作. Boot Animation Android系统自android 2.0以后,均使用/system/bin/bootanimation程序来显示开机动画,如需要修改开机动画,不用修改代码,只需要按格式要求制作bootanimation.zip包即可.zip存放路径为/system/media/目录下,或者/data/local/目录下.如果两个目录都存在时,优先使用

android旋转动画和平移动画详解,补充说一下如果制作gif动画放到csdn博客上

先上效果图: 我这里用的是GifCam来制作的gif动画,可以在http://download.csdn.net/detail/baidu_nod/7628461下载, 制作过程是先起一个模拟器,然后把GifCam的框拖到模拟器上面,点击Rec的new先,然后点击Rec,然后就save到本地成gif文件 这里做一个左右旋转,上下旋转,和左右移动的动画,先自己建立一个View的类,作为操作的对象: public class MyView extends View { private Paint m

[转]android logo:内核、android开机动画

平台信息:内核:linux2.6/linux3.0系统:android/android平台:S5PV310(samsungexynos4210/4412) 作者:xubin341719(欢迎转载,请注明作者) android开logo,这一块在工作改动的也是比较多的,也比较简单,不同的公司,不同型号的产品,开机的标识不一样. 我们平时目测的开机logo一般是两种:静态的和动画的.其实在实现logo的过程中,有四幅图片:(1).uboot显示:(2).kernel显示logo_linux_clut

android logo、android开机动画改变详解

android logo:内核.android开机动画 android开logo,这一块在工作改动的也是比较多的,也比较简单,不同的公司,不同型号的产品,开机的标识不一样. 我们平时目测的开机logo一般是两种:静态的和动画的.其实在实现logo的过程中,有四幅图片:(1).uboot显示:(2).kernel显示logo_linux_clut244.ppm:(3).android第一幅intilogo.rle:(4).android第二幅,bootanimation.前三幅一般我们做成相同的,

VS2008+GDI实现多幅图像的GIF动画制作

相信很多朋友和我一样,经常由于这或那的原因,需制作一些特定格式的图像.如开发过程中需要给菜单.工具条及按钮等添加对应的图形标识,通过代码或资源导入方式加载这些图像时往往会有较高的格式要求. 比如,为按钮添加"bmp"类型图标,而手头只有jpg"格式的图像,此时若是简单地在图像编辑器里改变"图像大小或保存为后缀"bmp"格式,很多情况是会读取失败并终止程序的. 当然,在如今这个移动互联网如此发达的时代,早就有很多在线图像制作及转换的网站.普遍遇到的

【OpenGL】“我叫MT”纯手工3D动画制作之2——建立模型

最近在家研习面经,温习基础,索性花些时间将本科期间完成的一些学习之作整理出来,分享之余顺便水点经验 其实这个事情起源于一门“计算机图形与动画(Computer Graphics & Animation)”的外方课程,当初的外籍教师Tony教的很认真,对于这门课自己也投入了非常多的时间.言归正传,这里先介绍一些涉及的技术,熟悉的同学请跳过哈~ D.准备工作 需要的相关库有: • QuickTime 7.7.1 for Windows (主要用于播放动画与配音) • QuickTime SDK (开

Android View动画

Animation TypeEvaluator View的animate方法 ValueAnimator ObjectAnimator AnimatorSet 使用xml来创建动画 animation objectAnimator set Animator set 自己定义ObjectAnimator属性 propertyValuesHolder Keyframe Interpolator 贝塞尔曲线 PathInterpolator 加减速引子 Android的动画模式:tween anima

Vectors(2): 绘制优美的路径动画

欢迎Follow我的GitHub, 关注我的CSDN. 时代在发展, 技术在进步, Android的Vector图像的时代已经到来. 在Google的最新支持库v23.2中, AppCompat类已经使用Vector图像, 使得AAR包减少9%, 大约70KB, 惠及所有高版本的应用. 当然我们也可以使用Vector, 瘦身应用. Vector图像是SVG格式在Android的表现形式. SVG图像适应屏幕, 图片较小, 还有很多优点, 参考. 关于Vectors的分析, 主要分为两节: (1)

Android SVG动画PathView源代码解析与使用教程(API 14)

使用的是一个第三方库android-pathview主要是一个自己定义View--PathView.跟全部自己定义View一样,重写了三个构造方法. 而且终于调用三个參数的构造方法,在里面获取自己定义属性. /** * Default constructor. * * @param context The Context of the application. */ public PathView(Context context) { this(context, null); } /** * D