美文网首页Android自定义ViewAndroid 自定义viewView
android自定义View——圆形波纹扫描效果

android自定义View——圆形波纹扫描效果

作者: Vonelone | 来源:发表于2017-06-16 19:22 被阅读350次

    蓝牙项目,考虑到后面可能会用到这个扫描的效果,所以参照大神写好的控件,增加了自己需要使用的接口。也顺便巩固一下自定义view中各种零碎的知识点。

    需要的效果图

    先放一个效果图,点击中心图片开始动画,再次点击结束动画:


    先来思路:

    可以看到,这个动画是由圆和图片构成,中心图片画出来,然后根据中心图片的大小确定创建波纹时的半径,波纹的最大半径为当前view的宽高较小的。
    

     动画部分,主要使用runable延时 + ValueAnimator 实现波纹效果; 然后整体旋转。
    

    代码来说话。

    • attrs中定义好需要在xml中设置的变量
    <attr name="color" format="color"/>
    <attr name="image" format="reference"/>
    
    <declare-styleable name="WaveCircleView">
        <attr name="color"/>
        <attr name="image"/>
        <attr name="stroke" format="boolean"/>
        <attr name="waveCreateSpeed" format="integer"/>
        <attr name="duration" format="integer"/>
    </declare-styleable>
    
    • 部分成员变量注释,方便代码阅读
    //波纹生成时的半径
        private float mWaveRadiusMin;
        //波纹消失前的半径
        private float mWaveRadiusMax;
        //每条波纹持续时间
        private long mWaveDuration;
        //波纹生成速度
        private long mWaveCreatedSpeed;
        private Paint mPaint;
        //画笔是否为stroke模式(即线条)
        private boolean stroke = false;
        //中间图标画笔
        private Paint mCenterBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //中间图标区域
        private Rect mCenterBitmapArea = new Rect();
        //波纹颜色
        private int mWaveColor;
        //波纹动画效果
        private Interpolator mInterpolator = new AccelerateInterpolator();
        //所有的水波纹
        private List<ValueAnimator> mAnimatorList = new ArrayList<>();
        //是否开启水波纹
        private boolean mIsRuning = false;
        //是否点击了中间图标
        private boolean mIsCenterClick = false;
        //中间的图标
        private Bitmap mCenterBitmap;
        //中间的圆形图标
        private Bitmap mCenterCircleBitmap;
        //旋转动画
        private Animation operatingAnim;
    
    • 构造方法
    public WaveCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, 
                                     R.styleable.WaveCircleView, 0, defStyleAttr);
            for (int i = 0; i < typedArray.length(); i++) {
                int attr = typedArray.getIndex(i);
                switch (attr) {
                    case R.styleable.WaveCircleView_color:
                        mWaveColor = typedArray.getColor(attr, Color.BLUE);
                        break;
                    case R.styleable.WaveCircleView_image:
                        mCenterBitmap = BitmapFactory.decodeResource(getResources(), 
                                     typedArray.getResourceId(attr, R.mipmap.translate));
                        break;
                    case R.styleable.WaveCircleView_duration:
                        mWaveDuration = typedArray.getInteger(attr, 3000);
                        break;
                    case R.styleable.WaveCircleView_waveCreateSpeed:
                        mWaveCreatedSpeed = typedArray.getInteger(attr, 1000);
                        break;
                    case R.styleable.WaveCircleView_stroke:
                        stroke = typedArray.getBoolean(attr, false);
                        break;
                }
            }
            typedArray.recycle();
    
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setStrokeWidth(3);
            mPaint.setColor(mWaveColor);
            mPaint.setDither(true);
            if (stroke)//如果xml中设置为false,就把画笔属性设置为Stroke,最后的效果是线条
                mPaint.setStyle(Paint.Style.STROKE);
            else//填充效果
                mPaint.setStyle(Paint.Style.FILL);
            if (mCenterBitmap == null) {
                mCenterBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.translate);
                mWaveRadiusMin = Math.min(mCenterBitmap.getWidth(), mCenterBitmap.getHeight()) / 2;
            }
            mWaveRadiusMin = Math.min(mCenterBitmap.getWidth(), mCenterBitmap.getHeight()) / 2;
        }
    
    
    • onDraw()中 ,先把中心图片渲染到画布
            if (mCenterCircleBitmap == null) {
                mCenterCircleBitmap = createCircleImage(mCenterBitmap, mCenterBitmap.getWidth());
            }
            canvas.drawBitmap(mCenterCircleBitmap, null, mCenterBitmapArea, mCenterBitmapPaint);
    
        private Bitmap createCircleImage(Bitmap source, int min) {
            final Paint paint = new Paint();
            paint.setAntiAlias(true);
            Bitmap target = Bitmap.createBitmap(min, min, Bitmap.Config.ARGB_8888);
            //产生一个同样大小的画布
            Canvas canvas = new Canvas(target);
            //首先绘制圆形
            canvas.drawCircle(min / 2, min / 2, min / 2, paint);
            //使用SRC_IN
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            //绘制图片
            canvas.drawBitmap(source, 0, 0, paint);
            return target;
        }
    
    • 波纹效果

    使用runable实现循环更新UI

    private Runnable mWaveRunable = new Runnable() {
            @Override
            public void run() {
                if (mIsRuning) {
                    createWaveAnimator();
                    invalidate();
                    //延时循环
                    postDelayed(mWaveRunable, mWaveCreatedSpeed);
                }
            }
        };
    
    private ValueAnimator createWaveAnimator() {
            final ValueAnimator mWaveAnimator = new ValueAnimator();
            mWaveAnimator.setFloatValues(mWaveRadiusMin, mWaveRadiusMax);
            mWaveAnimator.setDuration(mWaveDuration);
            mWaveAnimator.setRepeatCount(0);
            mWaveAnimator.setInterpolator(mInterpolator);
            mWaveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                }
            });
            //将生成的ValueAnimator存储在list里
            mAnimatorList.add(mWaveAnimator);
            //开始动画
            mWaveAnimator.start();
            return mWaveAnimator;
        }
    

    onDraw()中绘制波纹,根据动画集合中存储的AnimatedValue 改变画笔透明度 和 canvas 时圆的radius,完成扩散渐隐效果

    Iterator<ValueAnimator> iterator = mAnimatorList.iterator();
            while (iterator.hasNext()) {
                ValueAnimator valueAnimator = iterator.next();
    //            Log.e("AnimatedValue",(float)valueAnimator.getAnimatedValue() + "mWaveRadiusMax:" + mWaveRadiusMax);
                if (!valueAnimator.getAnimatedValue().equals(mWaveRadiusMax)) {
                    //设置透明度
                    mPaint.setAlpha(getAlpha((Float) valueAnimator.getAnimatedValue()));
                    //画水波纹
                    canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, (Float) valueAnimator.getAnimatedValue(), mPaint);
    
                } else {
                    valueAnimator.cancel();
                    iterator.remove();
                }
            }
    

    getAlpha()方法:

    private int getAlpha(float mRadius) {
            int alpha = 1;
            if (mWaveRadiusMax > 0) {
                alpha = (int) ((1 - (mRadius - mWaveRadiusMin) / (mWaveRadiusMax - mWaveRadiusMin)) * 255);
            }
            return alpha;
        }
    

    最后,onTouchEvent()实现点击中心控制动画开关

    @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    //判断是否点击在中心图片范围内
                    mIsCenterClick = false;
                     //mCenterBitmapArea 方法在onSizeChanged()中初始化
                    if (mCenterBitmapArea.contains((int) event.getX(), (int) event.getY())) {
                        mIsCenterClick = true;
                    }
                    break;
                case MotionEvent.ACTION_CANCEL:
                    break;
                case MotionEvent.ACTION_MOVE:
                    break;
                case MotionEvent.ACTION_UP:
                    if (mIsCenterClick && !mIsRuning) {
    
                        //当点击了按钮,启动水波纹
                        start();
    
                    } else {
                        stop();
    
                    }
                    break;
            }
            return true;
        }
    
    @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            mWaveRadiusMax = Math.min(w, h) / 2;
            //计算中间图标区域
            mCenterBitmapArea.set((w - mCenterBitmap.getWidth()) / 2, (h - mCenterBitmap.getHeight()) / 2
                    , (w + mCenterBitmap.getWidth()) / 2, (h + mCenterBitmap.getHeight()) / 2);
        }
    
    public void start() {
            if (!mIsRuning) {
                mIsRuning = true;
                mWaveRunable.run();
    
                //旋转效果
                operatingAnim = AnimationUtils.loadAnimation(getContext(), R.anim.roa);
                LinearInterpolator lin = new LinearInterpolator();
                operatingAnim.setInterpolator(lin);
                operatingAnim.setDuration(mWaveDuration);
                startAnimation(operatingAnim);
            }
        }
    
    • anim中定义的旋转动画以及使用
    <set xmlns:android="http://schemas.android.com/apk/res/android">
        <rotate
            android:fromDegrees="0"
            android:toDegrees="360"
            android:repeatCount="-1"
            android:pivotX="50%"
            android:pivotY="50%" />
    </set>
    

    最后 , xml中测试

    <com.ec.vone.view.WaveCircleView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:color="@color/colorAccent"
            app:stroke="true"
            app:duration="3000"
            app:waveCreateSpeed="500"
            app:image="@mipmap/bluetooth"
            />
    

    由于没有写onMeasure(),所以wrap_content占据父控件的全部大小
    效果如下:

    当然,部分需要用到的属性,也通过getter 和setter暴露出去,方便java代码中灵活控制。

    源码点击查看

    。。。。以后应该能用上

    代码中可能还有笔者未发现的bug和暂时不想解决的bug。。。欢迎交流~~~

    相关文章

      网友评论

      本文标题:android自定义View——圆形波纹扫描效果

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