1. 程式人生 > >自定義View實戰之漸變,可撥動,帶動畫圓環控制元件實現

自定義View實戰之漸變,可撥動,帶動畫圓環控制元件實現

效果圖預覽
這裡寫圖片描述

1. 分析

1. 繪製中間數字
2. 繪製帶刻度的圓環  考慮分成若干等份
3. 繪製漸變圓環 需要用到漸變相關屬性
4. 動畫處理的同時需要考慮時時計算角度
5. 圓環開關控制按鈕的波動範圍處理

2. 實現原理

1. 繪製文字這個簡單
2. 繪製刻度圓環 分等份 用canvas.drawArc畫若干圓環
3. 繪製漸變圓環,需要用到Paint的setShader方法設定漸變顏色
4. 屬性動畫處理

3. 初始化一些東西 初始化一般我放在onSizeChanged方法中

 @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);

    mCenterX = w / 2;
    mCenterY = h / 2;

    int[] colors = {Color.RED,Color.GREEN,Color.YELLOW};
    mSweepGradient = new SweepGradient(mCenterX,mCenterY, colors,null);

    mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ring_dot);
    mBitmap = conversionBitmap(mBitmap,dp2px(30),dp2px(30));
    mMinValidateTouchArcRadius = (int) (mCircleRadius - mBitmap.getWidth() / 2 * 1.5);
    mMaxValidateTouchArcRadius = (int) mCircleRadius;
}

4. 繪製中間數字和帶刻度線的圓環

 /**
 * 畫帶刻度線的圓環
 */
private void drawScaleRing(Canvas canvas) {
    float margin = mRingRadius - dp20;
    float left = mCenterX - margin;
    float top = mCenterY - margin;
    float right = left + 2 * margin;
    float bottom = top + 2 * margin;
    mScaleReacF.set(left,top,right,bottom);
    // 等分成360 / 6 = 60份 保證間距一樣的
    for (int i = 0; i < sweepAngle / 6; i++) {
        canvas.drawArc(mScaleReacF, startAngle + i * 6, 1f, false, mScaleRingPaint);
    }
}

/**
 * 畫中間的數字
 */
private void drawMidNum(Canvas canvas) {
    String value = String.valueOf(mStepNum);
    //測量文字的寬高
    mTextPaint.getTextBounds(value, 0, value.length(), mTextRect);
    int width = mTextRect.width();//文字寬
    int height = mTextRect.height();//文字高
    canvas.drawText(value, mCenterX - width / 2, mCenterY + height / 2, mTextPaint);
}

5. 繪製帶漸變色的進度圓環

 /**
 * 畫進度圓環
 */
private void drawRingProgress(Canvas canvas) {
    canvas.save();
    //canvas.rotate(-90);
    float left = mCenterX - mRingRadius;
    float top = mCenterY - mRingRadius;
    float right = left + 2 * mRingRadius;
    float bottom = top + 2 * mRingRadius;
    mProgressReacF.set(left,top,right,bottom);
    //設定漸變顏色
    mProgressPaint.setShader(mSweepGradient);
    float sweepAngle = mCurrentAngle;
    //繪製圓環 startAngle開始角度 sweepAngle掃瞄的角度
    canvas.drawArc(mProgressReacF, startAngle, sweepAngle, false, mProgressPaint);
    canvas.restore();
}

6. 進度條動畫和數字處理

//載入進度條動畫
public void setProgressAnimation(float last, float currentProgress, long duration) {
    mCurrentAngle = (currentProgress / mMaxProgress) * sweepAngle;
    ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, mCurrentAngle);
    progressAnimator.setDuration(duration);
    progressAnimator.setTarget(mCurrentAngle);
    progressAnimator.setInterpolator(new AccelerateInterpolator());
    progressAnimator.start();
    progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            /**每次要繪製的圓弧角度**/
            mCurrentAngle = (float) animation.getAnimatedValue();
            //根據當前進度和總進度計算當前步數
            mStepNum = (int) (mCurrentAngle / sweepAngle * mMaxStepNum);
            postInvalidate();
        }
    });
}

7. 觸控事件處理

@Override
public boolean onTouchEvent(MotionEvent event) {
    int x = (int) event.getX();
    int y = (int) event.getY();
    if (mIsShowControlIcon && (event.getAction() == MotionEvent.ACTION_MOVE || isTouchArc(x, y))) {
        // 通過當前觸控點搞到cos角度值
        float cos = computeCos(x, y);
        // 通過反三角函式獲得角度值
        double angle;
        if (x < mCenterX) { // 滑動超過180度
            angle = Math.PI * RADIAN + Math.acos(cos) * RADIAN;
        } else { // 沒有超過180度
            angle = Math.PI * RADIAN - Math.acos(cos) * RADIAN;
        }
        if (mCurrentAngle > 270 && angle < 90) {
            mCurrentAngle = 360;
            cos = -1;
        } else if (mCurrentAngle < 90 && angle > 270) {
            mCurrentAngle = 0;
            cos = -1;
        } else {
            mCurrentAngle = (float) angle;
        }
        mCurrentProgress = getSelectedValue();
        invalidate();
        return true;
    } else {
        return super.onTouchEvent(event);
    }
}

private int getSelectedValue() {
    return Math.round(mMaxProgress * (mCurrentAngle / sweepAngle));
}

/**
 * 按下時判斷按下的點是否按在圓邊範圍內
 * @param x
 * @param y
 */
private boolean isTouchArc(int x, int y) {
    double d = getTouchRadius(x, y);
    return d >= mMinValidateTouchArcRadius && d <= mMaxValidateTouchArcRadius;
}

/**
 * 計算某點到圓點的距離
 * @param x
 * @param y
 */
private double getTouchRadius(int x, int y) {
    int cx = x - mCenterX;
    int cy = y - mCenterY;
    return Math.hypot(cx, cy);
}

/**
 * 拿到傾斜的cos值
 */
private float computeCos(float x, float y) {
    float width = x - mCenterX;
    float height = y - mCenterY;
    float slope = (float) Math.sqrt(width * width + height * height);
    return height / slope;
}

7. 專案原始碼下載

後面統一提供原始碼下載連結

8. 聯絡方式

QQ:1509815887