天天看点

手势移动 圆形进度条

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);
}
           

继续阅读