天天看點

手勢移動 圓形進度條

Android 手勢移動 圓形進度條

首先明确:在Android中坐标系方向為 向右X軸正方向,向下Y軸正方向。

确定自定義view大小(圓弧所在矩形的大小)

mCRadius: 圓弧寬度半徑

mOval = new RectF(getPaddingLeft() + mCRadius, getPaddingTop() + mCRadius,
            getWidth() - getPaddingRight() - mCRadius,
            getHeight() - getPaddingBottom() - mCRadius);
           

圓形進度條,主要畫圓弧,先畫軌迹弧形,然後是進度弧形。

畫軌迹:360°圓弧,270°(有缺口)的圓弧。

isFilled: 填充模式

if (isFilled) {
        trackPaint.setStyle(Paint.Style.FILL);
    } else {
        trackPaint.setStyle(Paint.Style.STROKE);
    }
    if (circleBroken) {
        canvas.drawArc(mOval, 135, 270, isFilled, trackPaint);
    } else {
        canvas.drawArc(mOval, 90, 360, isFilled, trackPaint);
    }
           

畫進度:偏移進度 * (270°或者360°)/ 100 = 偏移角度

if (isFilled) {
        progressPaint.setStyle(Paint.Style.FILL);
    } else {
        progressPaint.setStyle(Paint.Style.STROKE);
    }
    if (circleBroken) {
        canvas.drawArc(mOval, 135 + mStartProgress * 2.7f, (moveProgress - mStartProgress) * 2.7f, isFilled, progressPaint);
    } else {
        canvas.drawArc(mOval, 270 + mStartProgress * 3.6f, (moveProgress - mStartProgress) * 3.6f, isFilled, progressPaint);
    }
           

中間部分繪制百分比進度值:

/**
 * draw the progress text
 *
 * @param canvas mCanvas
 */
private void drawProgressText(Canvas canvas) {
    if (textVisibility) {
        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize(mProgressTextSize);
        mTextPaint.setColor(mProgressTextColor);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        //    Typeface font = ResourcesCompat.getFont(getContext(),R.font.bahnschrift);
        //   mTextPaint.setTypeface(font);
        String progressText = ((int) moveProgress) + "%";
        float x = (getWidth() + getPaddingLeft() - getPaddingRight()) / 2;
        float y = (getHeight() + getPaddingTop() - getPaddingBottom() - (mTextPaint.descent() + mTextPaint.ascent())) / 2;
        canvas.drawText(progressText, x, y, mTextPaint);
    }
}
           

手勢觸摸移動,根據手指觸摸的坐标,計算與圓心之間的角度,根據

起始角度再計算其偏移角度,最後計算偏移進度。

private void moved(float x,float y){
    int startAngle = circleBroken ? 135 : 270;
    int endAngle = circleBroken ? 270 : 360;
    if (!isEffectiveArea(x,y)){
        return;
    }
    int cx = getWidth() / 2;
    int cy = getHeight() / 2;
    //  y 對邊   x 鄰邊
    double radian = Math.atan2(y - cy,x - cx);
    if (radian < 0) {
        radian = radian + 2 * Math.PI;   // 小于0的弧度值  轉成 正值
    }
    float angle =  Math.round(Math.toDegrees(radian));   // 相對于坐标系的實際角度

    // 90度缺口部分  不進行計算
    if (circleBroken && angle <= 135 && angle >= 45) return;

    float mSweepAngle =  getLastAngle(angle,startAngle);  
     // 加上起始角度的偏移角度
    //通過角度得到xy坐标
    // angleToXY(mSweepAngle);
    //通過角度擷取進度值
    float mProgress = (100 * mSweepAngle / endAngle);
    if (mProgress > 99) mProgress = 100;
    setProgress(mProgress);
}
private float getLastAngle(float angle,int startAngel) {
    //設定起始角度的關鍵代碼,正常情況下Math.round(Math.toDegrees(radian))就可以了。
    float lastAngle;
    if (angle < startAngel) {
        lastAngle = 360 - startAngel + Math.abs(angle);
    } else {
        lastAngle = angle - startAngel;
    }
    return lastAngle;
}

/**
 *  計算其觸摸點是否在圓弧可拖動範圍内
 */
private boolean isEffectiveArea(float x,float y){
    boolean result = false;
    int cx = getWidth() / 2;
    int cy = getHeight() / 2;
    double distance = Math.sqrt(Math.pow(x - cx, 2) + Math.pow(y - cy, 2));
    //弧線外面
    boolean isCircleOuter = distance <= getWidth();
    //弧線裡面,如果想設定裡面範圍都可以拖動,可以 0
    boolean isCircleInner = distance >= (cx - getPaddingLeft() - mCRadius - 40);
    if (isCircleOuter && isCircleInner) {
        result = true;
    }
    return result;
}
           

根據進度計算坐标值,主要用于托快放置位置。

/**
 * 通過角度得到x,y軸,用于托快上使用  // 相對于起始角度的偏移角度
 * @param angle
 */
private void angleToXY(float angle) {
    // 向右為0,向下為90
    float startAngle = circleBroken ? 135 : 270;
    // 計算實際角度
    float total = 360 - startAngle;
    float caculateAngle;   // 實際角度值
    if (angle < total) {
        caculateAngle = angle + startAngle;
    } else {
        caculateAngle = angle - total;
    }
    double radian = Math.toRadians(caculateAngle);  // 計算實際角度的弧度值
    double radius = getWidth() / 2 - getPaddingLeft()- mCRadius;  // 減去預留的部分值  mOval.left  得到半徑斜邊
    double x = getWidth() / 2 + radius * Math.cos(radian);  // 鄰邊 / 斜邊
    double y = getHeight() / 2 + radius * Math.sin(radian);  // 對應邊 / 斜邊
    mThumbLeft = (float) (x - mCRadius);
    mThumbTop = (float) (y - mCRadius);
}

private void progressToXY() {
    // 通過進度值擷取角度
    int endAngle = circleBroken ? 270 : 360;
    float angle = moveProgress / 100 * endAngle;
    angleToXY(angle);
}
           

繼續閱讀