查看原文
其他

让View像粒子一样自由运动

2017-09-04 夏蝉饮冰 终端研发部


前言介绍

升级版的粒子效果,可自定义Bitmap

博文地址:

http://ailoli.me/2017/06/28/粒子升级

正文

 之前作过一篇[萤火虫飞舞粒子效果,当时看还不错。无论是性能还是UI都满足了当时的设计效果,但实际应用到项目中,却发现由于SurfaceView 其本身是绘制在window层面上的,对View本身的属性有很多的限制,用起来却不是很实在,还存在着很多不足。 

于是便将之前的效果重新写了一下,改用继承View来实现,虽然说和SurfaceView 相比,在绘制性能上有那么一丝丝的不足 。但轮子本质的含义还是为了服务于项目,项目中方便的使用才是最重要的。

首先看一下效果图


接着分析实现过程中的几个问题

  • 如何保持不间断的绘制

  • 粒子的运动轨迹控制(随机方向,碰到边界回弹以及旋转)


    问题1:如何保持不间断的绘制   

 View的粒子绘制本身实在onDraw中进行的,所以最开始我的方案是在canvas绘制完一波之后,继续调用

invalidate()方法,这样就形成了一个死循环,就达到了不间断重复绘制的效果。

@Override
protected void onDraw(Canvas canvas) {    super.onDraw(canvas);    
   canvas.save();    //粒子的一波绘制    for (Particle circle : mCircles) {        
       circle.drawItem(canvas);    }    
   canvas.restore();    invalidate(); }

这里需要注意的是,canvas的绘制是一个阻塞的过程,也就是从canvas.save()方法之后,一直到invalidate()之前,是阻塞的。

 重绘是会一直等到所有的粒子绘制完成之后,才会继续调起的。

Tips:

  • canvas的save和restore方法是搭配使用的。save存储之前的canvas状态,restore恢复save之前的状态。

  • save方法是可以多次使用的,可以搭配使用的是方法restoreToCount(saveCount)。参数saveCount从1开始计数,表示可以恢复到第几次save之前的状态。


这种方式的缺点在demo完成之后很明显的体现了出来。

  • 第一,速度不可控制,譬如有些时候恰恰需要粒子变慢一点呢。使用这种方式就不太好实现了。

  • 第二,粒子动画的播放和暂停实现起来不优雅,诚然写一个布尔值来控制也可以,但也难免……太不优雅了吧。反正我个人是比较不喜欢写这种代码的。

那么,最终我的实现方式,是采用了属性动画来实现的,没错——就是ValueAnimator。 来看代码:

private ValueAnimator mParticleAnim; -----------------mParticleAnim = ValueAnimator.ofInt(0).setDuration(30); mParticleAnim.setRepeatCount(ValueAnimator.INFINITE); mParticleAnim.addListener(new AnimatorListenerAdapter() {    
   @Override    
   public void onAnimationRepeat(Animator animation) {        
   super.onAnimationRepeat(animation);        
   Log.d(TAG, "onAnimationRepeat: " + System.currentTimeMillis());        
   invalidate();    
   }
});

这段代码展示出来,你懂得。

 在一个无限循环的属性动画里,在Repeat监听事件里,调用View的invalidate方法重绘。这样每次触发重绘的时间间隔就是属性动画的持续时间。

 要是想控制粒子的运动速率,那么只需要调整动画的持续时间即可。
而且如果要对外暴露粒子动画开始或者停止的方法,只需要控制属性动画的start和stop就行了。

  问题2:粒子的运动轨迹    

在构建轮子的时候,思路其实一直都很清晰。View层级主要是调起和控制绘制。具体要绘制什么东西和路线的控制都由粒子对象内部来实现。这样就可以贯彻单一原则,各自负责各自的东西,降低耦合性。 

 我们来看一下粒子对象Particle内的代码:

public Particle(Bitmap drawBitmap, Matrix matrix, Paint paint, float x, float y, int width, int height) { //绘制的bitmap对象和矩阵对象,矩阵用来控制旋转和运动方向    mDrawBitmap = drawBitmap;    mBitmapMatrix = matrix;    mDrawBitmapWidth = drawBitmap.getWidth();    mDrawBitmapHeight = drawBitmap.getHeight();    mBitmapCenterX = mDrawBitmapWidth / 2f;    mBitmapCenterY = mDrawBitmapHeight / 2f; //画笔对象    mPaint = paint; //view 的宽和高用来判断边界    this.mWidth = width;    this.mHeight = height;    //粒子运动的坐标    this.mX = x;    this.mY = y; //粒子的开始坐标    mStartX = x;    mStartY = y; //x 和y轴的运动方向选择,随机函数    mIsAddX = mRandom.nextBoolean();    mIsAddY = mRandom.nextBoolean();    setRandomParm(); }
private void setRandomParm() { //x 和 y轴每次运动的距离和每次旋转的角度,随机值     mDisX = mRandom.nextInt(2) + 1.2f;     mDisY = mRandom.nextInt(2) + 1.2f;     mAddDegree = mRandom.nextInt(5) + 3f; }

运动轨迹这方面只需要随机出来x和y轴的方向,还有每次递增或者递减的值即可。怎么样是不是丝毫没有技术难度啊。好了,看绘制和到边界的处理代码吧。

public void drawItem(Canvas canvas) {    //绘制    mBitmapMatrix.reset();    mBitmapMatrix.preTranslate(mX += getPNValue(mIsAddX, mDisX), mY += getPNValue(mIsAddY, mDisY));    mBitmapMatrix.preRotate(mDegrees += mAddDegree, mBitmapCenterX, mBitmapCenterY);    canvas.drawBitmap(mDrawBitmap, mBitmapMatrix, mPaint);    Log.d(TAG, "mX : " + mX);    Log.d(TAG, "mY : " + mY);    judgeOutline(); }
private void judgeOutline() {    boolean judgeX = mX <= 0 || mX >= (mWidth - mDrawBitmapWidth);    boolean judgeY = mY <= 0 || mY >= (mHeight - mDrawBitmapHeight);    
   if (judgeX) {        mIsAddX = !mIsAddX;        mIsAddY = mRandom.nextBoolean();        setRandomParm();        
   if (mX <= 0) {            mX = 0;        } else {            mX = mWidth - mDrawBitmapWidth;        }        
       return;    }    
   if (judgeY) {        mIsAddY = !mIsAddY;        mIsAddX = mRandom.nextBoolean();        setRandomParm();        
   if (mY <= 0) {            mY = 0;        } else {            mY = mHeight - mDrawBitmapHeight;        }    } }

以上就是粒子对象内部的运动轨迹和边界判断代码了,怎样,是不是超级简单呢?



demo地址:

https://github.com/JadynAi/Particle


终端研发部提倡: 没有做不到的,只有想不到的

在这里获得的不仅仅是技术!


让心,在阳光下学会舞蹈

让灵魂,在痛苦中学会微笑

—终端研发部—



如果你觉得此文对您有所帮助,欢迎入群 QQ交流群 :232203809   

微信公众号:终端研发部


            

这里学到不仅仅是技术

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存