闲来无事,看了看RippleEffect的具体实现过程,先将心得写下,与诸位共享…

RippleEffect的效果就是点击View,然后View上一个圆圈慢慢的变大,然后消失…

原效果: RippleEffect

我的Demo效果: 这里写图片描述

原理:重写View的onDraw(Canvas canvas)方法,然后使用canvas.drawCircle(x, y, currentRadius, paint),通过不断的改变currentRadius的值,使其从小到大的变化,来达到这么一种效果。

(1)定义变量:

/** * 圆从最小到最大,需要多长时间 */
private int DURATION = 2000;
/** * 想要画的圆的最大半径 */
private float radiusMax = 0;
/** * View的最大宽度 */
private int WIDTH;
/** * View的最大高度 */
private int HEIGHT;
/** * 记录手指点下去的x坐标 */
private float x;
/** * 记录手指点下去的y坐标 */
private float y;
/** * 对timer的放大系数 */
private int FRAME_RATE = 10;
/** * 通过timer不停的timer++,达到让圆不断增大的效果 */
private int timer = 0;
/** * Handler用来更新界面用的 */
private Handler canvasHandler;
/** * 是否正在播放动画 */
private boolean animationRunning = false;
/** * 画笔 */
private Paint paint;
/** * 结合Handler用来刷新当前界面的任务 */
private final Runnable runnable = new Runnable() {
    @Override
    public void run() {
        invalidate();
    }
};

(2)重写protected void onSizeChanged(int w, int h, int oldw, int oldh)得到我们View的高度和宽度。

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.d("K", "onSizeChanged,w:" + w + ", h: " + h);
        WIDTH = w;
        HEIGHT = h;
    }

(3)在构造函数中,我们需要初始化我们的画笔,用来刷新界面的Handler。

    /** * 初始化画笔,Handler * @param context */
    private void init(Context context){
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(getResources().getColor(android.R.color.white));
        canvasHandler = new Handler();
    }

(4)想象一下,我们用手点击View的时候,点击的位置出现一个逐渐放大的圆,那么我们肯定就需要重写public boolean onTouchEvent(MotionEvent event)方法,然后记录下我们点击的(x, y)坐标,然后以这个(x, y)位置为圆心,以适当半径画一个圆就OK。

(4-1)重写onTouchEvent(MotionEvent event)方法,响应用户触屏事件。

@Override
public boolean onTouchEvent(MotionEvent event) {
    animateRipple(event);
    return super.onTouchEvent(event);
}

(4-2)编写animateRipple函数,在没有播放动画的时候,播放动画,即画圆。

/** * 开始画圆 * @param event */
public void animateRipple(MotionEvent event){
    //当前没有播放动画的时候,执行这些赋值操作
    if(!animationRunning){
        radiusMax = Math.max(WIDTH, HEIGHT);
        Log.d("K","radius:" + radiusMax);
        this.x = event.getX();
        this.y = event.getY();
        animationRunning = true;
        invalidate();
    }
}

这个函数最主要的作用,就是设置animationRunning这个变量,用以标示当前是否正在播放动画。然后确定了我们当前想要画的圆的最大半径是手机宽度和高度的最大值。

(4-3)接下来就得编写我们最核心的函数onDraw(Canvas canvas)了,

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if(animationRunning){
        if(DURATION <= timer * FRAME_RATE){
            animationRunning = false;
            timer = 0;
            canvas.restore();
            Log.d("K", "animation end.");
            return;
        }else{
            //每隔FRAME_RATE刷新一下
            canvasHandler.postDelayed(runnable, FRAME_RATE);
        }
        if(timer == 0){
            canvas.save();
        }
        float currentRadius = radiusMax * (((float) timer * FRAME_RATE) / DURATION);
        Log.d("K", "current radius:" + currentRadius);
        canvas.drawCircle(x, y, (currentRadius), paint);
        timer++;
    }
}

在onDraw函数中,做的最重要的事情就是canvas.drawCircle(x, y, (currentRadius), paint);,这个是我们的核心代码,就是这句代码呈现出来我们想画的圆。那么currentRadius是靠什么来改变的呢?答案就是timer变量,我们知道每调用一次invalidate(),onDraw函数就会被调用一次,所以我们可以在onDraw函数中通过不断的累加timer,来模拟半径的不断增加。DURATION是总的时长,FRAME_RATE相当于是对timer的一个放大系数,FRAME_RATE * timer必须大于DURATION的时候,我们就canvas.restore()恢复现场,回到没有画圆之前的场景,即圆消失。

【源代码下载地址】

你可能感兴趣的内容
jmeter之GUI运行原理 收藏,2287 浏览
Web服务器的工作原理 收藏,2004 浏览
0条评论

dexcoder

这家伙太懒了 <( ̄ ﹌  ̄)>
Owner