自定义View之弹性滑动的LockView(下)

上篇文章我们实现了LockView的弹性滑动效果,那么本篇文章我们承接上篇来实现LockView的中心CircleWaveView。当然,LockView上下的两个圆也是自定义View,但是由于过于简单,我们就不再多费口舌了。
那么对于中心View的实现,主要有一下几点:
- 一.定义CircleWaveView类。
- 二.绘制CircleWaveView的内容
- 三.滑动、心跳动画以及状态改变动画实现

一.定义CircleWaveView类

1.添加自定义属性(这次补上来)。在attrs.xml文件中添加一下内容:

<declare-styleable name="CircleWaveView">
        <!--选中时的圆点图片-->
        <attr name="wave_color" format="color"/>
        <attr name="wave_text_color" format="color"/>
        <attr name="wave_text_size" format="dimension"/>
        <attr name="wave_text_str" format="string"/>
</declare-styleable>

2.新建CircleWaveView类继承View,重写相应的方法,初始化相关数据并读取自定义属性(都是老套路了,写起来自己都觉得烦哎)。

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


    public CircleWaveView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleWaveView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        if (attrs != null) {
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleWaveView);
            circleColor = typedArray.getColor(R.styleable.CircleWaveView_wave_color, context.getResources().getColor(R.color.red));
            mTextColor = typedArray.getColor(R.styleable.CircleWaveView_wave_text_color, context.getResources().getColor(R.color.white));
            mTextSize = typedArray.getDimension(R.styleable.CircleWaveView_wave_text_size, DensityUtils.dp2px(context, 16));
            mText = typedArray.getString(R.styleable.CircleWaveView_wave_text_str);
            typedArray.recycle();
        }
        init(context);
    }

    private void init(Context context) {
        mContext = context;
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setAntiAlias(true);
        mPaint.setColor(circleColor);
        mPaintText = new Paint();
        mPaintText.setColor(mTextColor);
        mPaintText.setStyle(Paint.Style.FILL);
        mPaintText.setTextAlign(Paint.Align.CENTER);
        mPaintText.setAntiAlias(true);
        dp13 = DensityUtils.dp2px(mContext, 13);
        mScroller = new Scroller(context);
        bounds = new Rect();
        arrowUp = BitmapFactory.decodeResource(getResources(),
                R.drawable.arrow_up);
        arrowDown = BitmapFactory.decodeResource(getResources(),
                R.drawable.arrow_down);
    }

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mHeight = getHeight();
        mWidth = getWidth();
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mPieCenterX = mWidth / 2;
        mPieCenterY = mHeight / 2;
    }

二.绘制CircleWaveView的内容
重写onDraw方法,在onDraw里边绘制内容。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCircle(canvas);
        drawText(canvas);
        drawTriangle(canvas);
    }
    //  绘制圆
    private void drawCircle(Canvas canvas) {
        mPaint.setColor(circleColor);
        int verticalCenter = getHeight() / 2;
        int horizontalCenter = getWidth() / 2;
        int mRadius = Math.min(verticalCenter, horizontalCenter) - Math.min(verticalCenter, horizontalCenter) / 5;
        radius = Math.min(verticalCenter, horizontalCenter) - Math.min(verticalCenter, horizontalCenter) / 5;
        if (transforming) {
            mPaint.setColor(getResources().getColor(R.color.green));
            canvas.drawCircle(mPieCenterX, mPieCenterY, mRadius, mPaint);
            mRadius = isLock ? transformDelta : mRadius - transformDelta;
            mPaint.setColor(getResources().getColor(R.color.red));
            canvas.drawCircle(mPieCenterX, mPieCenterY, mRadius, mPaint);
        } else {
            mRadius = mRadius - waveDelta;
            if (!isBluetoothConnect) {
                if (isNoNetData) {
                    mPaint.setColor(getColor(R.color.gray));
                } else
                    mPaint.setColor(isLock ? getColor(R.color.redLight) : getColor(R.color.greenLight));
            } else {
                if (isLockPrepared) {
                    mPaint.setColor(getColor(R.color.redDark));
                } else if (isUnLockPrePared) {
                    mPaint.setColor(getColor(R.color.greenDark));
                } else {
                    mPaint.setColor(isLock ? getColor(R.color.red) : getColor(R.color.green));
                }
            }
            canvas.drawCircle(mPieCenterX, mPieCenterY, mRadius, mPaint);
        }
    }

   //  绘制圆中两个三角
    private void drawTriangle(Canvas canvas) {
        int left = (mWidth - arrowUp.getWidth()) / 2;
        canvas.drawBitmap(arrowUp, left, mHeight / 2 - radius + dp13, mPaint);
        canvas.drawBitmap(arrowDown, left, mHeight / 2 + radius - dp13 - arrowDown.getHeight(), mPaint);
        /*int radius = Math.min(mHeight, mWidth) / 2 - Math.min(mHeight, mWidth) / 8;
        mPaintTrangel.setStyle(Paint.Style.FILL);
        mPaintTrangel.setShadowLayer(4, 0, 3, Color.GRAY);
        //  三角形顶点到圆边的距离
        int h0 = DensityUtils.dp2px(mContext, 10);
        //  三角形高
        int h1 = DensityUtils.dp2px(mContext, 12);
        //  三角形底边长
        int w = DensityUtils.dp2px(mContext, 14);
        mPaintTrangel.setColor(getResources().getColor(R.color.transparent_33));
        mPath.moveTo(mWidth / 2, mHeight / 2 - (radius - h0));
        mPath.lineTo(mWidth / 2 - w, mHeight / 2 - (radius - h1 - h0));
        mPath.lineTo(mWidth / 2 + w, mHeight / 2 - (radius - h1 - h0));
        canvas.drawPath(mPath, mPaintTrangel);
        mPaintTrangel.setShadowLayer(4, 0, -3, Color.GRAY);
        mPath.moveTo(mWidth / 2, mHeight / 2 + (radius - h0));
        mPath.lineTo(mWidth / 2 - w, mHeight / 2 + (radius - h1 - h0));
        mPath.lineTo(mWidth / 2 + w, mHeight / 2 + (radius - h1 - h0));
        canvas.drawPath(mPath, mPaintTrangel);*/
    }


    //  绘制圆中的文字
    private void drawText(Canvas canvas) {
        if (isConnecting) return;
        if (TextUtils.isEmpty(mText)) { //   绘制单行文字
            String text = mContext.getResources().getString(R.string.ble_not_connect);
            canvas.drawText(text, mPieCenterX, getBaseline(text), mPaintText);
            return;
        }
        if (isBluetoothConnect) {   //   绘制单行文字
            canvas.drawText(mText, mPieCenterX, getBaseline(mText), mPaintText);
        } else {    //   绘制两行文字
            String text = mContext.getResources().getString(R.string.ble_not_connect);
            int baseline = getBaseline(text);
            canvas.drawText(text, mPieCenterX, baseline - 30, mPaintText);
            mPaintText.setTextSize(DensityUtils.dp2px(mContext, 12));
            canvas.drawText(mText, mPieCenterX, baseline + 30, mPaintText);
        }
    }

    private int getBaseline(String text) {
        mPaintText.setTextSize(mTextSize);
        mPaintText.getTextBounds(text, 0, text.length(), bounds);
        Paint.FontMetricsInt fontMetricsInt = mPaintText.getFontMetricsInt();
        return (getMeasuredHeight() - fontMetricsInt.bottom + fontMetricsInt.top) / 2
                - fontMetricsInt.top;
    }

三.滑动及心跳动画实现

1.使用Scroller来对该View进行滑动操作,并对LockView提供调用接口。

 public void smoothScroll(int destX, int destY) {
        int scrollY = getScrollY();
        int delta = destY - scrollY;
        mScroller.startScroll(destX, scrollY, 0, delta, 400);
        invalidate();
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

2.心跳动画效果的实现。我们这里使用属性动画,通过ValueAnimator动画来计算缩放比例,根据比例计算圆半径的大小,并通过invalidate()方法不断重绘View,从而达到一个心跳动画的效果。

//  开始心跳动画
public void startWave() {
        if (animator != null && animator.isRunning())
            animator.end();
        animator = ValueAnimator.ofFloat(0f, 1f, 0f);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setRepeatMode(ValueAnimator.RESTART);
        animator.setDuration(600);

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int verticalCenter = getHeight() / 2;
                int horizontalCenter = getWidth() / 2;
                waveDelta = (int) (Math.min(verticalCenter, horizontalCenter) * (float) animation.getAnimatedValue() / 16);
                invalidate();
            }
        });

        animator.start();
    }
 //  停止心跳动画
 public void stopWave() {
        if (animator != null && animator.isRunning())
            animator.end();
    }
  1. 改变锁的状态。这里也是通过ValueAnimator动画来实现一个半径从0扩展到最大或者从最大缩小到0的一个动画。
public void changeLockState(final boolean lock) {
        stopWave();
        if (this.isLock != lock) {
            transforming = true;
            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 0.99f);
            valueAnimator.setDuration(500);
            valueAnimator.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animation) {

                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    transforming = false;
                    isLock = lock;
                    invalidate();
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                    transforming = false;
                }

                @Override
                public void onAnimationRepeat(Animator animation) {

                }
            });
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    int verticalCenter = getHeight() / 2;
                    int horizontalCenter = getWidth() / 2;
                    transformDelta = (int) ((Math.min(verticalCenter, horizontalCenter) - Math.min(verticalCenter, horizontalCenter) / 6)
                            * (float) animation.getAnimatedValue());
                    invalidate();
                }
            });
            valueAnimator.start();
        }
    }

至此,关于LockView的绘制到这里就完全结束了。最后还是放一下源码,需要学习的可以自己取啦!

源码链接

猜你喜欢

转载自blog.csdn.net/qq_20521573/article/details/81047326