美文网首页
自定义View-仿波浪扩散效果

自定义View-仿波浪扩散效果

作者: Alden_Lei | 来源:发表于2018-12-08 17:14 被阅读0次

    老规矩,先看效果图吧


    device-2018-12-08-162806.gif

    View效果是在巨人肩膀上改进的,源自:https://blog.csdn.net/q1242027878/article/details/73832074?utm_source=blogxgwz7

    说一下修改的地方:
    1.新增圆心的图片绘制,图片设置属性app:imageSrc="";
    2.修复点击事件无效的bug,原因在于其checkIsInCircle()方法判断是否点击在此view中心圆圈内逻辑不对;
    3.修复原view画波浪扩散圈第一个会最后一个会重叠问题(仔细观察原view的动态图会发现某个圈重叠显得更粗),原因在与其计算绘制间隔位置有错
    4.新增从中间实心圈位置边缘开始绘制扩散波纹,并且在第一个和第二个波浪间实现淡出效果,否则看起来第一个波浪出现比较生硬

    下面开始贴代码:

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.util.AttributeSet;
    import android.view.MotionEvent;
    import android.view.View;
    
    import com.theaty.wavetest.R;
    
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class WaveView extends View {
    
        private static final String TAG = "ICE";
        private Paint mPaint;
        private int mRadius;//里面圆圈的半径
        private Context mContext;
        private int mWidth;//控件的宽度
        /**
         * 设置波浪圆圈的宽度
         */
        private int mStrokeWidth = 2;
        /**
         * 中心圆填充颜色
         */
        private int mFillColor;
        /**
         * 最中间的圆的边框颜色
         */
        private int mCircleStrokeColor;
        /**
         * 字体大小
         */
        private int mTextSize = 30;
        
        /**
         * 中间图片padding值大小,即中心图片距离四周的距离
         */
        private int mImagePadding = 50;
        /**
         * 中间图片的id
         */
        private int mImageSrc = 0;
        /**
         * 多个扩散波浪间隙大小
         */
        private int gapSize;
        /**
         * 第一个扩散波浪的半径
         */
        private int firstRadius;
        /**
         * 控制生成扩散波浪的数量
         */
        private int numberOfCircle = 4;
        /**
         * 扩散波浪颜色
         */
        private int mLineColor;
      /**
         *绘制的文字
         */
        private String mText;
        /**
         * 中间文字颜色
         */
        private int mTextColor;
        private Paint mTextPaint;
        private boolean isFirstTime = true;
        //点击事件监听器
        private float mDownX, mDownY;
        private OnClickListener mClickListener;
    
    /**
    *是否绘制图片
    **/
        private boolean isShowBitmap = true;
    
    
        /**
         * 设置波浪扩散的速度,单位毫秒,值越小扩散越快
         */
        private int period = 100;
    
        public WaveView(Context context) {
            this(context, null);
        }
    
        public WaveView(Context context, AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
            mText = ta.getString(R.styleable.WaveView_text);
            if (mText == null) mText = "";
            mTextColor = ta.getColor(R.styleable.WaveView_textColor, Color.BLACK);
            mTextSize = ta.getDimensionPixelSize(R.styleable.WaveView_textSize, mTextSize);
            mFillColor = ta.getColor(R.styleable.WaveView_fillColor, Color.WHITE);
            mLineColor = ta.getColor(R.styleable.WaveView_waveColor, Color.BLACK);
            mCircleStrokeColor = ta.getColor(R.styleable.WaveView_strokeColor, mFillColor);
            mImagePadding = ta.getDimensionPixelSize(R.styleable.WaveView_imagePadding, mImagePadding);
            mImageSrc = ta.getResourceId(R.styleable.WaveView_imageSrc, R.drawable.ic_scan);
            ta.recycle();  //注意回收
    
            init(context);
        }
    
        private void init(Context context) {
            mContext = context;
    
            mWidth = dip2px(50);
            mStrokeWidth = 2;
    
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setStyle(Paint.Style.STROKE);
    
            mTextPaint = new Paint();
            mTextPaint.setAntiAlias(true);
            mTextPaint.setStrokeCap(Paint.Cap.ROUND);
            mTextPaint.setStyle(Paint.Style.STROKE);
            mTextPaint.setTextSize(mTextSize);
            mTextPaint.setColor(mTextColor);
            numberOfCircle = 4;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int height = MeasureSpec.getSize(heightMeasureSpec);
            switch (widthMode) {
                case MeasureSpec.EXACTLY:
                    //match_parent 或者 精确的数值
                    mWidth = width;
                    break;
            }
            switch (heightMode) {
                case MeasureSpec.EXACTLY:
                    mWidth = Math.min(mWidth, height);
                    break;
            }
            mRadius = mWidth / numberOfCircle;
            gapSize = (mWidth / 2 - mRadius) / numberOfCircle;
            firstRadius = mRadius;// + gapSize;
            setMeasuredDimension(mWidth, mWidth);
    
        }
    
        //将图片按比例缩放
        private Bitmap scaleBitmap(int total, Bitmap bitmap) {
            int width = total;
            //一定要强转成float 不然有可能由于精度不够 出现 scale为0 的错误
            float scale = (float) width / (float) bitmap.getWidth();
            Matrix matrix = new Matrix();
            matrix.postScale(scale, scale);
            return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.translate(mWidth / 2, mWidth / 2);//平移
    
            //画中间的实体圆
            mPaint.setAlpha(255);
            mPaint.setColor(mFillColor);
            mPaint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(0, 0, mRadius, mPaint);
    
            //画圆的边(这是中间实体圆圈的描边,不需要可以去掉此绘制步骤)
            mPaint.setStrokeWidth(mStrokeWidth);
            mPaint.setColor(mCircleStrokeColor);
            mPaint.setStyle(Paint.Style.STROKE);
            canvas.drawCircle(0, 0, mRadius, mPaint);
    
            if (!isShowBitmap) {
                //画文字
                Rect rect = new Rect();//文字的区域
                mTextPaint.getTextBounds(mText, 0, mText.length(), rect);
                int height = rect.height();
                int width = rect.width();
                canvas.drawText(mText, -width / 2, height / 2, mTextPaint);
            } else {
                //画中间的图片
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(), mImageSrc);
                //这样缩放仅仅是确认中间的图片的宽高大小位置,如果再使用这图片会失真,所以画的时候还是用原图
                bitmap = scaleBitmap(mRadius - mImagePadding, bitmap);
                int bWidth = bitmap.getWidth();
                int bHeight = bitmap.getHeight();
                canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), mImageSrc), null, new RectF(-bWidth, -bHeight, bWidth, bHeight), mPaint);
            }
    
    
            //画周围的波浪
            firstRadius += 3;//每次刷新半径增加3像素
            firstRadius %= (mWidth / 2);//控制在控件的范围中
            if (firstRadius < mRadius) isFirstTime = false;
            firstRadius = checkRadius(firstRadius);//检查半径的范围
            mPaint.setColor(mLineColor);
            mPaint.setStyle(Paint.Style.STROKE);
    
    
            //画波浪
            for (int i = 0; i < numberOfCircle; i++) {
                //控制外部最大圆半径只有最大宽度的一半
                int radius = (firstRadius + i * gapSize) % (mWidth / 2);
                if (isFirstTime && radius > firstRadius) {
                    continue;
                }
                //检查半径的范围
                radius = checkRadius(radius);
                //用半径来计算透明度  半径越大  越透明
                double x = 1.0;
                if (radius > mRadius + gapSize) {
                    //后面圆的透明度是淡出
                    x = (mWidth / 2 - radius) * 1.0 / (mWidth / 2 - mRadius);
                } else {
                    //第一个圆的透明度是淡入效果
                    x = (radius - mRadius * 1.0) / gapSize;
                }
                //255*0.8表示最白的时候都是80%
                mPaint.setAlpha((int) (255 * 0.8 * x));
                canvas.drawCircle(0, 0, radius, mPaint);
    
    
            }
        }
    
        //检查波浪的半径  如果小于圆圈,那么加上圆圈的半径
        private int checkRadius(int radius) {
            if (radius < mRadius) {
                return radius + mRadius;// + gapSize;
            }
            return radius;
        }
    
        public int dip2px(float dpValue) {
            final float scale = mContext.getResources().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
        }
    
        public void startAnimation() {
    
            Timer timer = new Timer();
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    postInvalidate();
                }
            }, 0, period);
    
        }
        
    
        /**
         * 设置最中间的圆圈的颜色
         *
         * @param mCircleStrokeColor
         */
        public void setmCircleStrokeColor(int mCircleStrokeColor) {
            this.mCircleStrokeColor = mCircleStrokeColor;
        }
    
        @Override
        public void setOnClickListener(OnClickListener l) {
            mClickListener = l;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
    
                case MotionEvent.ACTION_DOWN:
                    mDownX = event.getX();
                    mDownY = event.getY();
                    return checkIsInCircle((int) mDownX, (int) mDownY);
    
                case MotionEvent.ACTION_UP:
                    int upX = (int) event.getX(), upY = (int) event.getY();
                    if (checkIsInCircle(upX, upY) && mClickListener != null) {
                        mClickListener.onClick(this);
                    }
                    break;
    
            }
            return true;
        }
    
        /**
         * 检查点x,y是否落在圆圈内
         *
         * @param x
         * @param y
         * @return
         */
        private boolean checkIsInCircle(int x, int y) {
    
            int centerX = (getRight() - getLeft()) / 2;
            int centerY = (getBottom() - getTop()) / 2;
            boolean b = Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2) < Math.pow(mRadius, 2);
            return b;
        }
    
    
        public void setText(String text) {
            this.mText = text;
            invalidate();
        }
    
        public int getTextColor() {
            return mTextColor;
        }
    
        public void setTextColor(int mTextColor) {
            this.mTextColor = mTextColor;
        }
    
        public String getText() {
            return mText;
        }
    
    
        public int getLineColor() {
            return mLineColor;
        }
    
        public void setLineColor(int mLineColor) {
            this.mLineColor = mLineColor;
        }
    
        public int getGapSize() {
            return gapSize;
        }
    
        public void setGapSize(int gapSize) {
            this.gapSize = gapSize;
        }
    
        public int getFillColor() {
            return mFillColor;
        }
    
        public void setFillColor(int mFillColor) {
            this.mFillColor = mFillColor;
        }
    
        public int getTextSize() {
            return mTextSize;
        }
    
        public void setTextSize(int mTextSize) {
            this.mTextSize = mTextSize;
        }
    
        public int getStrokeWidth() {
            return mStrokeWidth;
        }
    
        public void setStrokeWidth(int mStrokeWidth) {
            this.mStrokeWidth = mStrokeWidth;
        }
    
    
        public int getPeriod() {
            return this.period;
        }
    
        public void setPeriod(int period) {
            this.period = period;
        }
    
        public int getmCircleStrokeColor() {
            return this.mCircleStrokeColor;
        }
    
        public int getmImagePadding() {
            return this.mImagePadding;
        }
    
        public void setmImagePadding(int mImagePadding) {
            this.mImagePadding = mImagePadding;
        }
    }
    

    attrs.xml属性文件:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
            <declare-styleable name="WaveView">
            <attr name="textSize" ></attr>
            <attr name="text"></attr>
            <attr name="textColor"></attr>
            <attr name="fillColor" format="color"/>
            <attr name="waveColor" format="color"/>
            <attr name="strokeColor" format="color"/>
            <attr name="imagePadding" format="dimension"/>
            <attr name="imageSrc" format="dimension"/>
        </declare-styleable>
    </resources>
    

    在activity中的使用:
    布局文件:

     <com.theaty.wavetest.view.WaveView
                android:id="@+id/wave"
                android:layout_width="250dp"
                android:layout_height="250dp"
                app:imagePadding="25dp"
               app:fillColor="#FFF"
                app:strokeColor="@color/colorMain55"
                app:text="点我匹配"
                app:textColor="@color/colorAccent"
                app:textSize="18sp"
                app:imageSrc="@drawable/avatar"
                app:waveColor="@color/colorAccent" />
    

    Activtiy:

      WaveView waveView;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            waveView = findViewById(R.id.wave);
            waveView.setPeriod(120);
            waveView.setTextSize(waveView.dip2px(18));
            waveView.setStrokeWidth(1);
            waveView.setTextColor(getResources().getColor(R.color.colorPrimary));
            waveView.setmCircleStrokeColor(getResources().getColor(R.color.white));
            waveView.setLineColor(getResources().getColor(R.color.white));
            waveView.setFillColor(getResources().getColor(R.color.colorMain55));
            waveView.setGapSize(30);
            waveView.startAnimation();
            waveView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this, "点击了wave", Toast.LENGTH_SHORT).show();
                }
            });
    }
    

    代码理解也不难,有问题可以留言一起交流交流, ^_^

    相关文章

      网友评论

          本文标题:自定义View-仿波浪扩散效果

          本文链接:https://www.haomeiwen.com/subject/yjyfhqtx.html