Android自定义圆弧进度条,计算坐标到圆心得连线与x轴(心中要有轴)夹角

 

效果图片已经看到了,圆弧可以根据手的滑动旋转。

下面直接贴代码,有兴趣自己研读一下.不在过多赘述,自己能理解贯通最好。方法可以灵活运用,与多个场景。

 

下面这个方法是关键,以后可以都理解利用:(计算坐标到圆心得连线与x轴(心中要有轴)夹角)

/**
     * 以按钮圆心为坐标圆点,建立坐标系,求出(targetX, targetY)坐标与x轴的夹角
     *
     * @param targetX x坐标
     * @param targetY y坐标
     * @return (targetX, targetY)坐标与x轴的夹角
     */
    private float calcAngle(float targetX, float targetY) {
        float x = targetX - weith / 2;//len/2 圆点x坐标
        float y = targetY - height / 2;//len/2 圆点y坐标
        double radian;

        if (x != 0) {
            float tan = Math.abs(y / x);
            if (x > 0) {
                if (y >= 0) {
                    radian = Math.atan(tan);
                } else {
                    radian = 2 * Math.PI - Math.atan(tan);
                }
            } else {
                if (y >= 0) {
                    radian = Math.PI - Math.atan(tan);
                } else {
                    radian = Math.PI + Math.atan(tan);
                }
            }
        } else {
            if (y > 0) {
                radian = Math.PI / 2;
            } else {
                radian = -Math.PI / 2;
            }
        }
        return (float) ((radian * 180) / Math.PI);
    }

下面的就是完整代码:”



import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;

import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;

import com.iszcc.x5audio.R;
import com.iszcc.x5audio.uitl.LogUtil;


public class RotateView extends View {

    private int weith, height;

    private Bitmap mBitmap;

    private Paint mTextPaint;

    private String text = "100";

    private Paint basePaint;

    private int maxAcgle = 270;//圆弧最大角度

    private float recentAcgle;//当前角度

    private int minRang = 0;//范围
    private int maxRang = 1;//范围
    private int newScale = 0;//保留小数位数


    private int mTextSize = 40;
    private int mTextStrokeWidth = 40;


    private int arcStrokeWidth = 3;
    //内弧粗度
    private int arcWidth = 50;

    private float chazhi = 1;


    private int recentInt;//当前值

    public RotateView(Context context) {
        super(context);
        init(context);
    }

    public RotateView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public RotateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       


        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);

        int heightSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(widthMeasureSpec);

        /*1、UNSPECIFIED
                父控件没有对子控件施加任何约束,子控件可以是任意大小(也就是未指定)
                UNSPECIFIED在源码里处理和EXACTLY一样,当View的宽高值为0的时候或者没有设置
                宽高值的时候,模式为UNSPECIFIED
         *2、EXACTLY
                父控件决定子控件确切的大小,子控件被限定在给定的边界里面,忽略本身的大小
                当设置width为match_parent时,模式为EXACTLY,因为子控件View会占据剩余的父空间,
                所以大小是确定的。
         *3、AT_MOST
         *      子控件最大能够达到的指定大小
         *      当设置wrap_content时,模式为AT_MOST,表示子控件View的大小最多是多少,
         *      这样这个子控件View会根据这个上限来设置自己的尺寸
         */
        if (widthMode == MeasureSpec.EXACTLY) {
            weith = widthSize;
        } else {
            weith = 700;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = 700;
        }


        //应用测量值
        setMeasuredDimension(weith, height);


    }

    private void init(Context context) {

        mBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.mainvol_bg);

        mTextPaint = new Paint();
        mTextPaint.setColor(Color.WHITE);
        mTextPaint.setStrokeWidth(dp2px(mTextStrokeWidth));
        mTextPaint.setTextSize(dp2px(mTextSize));


        basePaint = new Paint();
        basePaint.setAntiAlias(true);
        basePaint.setColor(Color.WHITE);
        basePaint.setStyle(Paint.Style.STROKE);
        basePaint.setStrokeWidth(arcStrokeWidth);
    }


    float downX = 0;
    float downY = 0;

    int resetInt = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:

                downX = event.getX();
                downY = event.getY();

                break;
            case MotionEvent.ACTION_MOVE:


                float angleCha = calcAngle(event.getX(), event.getY()) - calcAngle(downX, downY);
                //Log.e("RotateView:", angleCha + "  " + calcAngle(event.getX(), event.getY()) + "  __________ " + calcAngle(downX, downY));
                downY = event.getY();
                downX = event.getX();

                // 防止越界
                if (angleCha < -300) {
                    angleCha = angleCha + 360;
                } else if (angleCha > 300) {
                    angleCha = angleCha - 360;
                }


                recentAcgle += angleCha;

                if (recentAcgle < 0) {
                    recentAcgle = 0;
                } else if (recentAcgle >= maxAcgle) {
                    recentAcgle = maxAcgle;
                }


                if (recentAcgle == maxAcgle) {
                    resetInt = maxRang;
                } else {
                    resetInt = (int) (recentAcgle / chazhi);
                }


                if (resetInt != recentInt) {

                    recentInt = resetInt;

                    if (listener != null) {
                        listener.onRotateChange(recentInt);

                    }

                }

       /*//保留一位小数的返回值
                BigDecimal b = new BigDecimal(recentAcgle / chazhi);
                recentInt = b.setScale(newScale, BigDecimal.ROUND_HALF_UP).floatValue() + minRang;*/
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                LogUtil.e("MotionEvent.ACTION_UP 00 : "+recentInt);
                if (listener != null) {

                    LogUtil.e("MotionEvent.ACTION_UP : "+recentInt);
                    listener.onStop(recentInt);
                }
               /* float angleChaUp = calcAngle(event.getX(), event.getY()) - calcAngle(downX, downY);

                downY = event.getY();
                downX = event.getX();

                // 防止越界
                if (angleChaUp < -300) {
                    angleChaUp = angleChaUp + 360;
                } else if (angleChaUp > 300) {
                    angleChaUp = angleChaUp - 360;
                }


                recentAcgle += angleChaUp;

                if (recentAcgle < 0) {
                    recentAcgle = 0;
                } else if (recentAcgle > maxAcgle) {
                    recentAcgle = maxAcgle;
                }

                if (recentAcgle == maxAcgle) {
                    resetInt = maxRang;
                } else {
                    resetInt = (int) (recentAcgle / chazhi);
                }
                if (resetInt != recentInt) {

                    recentInt = resetInt;

                    if (listener != null) {

                        LogUtil.e("MotionEvent.ACTION_UP : "+recentInt);
                        listener.onStop(recentInt);
                    }
                }*/
                break;


            case MotionEvent.ACTION_CANCEL:

                break;
        }


        return true;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);


        canvas.save();

        Rect dst = new Rect(0, 0, weith, height);
        Rect src = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
        canvas.drawBitmap(mBitmap, src, dst, null);

        //画内层弧
        basePaint.setColor(Color.WHITE);
        basePaint.setStrokeWidth(arcWidth);
        RectF innerArc = new RectF(arcStrokeWidth + 80, arcStrokeWidth + 80, weith - arcStrokeWidth - 80, height - arcStrokeWidth - 80);
        canvas.drawArc(innerArc, 135, recentAcgle, false, basePaint);

        mTextPaint.setTextSize(dp2px(mTextSize));
        mTextPaint.setStrokeWidth(dp2px(mTextStrokeWidth));

        float textWieth = mTextPaint.measureText(text);

        canvas.drawText(text, weith / 2 - textWieth / 2, height / 2 + dp2px(mTextSize) / 2, mTextPaint);


    }

    private int dp2px(float dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dp, getResources().getDisplayMetrics());
    }

    private int sp2px(float sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                sp, getResources().getDisplayMetrics());
    }

    /**
     * 以按钮圆心为坐标圆点,建立坐标系,求出(targetX, targetY)坐标与x轴的夹角
     *
     * @param targetX x坐标
     * @param targetY y坐标
     * @return (targetX, targetY)坐标与x轴的夹角
     */
    private float calcAngle(float targetX, float targetY) {
        float x = targetX - weith / 2;//len/2 圆点x坐标
        float y = targetY - height / 2;//len/2 圆点y坐标
        double radian;

        if (x != 0) {
            float tan = Math.abs(y / x);
            if (x > 0) {
                if (y >= 0) {
                    radian = Math.atan(tan);
                } else {
                    radian = 2 * Math.PI - Math.atan(tan);
                }
            } else {
                if (y >= 0) {
                    radian = Math.PI - Math.atan(tan);
                } else {
                    radian = Math.PI + Math.atan(tan);
                }
            }
        } else {
            if (y > 0) {
                radian = Math.PI / 2;
            } else {
                radian = -Math.PI / 2;
            }
        }
        return (float) ((radian * 180) / Math.PI);
    }

    /**
     * 设置范围最大值和最小值
     * 保留几位小数
     *
     * @param min 最小值
     * @param max 最大值
     *            newScale 保留小数位数
     */
    public void setRotate(int min, int max) {
        this.maxRang = max;
        this.minRang = min;
        // this.newScale = newScale;
        this.newScale = 0;

        chazhi = maxAcgle / (float) (max - min);
    }

    public void setText(String text) {

        this.text = text;
        invalidate();
    }

    public void setRecentInt(int recent) {

        recentAcgle = (recent / (float) (maxRang - minRang)) * maxAcgle;

        recentInt = recent;

    }


    public void setTextSizeWidth(int textSize, int textStrokeWidth) {
        this.mTextSize = textSize;
        this.mTextStrokeWidth = textStrokeWidth;
    }


    public interface OnRotateViewListener {

        void onStart(int angle);

        void onRotateChange(int angle);

        void onStop(int angle);
    }


    private OnRotateViewStopListener listener;

    public void setOnRotateViewStopListener(OnRotateViewStopListener listener) {

        this.listener = listener;
    }

    public interface OnRotateViewStopListener {

        void onRotateChange(int angle);

        void onStop(int angle);
    }
}

 

 

Android中可以通过自定义View来实现圆弧进度条。以下是实现步骤: 1. 创建一个自定义View类,并继承View。在构造方法中初始化画笔和属性。 2. 重写onMeasure方法,指定View的宽高。 3. 重写onDraw方法,通过Canvas绘制圆弧。 4. 添加一个方法用于设置进度条进度值,例如setProgress(int progress)。 5. 在onDraw方法中使用Path和Canvas绘制圆弧。 6. 在setProgress方法中计算当前进度对应的角度,然后调用invalidate方法强制刷新视图。 7. 使用属性动画或ObjectAnimator来实现进度条动画效果。 8. 在布局文件中添加自定义View并设置相关属性。 9. 在Java代码中调用setProgress方法来更新进度条的值。 以下是一个简单的示例代码: ``` public class CustomArcProgressBar extends View { private Paint paint; private RectF rectF; private int progress; public CustomArcProgressBar(Context context) { super(context); init(); } public CustomArcProgressBar(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CustomArcProgressBar(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paint = new Paint(); rectF = new RectF(); progress = 0; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(Math.min(width, height), Math.min(width, height)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int centerX = getWidth() / 2; int centerY = getHeight() / 2; int radius = centerX - 10; paint.setColor(Color.GRAY); paint.setStrokeWidth(5); paint.setStyle(Paint.Style.STROKE); rectF.set(centerX - radius, centerY - radius, centerX + radius, centerY + radius); canvas.drawArc(rectF, 0, 360, false, paint); paint.setColor(Color.BLUE); canvas.drawArc(rectF, -90, (float) (progress * 3.6), false, paint); } public void setProgress(int progress) { if(progress >= 0 && progress <= 100) { this.progress = progress; invalidate(); } } } ``` 希望以上回答对您有帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值