安卓自定义圆环双滑动进度条

先看下效果:


说明:可以根据需要自行修改,黄色球为开始,红色球为结束,对应圆环里面的  :开始<--->结束;最大进度可调,默认100;开始和结束球都可手动拖动。(不喜勿碰)

不废话,直接上代码:


 第一步,使用到的工具类

1.     先上工具类:


import android.graphics.Point;
import android.graphics.PointF;

import static android.R.attr.id;


public class ChartUtils {
    /**
     * 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
     *
     * @param cirX     centerX
     * @param cirY     centerY

     * @return 扇形终射线与圆弧交叉点的xy坐标
     */
    public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float
            cirAngle) {
        float posX = 0.0f;
        float posY = 0.0f;
        //将角度转换为弧度
        float arcAngle = (float) (Math.PI * cirAngle / 180.0);
        if (cirAngle < 90) {
            posX = cirX + (float) (Math.cos(arcAngle)) * radius;
            posY = cirY + (float) (Math.sin(arcAngle)) * radius;
        } else if (cirAngle == 90) {
            posX = cirX;
            posY = cirY + radius;
        } else if (cirAngle > 90 && cirAngle < 180) {
            arcAngle = (float) (Math.PI * (180 - cirAngle) / 180.0);
            posX = cirX - (float) (Math.cos(arcAngle)) * radius;
            posY = cirY + (float) (Math.sin(arcAngle)) * radius;
        } else if (cirAngle == 180) {
            posX = cirX - radius;
            posY = cirY;
        } else if (cirAngle > 180 && cirAngle < 270) {
            arcAngle = (float) (Math.PI * (cirAngle - 180) / 180.0);
            posX = cirX - (float) (Math.cos(arcAngle)) * radius;
            posY = cirY - (float) (Math.sin(arcAngle)) * radius;
        } else if (cirAngle == 270) {
            posX = cirX;
            posY = cirY - radius;
        } else {
            arcAngle = (float) (Math.PI * (360 - cirAngle) / 180.0);
            posX = cirX + (float) (Math.cos(arcAngle)) * radius;
            posY = cirY - (float) (Math.sin(arcAngle)) * radius;
        }
        return new PointF(posX, posY);
    }

    /**
     * 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
     *
     * @param cirX       centerX
     * @param cirY       centerY
     * @param radius     圆半径

     * @return 扇形终射线与圆弧交叉点的xy坐标
     */
    public static PointF calcArcEndPointXY(float cirX, float cirY, float radius, float
            cirAngle, float orginAngle) {
        cirAngle = (orginAngle + cirAngle) % 360;
        return calcArcEndPointXY(cirX, cirY, radius, cirAngle);
    }

    public static double calSweep(float x, float y, float radius) {
        double sweep = 0;
        if (x > radius) {
            if (y <= radius) {
//                                double t = (x - radius) / (radius - y);
                sweep = (Math.toDegrees(Math.atan(t))) + 270;
            } else {
//                                double t = (y - radius) / (x - radius);
                sweep = (Math.toDegrees(Math.atan(t)));
            }

        } else {
            if (y <= radius) {
//                                double t = (radius - y) / (radius - x);
                sweep = (Math.toDegrees(Math.atan(t))) + 180;
            } else {
//                                double t = (radius - x) / (y - radius);
                sweep = (Math.toDegrees(Math.atan(t))) + 90;
            }

        }

        return sweep%360;
    }

    public static PointF calPointByAngle(int x, int y, int r, float angle) {
        PointF p = new PointF();

        if (angle >= 0 && angle < 90) {
//            第四象限
            p.x = (float) (r * (Math.cos(Math.PI * angle / 180.0)) + 1);
            p.y = (float) (r * (1 +Math.sin(Math.PI * angle / 180.0)));

        } else if (angle >= 90 && angle < 180) {
//            第三象限
            p.x = (float) (r * (-Math.sin(Math.PI * (angle - 90) / 180.0)) + 1);
            p.y = (float) (r * (1 + Math.cos(Math.PI * (angle - 90) / 180.0)));
        } else if (angle >= 180 && angle < 270) {
//            第二象限
            p.x = (float) (r * (-Math.cos(Math.PI * (angle - 180) / 180.0)) + 1);
            p.y = (float) (r * (1 - Math.sin(Math.PI * (angle - 180) / 180.0)));
        } else if (angle >= 270 && angle <= 360) {
//            第一象限
            p.x = (float) (r * (Math.sin(Math.PI * (angle - 270) / 180.0)) + 1);
            p.y = (float) (r * (1 - Math.cos(Math.PI * (angle - 270) / 180.0)));
        }

        return p;
    }

}
 

第二步,自定义view:

2:circle:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.Map;

import static android.R.attr.angle;
import static android.R.attr.centerX;
import static android.R.attr.centerY;
import static android.R.attr.max;
import static android.R.attr.radius;
import static android.R.attr.textColor;
import static android.R.attr.textSize;
import static android.R.attr.x;
import static android.R.attr.y;


public class Circle extends View {

    private Paint mPaint;
    private int circleColor;
    private int progressColor;
    private int progressWidth;
    private float startAngle;
    private float sweepAngle;
    private int radius;
    private int centreX;
    private int centreY;
    private int maxError = 70;
    private int maxError0 = 100;
    private boolean downOnArc;
    private boolean isSecond = true;
    private int maxProgress = 100;
    /**
     * 中间进度百分比的字符串的颜色
     */
    private int textColor;

    /**
     * 中间进度百分比的字符串的字体
     */
    private float textSize;


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

    public Circle(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Circle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mPaint = new Paint();

        circleColor = Color.GRAY;
        progressColor = Color.GREEN;
        progressWidth = 30;
        startAngle = 270;
        sweepAngle = 60;
        textColor = Color.BLACK;
        textSize = 40;

    }

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

        //获取测量模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //获取测量大小
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int mWidth = 0;
        int mHeight = 0;

        if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
            radius = widthSize / 2;
            centreX = widthSize / 2;
            centreY = heightSize / 2;

            mWidth = widthSize;
            mHeight = heightSize;
        }

        if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
            mWidth = (int) (radius * 2);
            mHeight = (int) (radius * 2);
            centreX = radius;
            centreY = radius;

        }
        setMeasuredDimension(mWidth, mHeight);
    }


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

        mPaint.setColor(circleColor);
        mPaint.setStrokeWidth(progressWidth);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.STROKE);
//        RectF rect = new RectF(progressWidth, progressWidth, getWidth() - progressWidth, getHeight() - progressWidth);
        RectF rect = new RectF(progressWidth * 2, progressWidth * 2, getWidth() - progressWidth * 2, getHeight() - progressWidth * 2);
        canvas.drawArc(rect, 0, 360, false, mPaint);
        mPaint.setColor(progressColor);
        canvas.drawArc(rect, startAngle, sweepAngle, false, mPaint);

//        画小圆
        Paint p = new Paint();
        p.setColor(Color.RED);
        p.setStrokeWidth(4);
        p.setStyle(Paint.Style.FILL);
        PointF point = ChartUtils.calcArcEndPointXY(centreX, centreY, radius - progressWidth * 2, sweepAngle, startAngle);
        canvas.drawCircle(point.x, point.y, 40, p);
        //        画小圆2
        p.setColor(Color.YELLOW);
        PointF point2 = ChartUtils.calcArcEndPointXY(centreX, centreY, radius - progressWidth * 2, 0, startAngle);
        canvas.drawCircle(point2.x, point2.y, 40, p);

        /**
         * 画文字
         */
        mPaint.setStrokeWidth(0);
        mPaint.setColor(textColor);
        mPaint.setTextSize(textSize);
        String textTime = getTimeText(startAngle, sweepAngle);
        float textWidth = mPaint.measureText(textTime);
        canvas.drawText(textTime, centreX - textWidth / 2, centreY + textSize / 2, mPaint);
    }

    private String getTimeText(float startAngle, float sweepAngle) {
        float startProgress = (startAngle + 90) % 360 / 360 * maxProgress;
        float endProgress = sweepAngle / 360 * maxProgress + startProgress;

        String result = startProgress + "<--->" + endProgress%maxProgress;

        return result;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        int action = event.getAction();
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (action) {
            case MotionEvent.ACTION_DOWN:

                    if (isTouchArc(x, y)) {

                        downOnArc = true;
                        changePosition(x, y, radius);
                        return true;
                    }
                break;
            case MotionEvent.ACTION_MOVE:

                    if (downOnArc) {

                        changePosition(x, y, radius);
                        return true;
                    }

                break;
            case MotionEvent.ACTION_UP:

                downOnArc = false;

                changePosition(x, y, radius);

                break;
        }
        return super.onTouchEvent(event);
    }

    // 判断是否按在圆边上
    private boolean isTouchArc(int x, int y) {
//        double d = getTouchRadius(x, y);
        PointF p = ChartUtils.calcArcEndPointXY(centreX, centreY, radius, sweepAngle, startAngle);
        PointF p2 = ChartUtils.calcArcEndPointXY(centreX, centreY, radius, 0, startAngle);
        int absx = (int) Math.abs(x - p.x);
        int absy = (int) Math.abs(y - p.y);
        int absx2 = (int) Math.abs(x - p2.x);
        int absy2 = (int) Math.abs(y - p2.y);
        if (absx <= maxError && absy <= maxError) {
            isSecond = true;
            return true;
        }
        if (absx2 <= maxError0 && absy2 <= maxError0) {
            isSecond = false;
            return true;
        }

        return false;
    }


    private void changePosition(int x, int y, int r) {

        double v = ChartUtils.calSweep(x, y, r);
        if (sweepAngle >= 360) {
            sweepAngle = sweepAngle % 360;
        }

        if (isSecond) {
            changeSecond(x, y, r, v);
        } else {
            changeFirst(v);
        }

        if (changeListener != null) {
            changeListener.onProgressChange(startAngle, sweepAngle);
        }
        invalidate();


    }

    //    改变第二个点的位置
    private void changeSecond(int x, int y, int r, double v) {

        if (x > r) {
            if (y <= r) {
                if (v >= startAngle) {
                    sweepAngle = (float) (v - startAngle);
                } else {
                    sweepAngle = (float) (360 - (startAngle - v));
                }
            } else {
                sweepAngle = (float) (360 - (startAngle - v));
            }
        } else {
            sweepAngle = (float) (360 + v - startAngle);
        }
        sweepAngle = sweepAngle % 360;

    }

    //    改变第一个点的位置
    private void changeFirst(double v) {

//        float secondAngle = (startAngle + sweepAngle) % 360;

        if (sweepAngle < 0) {
            sweepAngle = sweepAngle + 360;
        }
        float cSweep = (float) (v - startAngle);
        startAngle = (float) v;
        sweepAngle = sweepAngle - cSweep;

    }

    //    判断第一个原点是不是跟在第二个后面
    private boolean isAfterFllow(float start, float sweep) {

        float startProgress = (startAngle + 90) % 360 / 360 * maxProgress;
        float endProgress = sweepAngle / 360 * maxProgress + startProgress;

        return endProgress <= maxProgress && (Math.ceil(sweep) >= 30);
    }


    // 计算某点到圆点的距离

    private double getTouchRadius(int x, int y) {
        int cx = x - getWidth() / 2;
        int cy = y - getHeight() / 2;
        return Math.hypot(cx, cy);
    }


    private OnProgressChangeListener changeListener;

    public interface OnProgressChangeListener {

        void onProgressChange(float start, float sweep);

    }

    public void setListener(OnProgressChangeListener changeListener) {
        this.changeListener = changeListener;
    }

}
 

第三步,布局:


<com.example.dyy.circleandaddress.Circle
    android:id="@+id/circle"
    android:layout_gravity="center_horizontal"
    android:layout_width="300dp"
    android:layout_height="300dp"
    android:padding="100dp" />

end!





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Halcon是一款著名的机器视觉软件,它提供了许多实用的工具来协助用户进行图像处理和分析。其中,圆环ROI区域是一个非常重要的概念,在诸多视觉应用中得到了广泛应用。 圆环ROI区域指的是一个由两个圆组成的环形区域,通常用于提取目标物体的边缘或轮廓。在Halcon中,可以通过调用ROI类的gen_circle_contour_xld函数来生成一个圆环ROI区域。此函数需要输入两个圆的坐标以及半径,然后返回一个表示圆环轮廓的XLD对象。 一般情况下,圆环ROI区域被用于检测和量化目标物体的周长、直径等特征,以及判断其是否符合特定的形状或结构要求。圆环ROI区域的大小、形状、位置等参数可以通过调整圆的坐标和半径而进行精确控制。 总之,圆环ROI区域是Halcon中一个非常有用的工具,能够帮助用户实现精准的图像处理和分析,提高视觉检测的准确性和效率。 ### 回答2: Halcon中的圆环ROI(Region of Interest)区域是指由两个圆形构成的区域,即内圆和外圆之间的部分。使用该功能可以在一幅图像中选择一个特定的圆环区域,然后在操作中对其进行特定的处理,例如测量、分析、识别或跟踪等。 在使用Halcon进行圆环ROI区域选择时,需要指定内圆和外圆的半径大小和位置。可以通过直接在图像上拖动鼠标进行手动绘制,也可以通过设置半径大小和圆心坐标位置来精确选择ROI区域。 在选择完成之后,还可以对该区域进行缩放、旋转、移动等操作,以便更好地适应不同的图像处理需求。此外,Halcon还提供了丰富的圆环ROI区域处理功能,如测量角度、计算面积、分割对象等,可以方便地实现各种各样的图像处理应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值