Android 自定义 view 使用 Canvas 实现支付宝咻一咻功能

781 阅读5分钟
原文链接: blog.csdn.net

昨天写了一篇关于支付宝咻一咻的功能,但是一直想通过使用Canvas来实现它,之前有的地方没想通,今天突然想通了,今天就自定义继承view而不是继承ViewGroup或者容器view来实现,但是也有个缺点,就是点击区域问题,先一步步来实现它,

第一步:画图片


上面画的是drawBitmap的时候离左边和上边的值,思路通过图应该实现起来不难,代码如下:

package com.alipay_xiuyixiu.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
import com.alipay_xiuyixiu.R;
/**
 * 仿支付宝咻一咻功能
 * Created by admin on 2016/12/30.
 */
public class AlipayView extends View {
    private int bpWidth;//图片的宽度
    private int bpHeight;//图片的高度
    private int width;//view的宽度
    private int height;//view的高度
    private Paint paint;
    private Bitmap bitmap;
    public AlipayView(Context context) {
        this(context,null);
    }
    public AlipayView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public AlipayView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        initBitmap();
    }
    private void initBitmap() {
        bitmap =  BitmapFactory.decodeResource(getResources(), R.mipmap.icon);
        bpWidth = bitmap.getWidth();
        bpHeight = bitmap.getHeight();
    }
    private void initPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.FILL);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(bitmap,width/2-bpWidth/2,height/2-bpHeight/2,null);
    }
}
效果图:


第一步实现出来了,那么第二步就是画圆,先绘制一个圆,要想达到水波纹的效果,哪就要让圆的半径是不断变化的过程,

第二步:绘制单个半径不断变化的圆,使用handler间隔多少时间去更新UI即可

package com.alipay_xiuyixiu.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import com.alipay_xiuyixiu.R;
/**
 * 仿支付宝咻一咻功能
 * Created by admin on 2016/12/30.
 */
public class AlipayView extends View {
    private int bpWidth;//图片的宽度
    private int bpHeight;//图片的高度
    private int width;//view的宽度
    private int height;//view的高度
    private Paint paint;
    private Bitmap bitmap;
    private int raduis;//圆的半径
    private Handler mHandler = new Handler();
    public AlipayView(Context context) {
        this(context,null);
    }
    public AlipayView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public AlipayView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        initBitmap();
    }
    private void initBitmap() {
        bitmap =  BitmapFactory.decodeResource(getResources(), R.mipmap.icon);
        bpWidth = bitmap.getWidth();
        bpHeight = bitmap.getHeight();
    }
    private void initPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.parseColor("#0099CC"));
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(width/2,height/2,raduis,paint);
        canvas.drawBitmap(bitmap,width/2-bpWidth/2,height/2-bpHeight/2,null);//千万要注意这个bitmap要绘制在圆的后面 不然会被圆盖住
    }
    /**
     * 开始水波纹效果
     */
    public void startRipper(){
        mHandler.post(runnable);
    }
    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            if(raduis>=width/2-20){
                raduis=Math.min(bpWidth,bpHeight)/2;
            }else{
                raduis+=3;
            }
            postInvalidate();
            mHandler.postDelayed(runnable,20);
        }
    };
}
效果:


这是第二步实现的效果,离我们想要的目标就是要同时是多个圆的半径不断的发生变化,

第三步:绘制多个半径不断变化的圆

思路:

a:记录当前的时间然后每隔多少毫秒添加一个半径到集合中,然后重新记录当前的时间

b:加入半径后就对半径进行不断的变化

c:判断集合中存储的半径的值是否大于我们预先规定的值,大于了就移除

根据上面三点进行编码:

package com.alipay_xiuyixiu.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.alipay_xiuyixiu.R;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
 * 仿支付宝咻一咻功能
 * Created by admin on 2016/12/30.
 */
public class AlipayView extends View {
    private int bpWidth;//图片的宽度
    private int bpHeight;//图片的高度
    private int width;//view的宽度
    private int height;//view的高度
    private Paint paint;
    private Bitmap bitmap;
    private int raduis;//圆的半径
    private Handler mHandler = new Handler();
    private List<Integer> raduisList = Collections.synchronizedList(new ArrayList<Integer>());//存储半径的集合
    private long intervalTime = 300;//间隔的时间
    private long currentTime = System.currentTimeMillis();//系统当前的时间
    public AlipayView(Context context) {
        this(context,null);
    }
    public AlipayView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }
    public AlipayView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        initBitmap();
    }
    private void initBitmap() {
        bitmap =  BitmapFactory.decodeResource(getResources(), R.mipmap.icon);
        bpWidth = bitmap.getWidth();
        bpHeight = bitmap.getHeight();
        raduis = Math.min(bpWidth,bpHeight)/2;
        raduisList.add(raduis);
    }
    private void initPaint() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.parseColor("#0099CC"));
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for(int i=0;i<raduisList.size();i++){
            canvas.drawCircle(width/2,height/2,raduisList.get(i),paint);
        }
        canvas.drawBitmap(bitmap,width/2-bpWidth/2,height/2-bpHeight/2,null);//千万要注意这个bitmap要绘制在圆的后面 不然会被圆盖住
    }
    /**
     * 开始水波纹效果
     */
    public void startRipper(){
        mHandler.post(runnable);
    }
    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            postInvalidate();
            if(System.currentTimeMillis()-currentTime>intervalTime){
                raduisList.add(Math.min(bpWidth,bpHeight)/2);
                currentTime = System.currentTimeMillis();
            }
            for(int i=0;i<raduisList.size();i++){//改变每个半径的值
                raduisList.set(i,raduisList.get(i)+3);
            }
            //判断半径是否超过预先设定的值
            Iterator<Integer> iterator = raduisList.iterator();//使用这个迭代防止出现并发修改的异常
            while (iterator.hasNext()) {
                Integer r = iterator.next();
                if(r>=(width/2-20)){
                    iterator.remove();
                }
            }
            mHandler.postDelayed(runnable,20);
        }
    };
}
我把paint的设置成了paint.setStyle(Paint.Style.STROKE);这样能更好的观察是否有个半径不断的在变化,效果:


生成了4个圆,这生成多少个圆,在于你每隔多少时间加入半径决定的,现在改变下画笔的style:

paint.setStyle(Paint.Style.FILL);

效果:


总感觉有bug,先把第四步实现,有bug再改,

第四步:就是圆的半径大到一定程度会有个颜色的变化,其实就是设置paint的setColor()而已

颜色变化分析图:


@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    for(int i=0;i<raduisList.size();i++){
        int r = raduisList.get(i);
        int alpha = 255-255*(r-bpWidth/2)/(width/2-bpWidth/2);
        paint.setAlpha(alpha);
        canvas.drawCircle(width/2,height/2,r,paint);
    }
    canvas.drawBitmap(bitmap,width/2-bpWidth/2,height/2-bpHeight/2,null);//千万要注意这个bitmap要绘制在圆的后面 不然会被圆盖住
}
上面是根据半径的变化设置paint的颜色不断的进行变化,

效果:


ok,基本实现了