使用SurfaceView模拟下雪花的动画
来源:互联网 发布:2017中国经济数据 编辑:程序博客网 时间:2024/05/20 09:11
学习安卓笔记之自定义控件(四)
——使用SurfaceView进行简单的绘制
虽然没赶上移动开发最好的时候,也没赶上最好找工作的时候,但是赶上了学习移动开发最好的时候。资料很多,大部分常见的都能搜索出一大把,当然了“下雪花动画的效果 的示例也是非常的多。今天我也来仿造一个,既然是仿造,那就不能白写,不研究一下原理就没什么意义了。网上实现这个功能方法有很多种,比如在自定义控件中开启线程然后不停的绘制,或者直接SurfaceView去实现这种效果。虽然实现的方式不一样,但是实现的思路是一样的。先看一下将要实现的效果,这里没用到所谓的雪花(其实就是图片,很不幸我手上没有,我干脆就直接画圆圈)。
大致实现思路
- 准备数据模型:其实就是一个类,用于存放要绘制图形的位置、大小和下降偏移量的值。
- 创建类并继承SurfaceView:“雪花”的降落效果就是在这个类中实现的。
- 声明并初始化与绘制相关的值:就是初始化数据模型。
- 绘制图形:在线程中完成界面的刷新,达到不断降落的效果。
创建数据模型
就是创建一个类用于存放要绘制的圆的一些属性,代码如下:
package lyan.downsurfaceview.view2;/** * 圆圈的数据模型 * Author LYJ * Created on 2016/9/20. * Time 13:23 */public class MyCircle { private float centerX;//坐标X private float centerY;//坐标Y private float radius;//半径 private float speed;//下落距离 public MyCircle setSpeed(float speed) { this.speed = speed; return this; } public MyCircle setCenterX(float centerX) { this.centerX = centerX; return this; } public MyCircle setCenterY(float centerY) { this.centerY = centerY; return this; } public MyCircle setRadius(float radius) { this.radius = radius; return this; } public float getCenterX() { return centerX; } public float getCenterY() { return centerY; } public float getRadius() { return radius; } public float getSpeed() { return speed; }}
创建MySurfaceView
我们想要的效果都会在这里去实现。这次我先不把整个类贴出来,我先贴出一些看起来比较干净一些的代码,看看我们创建的类都用到了什么,也就是实现功能的主体:
package lyan.downsurfaceview.view1;import android.content.Context;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;/** * Author LYJ * Created on 2016/9/20. * Time 13:23 */public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable{ /** * 构造方法 * @param context */ public MySurfaceView(Context context) { super(context); } public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); } public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } /** * Surface的回调 * @param surfaceHolder */ @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { } @Override public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { } @Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) { } /** * 线程 */ @Override public void run() { }}
继承SurfaceView之后的构造方法就不讲了,这是必须得有的。从实现的接口来看,我们这里继承了两个接口一个是Runalbe,一个是SurfaceHolder.Callback接口。
Runable很好理解,我先说明一下,为什么这里用到了线程,我们来看一下上面给出的效果图,图中可以看出这些圆圈是不断降落的,这种视觉效果就是通过线程去实现的。实现过程就是循环操作(canvas.drawCircle -> Thread.sleep -> 更改圆圈的纵坐标值)来达到降落动画的效果。
讲SurfaceHolder.Callback接口之前,我先说明一下我对SurfaceView,Surface,SurfaceHolder之间关系的理解,就是当创建SufaceView后SurfaceView内容同时创建了Surface(这是一个常量,它只被创建一次),在Surface第一次创建之后会调用SurfaceHolder.Callback回调接口中的surfaceCreated方法,在该方法中开启线程刷新界面来实现动画效果。(这里可能有误,如果有错的地方请提醒我,我会及时的改正,由于我的studio最近关联不到源码,所以参考了一些资料理解可能有误)
总结一下它们在该示例中的作用:Runable的run方法就是用来刷新界面的,我们将会使用SurfaceHolder.Callback的surfaceCreated来开启线程。
MySurfaceView的具体实现
该类的具体实现如下所示,完全按照上面给出的说明和骨架实现的:
package lyan.downsurfaceview.view2;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PixelFormat;import android.graphics.PorterDuff;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;import android.view.View;import java.util.ArrayList;import java.util.Random;/** * Author LYJ * Created on 2016/9/19. * Time 15:48 */public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable { private Paint paint;//画笔 private int counts = 5;//画圆的次数数 private float screenWidth, screenHeight;//屏幕宽高 private float[] raidusArray; private Thread thread; private boolean isRunning;//判断图片是否在进行移动 private static Random random = new Random();//获取随机数 private ArrayList<MyCircle> circle_xxl = new ArrayList<>();//存圆的集合 private ArrayList<MyCircle> circle_xl = new ArrayList<>(); private ArrayList<MyCircle> circle_l = new ArrayList<>(); private ArrayList<MyCircle> circle_m = new ArrayList<>(); private ArrayList<MyCircle> circle_s = new ArrayList<>(); private SurfaceHolder holder; private Canvas canvas = null; private String theWhite = "#45F8E58D"; /** * 构造方法 * * @param context */ public MySurfaceView(Context context) { this(context, null); } public MySurfaceView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); raidusArray = new float[]{ dip2px(context, 2 * 2), dip2px(context, 4 * 2), dip2px(context, 6 * 2), dip2px(context, 8 * 2), dip2px(context, 10 * 2)};//圆的半径 holder = getHolder(); holder.addCallback(this); //设置底层绘制的surfaceview透明 setZOrderOnTop(true); holder.setFormat(PixelFormat.TRANSPARENT); init(context); addRandomCircle(); } /** * 初始化园 */ private void addRandomCircle() { for (int i = 0; i < counts; i++) { circle_xxl.add(new MyCircle() .setCenterX(random.nextFloat() * screenWidth) .setCenterY(random.nextFloat() * screenHeight) .setRadius(raidusArray[4]) .setSpeed(10)); circle_xl.add(new MyCircle() .setCenterX(random.nextFloat() * screenWidth) .setCenterY(random.nextFloat() * screenHeight) .setRadius(raidusArray[3]) .setSpeed(8)); circle_l.add(new MyCircle() .setCenterX(random.nextFloat() * screenWidth) .setCenterY(random.nextFloat() * screenHeight) .setRadius(raidusArray[2]) .setSpeed(6)); circle_m.add(new MyCircle() .setCenterX(random.nextFloat() * screenWidth) .setCenterY(random.nextFloat() * screenHeight) .setRadius(raidusArray[1]) .setSpeed(4)); circle_s.add(new MyCircle() .setCenterX(random.nextFloat() * screenWidth) .setCenterY(random.nextFloat() * screenHeight) .setRadius(raidusArray[0]) .setSpeed(2)); } } /** * 初始化 * * @param context */ private void init(Context context) { screenWidth = context.getResources().getDisplayMetrics().widthPixels; screenHeight = context.getResources().getDisplayMetrics().heightPixels; //Log.e("屏幕的宽和高","w-->" +screenWidth + "h-->" +screenHeight); paint = new Paint(); paint.setColor(Color.parseColor(theWhite)); paint.setAntiAlias(true); paint.setStyle(Paint.Style.FILL); } /** * run(线程) */ @Override public void run() { while (isRunning) { try { canvas = holder.lockCanvas();//锁定画布 canvas.drawColor(Color.RED, PorterDuff.Mode.CLEAR); if (canvas != null) { //画圆 drawMyCircle(canvas); //改变位置 for (int i = 0; i < counts; i++) { CircleDrop(circle_xxl.get(i), 10); CircleDrop(circle_xl.get(i), 8); CircleDrop(circle_l.get(i), 6); CircleDrop(circle_m.get(i), 4); CircleDrop(circle_s.get(i), 2); } if (canvas != null) { holder.unlockCanvasAndPost(canvas); } } Thread.sleep(200); } catch (Exception e) { e.printStackTrace(); } } } /** * 降落 * * @param myCircle */ private void CircleDrop(MyCircle myCircle, int speed) { if (myCircle.getCenterY() > screenHeight) { myCircle.setCenterY(0); } myCircle.setCenterY(myCircle.getCenterY() + speed); } /** * 画圆 * * @param canvas */ private void drawMyCircle(Canvas canvas) { for (int i = 0; i < counts; i++) { canvas.drawCircle(circle_s.get(i).getCenterX(), circle_s.get(i).getCenterY(), circle_s.get(i).getRadius(), paint); canvas.drawCircle(circle_m.get(i).getCenterX(), circle_m.get(i).getCenterY(), circle_m.get(i).getRadius(), paint); canvas.drawCircle(circle_l.get(i).getCenterX(), circle_l.get(i).getCenterY(), circle_l.get(i).getRadius(), paint); canvas.drawCircle(circle_xl.get(i).getCenterX(), circle_xl.get(i).getCenterY(), circle_xl.get(i).getRadius(), paint); canvas.drawCircle(circle_xxl.get(i).getCenterX(), circle_xxl.get(i).getCenterY(), circle_xxl.get(i).getRadius(), paint); } } /** * callback(回调) * * @param holder */ @Override public void surfaceCreated(SurfaceHolder holder) { thread = new Thread(this); thread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); isRunning = (visibility == VISIBLE); } /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }}
检验下成果
先来看一下布局,布局很简单:
<?xml version="1.0" encoding="utf-8"?><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" android:background="#0c244e" tools:context="lyan.downsurfaceview.MainActivity"> <lyan.downsurfaceview.view2.MySurfaceView android:layout_width="match_parent" android:layout_height="match_parent" /></RelativeLayout>
【运行一下,看看效果】:
增加背景渐变效果
看着上面的图有点单调,接下来再来用属性动画加个背景渐变的效果,MainActivity中的代码示例如下:
package lyan.downsurfaceview;import android.animation.ArgbEvaluator;import android.animation.ObjectAnimator;import android.graphics.Color;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View view = getLayoutInflater().inflate(R.layout.activity_main,null); setContentView(view); //属性动画 ObjectAnimator parentAnimator = ObjectAnimator.ofInt( view,"backgroundColor", Color.parseColor("#ffb300"), Color.parseColor("#0000ff")); parentAnimator.setEvaluator(new ArgbEvaluator()); parentAnimator.setRepeatCount(-1);//播放次数:循环 parentAnimator.setRepeatMode(2);//播放模式:循环播放时不是从头开始,从结尾开始 parentAnimator.setDuration(5000);//播放时间5000ms parentAnimator.start();//播放动画 }}
【点击运行,看一下运行效果】:
总结
我们不是要模拟下雪吗?怎嘛一个雪花都没有。很简单将圆圈换成雪花图片就可以了。
参考资料
- http://www.tuicool.com/articles/VbeAbuJ
- http://blog.csdn.net/luoshengyang/article/details/8661317/
- http://www.360doc.com/content/13/0927/09/7044580_317380131.shtml
- http://blog.csdn.net/zhaoyw2008/article/details/45825069
- http://www.cnblogs.com/bausch/archive/2011/10/20/2219068.html
- http://blog.csdn.net/jackie03/article/details/37922409
- 使用SurfaceView模拟下雪花的动画
- 浅析SurfaceView使用surfaceview制作你想要的动画
- 使用SurfaceView模拟写字板
- Android 用surfaceview模拟帧动画的效果,解决帧动画的OOM问题
- 聊天过程中的飘雪花 ,雪花堆积,雪花消融,聊天过程中飘出的图片,动画
- 使用SurfaceView播放gif动画
- SurfaceView简单使用--可做帧动画
- 显微镜下的雪花片!
- Android之雪花飘落的动画特效
- 使用SurfaceView实现异步动画绘图的小实例
- 使用SurfaceView实现简单的红包雨动画
- Android 使用SurfaceView进行2D动画的开发
- Android-使用SurfaceView多线程绘制动画
- 为什么要使用SurfaceView来实现动画?
- 使用SurfaceView实现飘赞动画
- 使用SurfaceView实现飘赞动画
- 核心动画-雪花
- SurfaceView的基本使用
- linux grep命令详解
- SolveProblem:Git 遇到了early EOF index-pack failed问题
- c#输出九九乘法表
- Qualcomm平台qcril初始化及消息处理流程
- JavaSE学习笔记
- 使用SurfaceView模拟下雪花的动画
- CALayer基本属性
- CTreeCtrl 控件使用总结
- 聊聊JVM:相对全面的GC总结(专栏)
- uip之protothreads
- php结合数据库演示商品多图片上传
- Weblogic10 与 Websphere MQ7集成方案[1]
- LEETCODE之Combination Sum系列
- Activity与Fragment通用的跳转工具类JumpUtil的实现