自定义View绘制--波纹扩散动画

      波纹效果需要同时绘制多个同心圆,而且这些圆 不是页面内的元素,未触发之前不需要显示。如果用属性动画实现,至少需要在 xml 布局文件中添加多个 ImageView 画圆,效率低还不易复用。

      WaveView分析
使用自定义View绘制动画的主要思路是这样的:
分解动画成帧,考虑如何在 onDraw 中绘制每一帧提取出绘制所需参数,分为随时间变化和不可变两种,不可变参数可以暴露去(setter方法/attribute设置),总结随时间变化的参数变化规律,实现时间轴按需求提供出播放,暂停,停止,重置等等方法

按照这个思路,一步步实现一下 WaveView吧。
第一步:
在onDraw中画圆需要用到 canvas.drawCircle 方法,四个参数:圆心x、y坐标,半径和Paint。WaveView 绘制的每一帧都是圆,区别是圆的半径,数量,透明度。还需要设置圆的最小半径和最大半径,以及扩散的时候两个圆的半径差。

每次绘制只需要把所有的圆画出来。

第二步:

扩散过程中随时间变化的参数只有半径和透明度。圆的数量,最小最大半径和半径差则是不可变参数。

第三步:

计算出圆心和中心图片的距离

以上是画波纹的全部思路,最后附上全部代码

public class HankView extends View {
    /**
     * 扩散圆圈颜色
     */
    private int mColor = getResources().getColor(R.color.colorAccent);
    /**
     * 圆圈中心颜色
     */
    private int mCoreColor = getResources().getColor(R.color.colorPrimary);
    /**
     * 圆圈中心图片
     */
    private Bitmap mBitmap;
    /**
     * 中心圆半径
     */
    private float mCoreRadius = 50;
    /**
     * 扩散圆宽度
     */
    private int mDiffuseWidth = 3;
    /**
     * 最大宽度
     */
    private Integer mMaxWidth = 255;
    /**
     * 是否正在扩散中
     */
    private boolean mIsDiffuse = false;
    // 透明度集合
    private List mAlphas = new ArrayList<>();
    // 扩散圆半径集合
    private List mWidths = new ArrayList<>();
    private Paint mPaint;
    private OnClickListener mOnClickListener;
    private String TAG = "DiffuseView";
    private int mMarginBottom;    // 麦克风图片距离底边距


    public DiffuseView(Context context) {
        this(context, null);
    }


    public DiffuseView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }


    public DiffuseView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();


        TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.DiffuseView);
        mColor = a.getColor(R.styleable.DiffuseView_diffuse_color, mColor);
        mCoreColor = a.getColor(R.styleable.DiffuseView_diffuse_coreColor, mCoreColor);
        mCoreRadius = a.getFloat(R.styleable.DiffuseView_diffuse_coreRadius, mCoreRadius);
        mDiffuseWidth = a.getInt(R.styleable.DiffuseView_diffuse_width, mDiffuseWidth);
        mMaxWidth = a.getInt(R.styleable.DiffuseView_diffuse_maxWidth, mMaxWidth);
        int imageId = a.getResourceId(R.styleable.DiffuseView_diffuse_coreImage, -1);
        if (imageId != -1) {
            mBitmap = BitmapFactory.decodeResource(getResources(), imageId);
        }
        mMarginBottom = a.getDimensionPixelOffset(R.styleable.DiffuseView_margin_bottom, 12);
        a.recycle();
    }


    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mAlphas.add(255);
        mWidths.add(0);
    }


    /**
     * 开始扩散
     */
    public void start() {
        mIsDiffuse = true;
        invalidate();
    }


    /**
     * 停止扩散
     */
    public void stop() {
        mIsDiffuse = false;
        mWidths.clear();
        mAlphas.clear();
        mAlphas.add(255);
        mWidths.add(0);
        invalidate();
    }


    /**
     * 是否扩散中
     */
    public boolean isDiffuse() {
        return mIsDiffuse;
    }


    /**
     * 设置扩散圆颜色
     */
    public void setColor(int colorId) {
        mColor = colorId;
    }


    /**
     * 设置中心圆颜色
     */
    public void setCoreColor(int colorId) {
        mCoreColor = colorId;
    }


    /**
     * 设置中心圆图片
     */
    public void setCoreImage(int imageId) {
        mBitmap = BitmapFactory.decodeResource(getResources(), imageId);
    }


    /**
     * 设置中心圆半径
     */
    public void setCoreRadius(int radius) {
        mCoreRadius = radius;
    }


    /**
     * 设置扩散圆宽度(值越小宽度越大)
     */
    public void setDiffuseWidth(int width) {
        mDiffuseWidth = width;
    }


    /**
     * 设置最大宽度
     */
    public void setMaxWidth(int maxWidth) {
        mMaxWidth = maxWidth;
    }


    @Override
    public void setOnClickListener(@Nullable OnClickListener onClickListener) {
        mOnClickListener = onClickListener;
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                float downX = event.getX();
                float downY = event.getY();
                if (downX >= (getWidth() / 2 - mBitmap.getWidth() / 2) &&    // 左
                        downX <= (getWidth() / 2 + mBitmap.getWidth() / 2) &&    // 右
                        downY >= (getHeight() - mBitmap.getHeight() - mMarginBottom) &&    // 上
                        downY <= (getHeight() - mMarginBottom)) {    // 下
                    // 只有在图片范围内才具备点击响应
                    mOnClickListener.onClick(this);
                }
                break;
        }
        return super.dispatchTouchEvent(event);
    }


    @Override
    public void invalidate() {
        if (hasWindowFocus()) {
            super.invalidate();
        }
    }


    @Override
    public void onDraw(Canvas canvas) {
        // 绘制扩散圆
        mPaint.setColor(mColor);
        for (int i = 0; i < mAlphas.size(); i++) {
            // 设置透明度
            Integer alpha = mAlphas.get(i);
            mPaint.setAlpha(alpha);
            // 绘制扩散圆
            Integer width = mWidths.get(i);
            canvas.drawCircle(getWidth() / 2, getHeight() - mMarginBottom - mBitmap.getHeight() / 2, mCoreRadius + width, mPaint);


            if (alpha > 0 && width < mMaxWidth) {
                mAlphas.set(i, alpha - 1);
                mWidths.set(i, width + 1);
            }
        }
        // 判断当扩散圆扩散到指定宽度时添加新扩散圆
        if (mWidths.get(mWidths.size() - 1) == mMaxWidth / mDiffuseWidth) {
            mAlphas.add(255);
            mWidths.add(0);
        }
        // 超过10个扩散圆,删除最外层
        if (mWidths.size() >= 10) {
            mWidths.remove(0);
            mAlphas.remove(0);
        }


        // 绘制中心圆及图片
        mPaint.setAlpha(255);
        mPaint.setColor(mCoreColor);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, mCoreRadius, mPaint);


        if (mBitmap != null) {
            canvas.drawBitmap(mBitmap, getWidth() / 2 - mBitmap.getWidth() / 2
                    , getHeight() - mBitmap.getHeight() - mMarginBottom, mPaint);
        }


        if (mIsDiffuse) {
            invalidate();
        }
    }
}


 



 

你可能感兴趣的:(自定义View绘制--波纹扩散动画)