android自定义view粒子效果之雨(not surfaceview)

5 篇文章 0 订阅
2 篇文章 0 订阅

首先声明的是,粒子效果不一定是用surfaceview来实现的,只要可以绘制和更新绘制既可以做到很多精彩的画面。


简单的说一下自定义view吧,其实就是继承View,然后生成几个构造方法,这样就是一个简单的自定义view。

public class MyView extends View{

	public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
	}

	public MyView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public MyView(Context context) {
		super(context);
	}

}
但是它什么也做不了,什么也不显示,很多人都知道,覆盖onDraw(Canvas)方法,没错

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		//做自己想做的事情(绘制)
	}

这样,一个可以显示自己绘制的view已经完成一半了,为什么说一半,因为还不会动,但是这会有很多逻辑,而这些逻辑不应该出现在主线程中,所以用Handler来做这部分事情,开启一个处理逻辑的线程通过Handler来更新绘制。

	private Runnable run = new Runnable() {
		public void run() {
			long curTime = 0;
			while (true) {
				
				curTime = System.currentTimeMillis();
				//此处加入逻辑
				
				mHandler.sendEmptyMessage(0);
				curTime = System.currentTimeMillis() - curTime;
				try {
					if(curTime < 30){
						Thread.sleep(30 - curTime);
					}
				} catch (InterruptedException e) {
					break;
				}
			}
		}
	};
	private void logic(){
		//此处加入逻辑处理
	}
	private Handler mHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			invalidate();
		};
	};

好吧,这样基本上一个自定义view的框架就出来了。

铺垫好了后面就会好理解了,绘制和动画基本上都是在onDraw里面和logic里面完成,其他的粒子效果也是。

粒子效果在我看来,其实就是具有相同属性的对象集合运动,说白了,就是一个列表里面有多个对象,每个对象自己都在动,放在一起,就出现了粒子效果。再简单点说,只要定义一个类,这个类有绘制和运动就可以了,然后实例化多个,放在列表里面,绘制的时候遍历每个元素就可以了。


所以要在自定义view中加入一个ArrayList就可以了,然后在onDraw和logic里面遍历list里面的每个元素。说到这里了,其实核心也出来了,粒子效果最终追述到的根源,还是单个元素的定制。


那么粒子效果有大家想象的那么难吗,其实没有,起码我写的几个都是在100行代码左右(单个元素),那么,来看看下雨的效果怎么实现的吧。


如果在手机上看,会很清楚和流畅,因为我用视频录制转换成gif,这样分辨率就小了很多,而且帧率也变了。


单个雨点实际上就是一条线,线的大小可以根据自己的喜好定制随机范围。那么需要定义一些基本的变量。显示的宽,高,随机数。

	/**
	 * 显示区域的宽度
	 */
	protected int width;
	/**
	 * 显示区域的高度
	 */
	protected int height;
	/**
	 * 效果元素的随机对象
	 */
	protected Random rand;

上面的宽高是通过View的getWidth()和getHeight()来传递的。rand也是在构造的时候就已经实例化了。

	public EffectItem(int width, int height){
		this.width = width;
		this.height = height;
		rand =new Random();
	}

上面的EffectItem其实就是Rain,只不过我把它抽离出来了,方便以后其他粒子复用,但是不影响阅读,把它看成Rain就可以了。绘制的区域出来了,那么绘制的线呢?这就通过rand来生成,先看看系统绘制直线的方法:

public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
        native_drawLine(mNativeCanvas, startX, startY, stopX, stopY, paint.mNativePaint);
    }

需要起始x,y和终点x,y。系统有个Rect的类,它有left, top, right, bottom。所以可以使用这个类来辅助。

	<span style="white-space:pre">	</span>int x = rand.nextInt(width);
		int y = rand.nextInt(height);
		int w = rand.nextInt(size / 2);
		int h = rand.nextInt(size);
		
		w = w > h ? h : w;
		
		point.left = x;
		point.top = y;
		point.right = x - w;
		point.bottom = y + h;

size的值可以自己定,我定的是50像素,这样一条线段的坐标就出来了,只要draw里面使用就可以了

	public void draw(Canvas canvas){
		canvas.drawLine(point.left, point.top, point.right, point.bottom, paint);
	}

还记得我前面说的吗,每个元素都会在自定义的View里面的onDraw(Canvas)里面遍历绘制,所以不用途担心这里面的draw不起作用。ok,绘制线出来了,但是,你会发现这个线不动,因为没有逻辑让它动起来。


那么该怎么动呢,对,用速度,要让它往下落,那么y的坐标就一直在变,所有y方向有有个速度,但是你又会发现,一个斜杠在往下掉,不符合自然规律,应该让它按照它的角度和方向落下,听的好难啊啊,其实就2行代码,上面代码你会很奇怪有个w,h,这次有用了,稍微在纸上画画,你会发现这条线段是按照一定规律,即一个系数(是几不重要),只要x方向乘以w和y方向乘以h就可以了,所以如果定义速度的话就好办多了。

	private void reset(){
		int x = rand.nextInt(width);
		int y = rand.nextInt(height);
		int w = rand.nextInt(size / 2);
		int h = rand.nextInt(size);
		
		w = w > h ? h : w;
		
		point.left = x;
		point.top = y;
		point.right = x - w;
		point.bottom = y + h;
		
		int speedX = w;
		int speedY = h;
		
		speedX = speedX == 0  ? 1 : speedX;
		speedY = speedY == 0  ? 1 : speedY;
		speedX = speedX > speedY ? speedY : speedX;
		
		speed.x = -speedX;
		speed.y = speedY;

	}
上面就是生成一个雨点的方法,speed是个Point的对象,这样可以表示x和y方向上的速度。

生成和绘制都有了,就差运动了

	public void move(){
		point.left += speed.x;
		point.top += speed.y;
		point.right = point.right + speed.x;
		point.bottom = point.bottom + speed.y;
		
		if(point.left < 0 || point.left > width || point.bottom > height){
			reset();
		}
		speed.y += rand.nextBoolean() ? 1 : 0;
	}

上面就是运动的方法,就是简单的把位置变换了下,但是当出绘制区域的时候,会重新生成一个雨点,所以,屏幕上实际上就是固定数目的雨点的不停的变换位置。最后的y方向的速度是不定时的将y方向加入加速度,这样就会有重力似的效果。每个元素move方法其实就是在自定义View中的logic中被遍历的。

到此位置,一个雨点的生成,绘制,运动都完成了,只要加入到list中然后在View里面遍历就基本完成了。一个雨点完整的代码如下:

public class RainPoint extends EffectItem{

	private Paint paint = new Paint();
	private	final int size = 50;	//长度在0-50像素
	private Rect point;		//雨点
	private Point speed;		//雨点x,y方向速度
	
	public RainPoint(int width, int height){
		super(width, height);
		point = new Rect();
		speed = new Point();

		paint.setColor(0xffffffff);
		reset();
	}
	
	public void draw(Canvas canvas){
		canvas.drawLine(point.left, point.top, point.right, point.bottom, paint);
	}
	
	public void move(){
		point.left += speed.x;
		point.top += speed.y;
		point.right = point.right + speed.x;
		point.bottom = point.bottom + speed.y;
		
		if(point.left < 0 || point.left > width || point.bottom > height){
			reset();
		}
		speed.y += rand.nextBoolean() ? 1 : 0;
	}
	
	private void reset(){
		int x = rand.nextInt(width);
		int y = rand.nextInt(height);
		int w = rand.nextInt(size / 2);
		int h = rand.nextInt(size);
		
		w = w > h ? h : w;
		
		point.left = x;
		point.top = y;
		point.right = x - w;
		point.bottom = y + h;
		
		int speedX = w;
		int speedY = h;
		
		speedX = speedX == 0  ? 1 : speedX;
		speedY = speedY == 0  ? 1 : speedY;
		speedX = speedX > speedY ? speedY : speedX;
		
		speed.x = -speedX;
		speed.y = speedY;

	}	
	
}
而EffectItem代码如下:
<pre name="code" class="java">public abstract class EffectItem implements EffectBase{
	/**
	 * 显示区域的宽度
	 */
	protected int width;
	/**
	 * 显示区域的高度
	 */
	protected int height;
	/**
	 * 效果元素的随机对象
	 */
	protected Random rand;

	
	public EffectItem(int width, int height){
		this.width = width;
		this.height = height;
		rand =new Random();
	}	
}
EffectBase接口定义了两个方法:
public interface EffectBase {
	/**
	 * 绘制效果
	 * @param canvas
	 */
	public void draw(Canvas canvas);
	
	/**
	 * 效果元素变化
	 */
	public void move();
	
}

这样,一个粒子的完整过程就出来了,而后面其它的粒子也是按照这个模型就行设计的。剩下的就是在list里面加入多个实例了,然后按部就班的在onDraw里面遍历每个元素的draw方法,在logic里面遍历move方法。

 

<span style="white-space:pre">	</span><span style="color:#ff0000;">最重要的是,因为这是自定义View,可以像使用其他View一样,放到布局里面,所以丰富它,可以有无限的遐想展示空间。</span>


下面我再贴出我丰富的几个丰富的效果,后面会说到怎么实现。

变化雨点颜色(在xml中定义属性就可以了)

每个雨点都随机颜色(在xml中定义属性就可以了)


还可以进行区域clip哦


好了,如果谁有好的想法也可以共享一下哦,看看如果能实现,看看效果。详细代码:https://github.com/xianfeng99/Particle

github上面的代码可能没有及时更新。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值