自定义view之分段圆环view,旋转,点击分段回调

该博客介绍了一个自定义View `MyRingView` 的实现,用于创建带有颜色和宽度可配置的圆环进度条。博客详细解析了如何通过`declare-styleable`定义属性,以及在`onCreate`中获取属性值来初始化组件。此外,还展示了`onTouchEvent`处理用户交互,如手势旋转和点击事件监听。最后,`drawRing`方法用于绘制圆环并根据数据动态调整进度。
摘要由CSDN通过智能技术生成

attrs内容

<declare-styleable name="MyRingView">
    <!-- 圆环宽度 -->
    <attr name="ringWidth" format="float" />
    <!-- 圆环的默认颜色 -->
    <attr name="ringNormalColor" format="color"/>
</declare-styleable>

代码

public class MyRingView extends View {

    private Paint dataPaint, assetsPaint;
    private RectF rectF;
    //
    private ArrayList<Paint> paints;
    //数据, key - 颜色值, value - 数值
    private ArrayMap<Integer, Integer> dataMap;
    private ArrayList<Integer> paintColor;
    private ArrayList<Integer> paintData;
    private ArrayList<Path> paths;
    private ArrayList<Region> regions;
    //圆环宽度
    private float ringWidth;
    //圆环默认颜色
    private int ringNormalColor;
    //圆环起始角
    private int ringStartAngle = 0, startAngle;
    //圆环角度
    private int ringAngle = 360;
    //圆环 宽   高
    private int viewWidth, viewHeight;
    //是否可以旋转
    private boolean rotate = false;

    private setOnClickListener listener;

    public void setListener(setOnClickListener listener) {
        this.listener = listener;
    }

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

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

    /**
     * Paint.ANTI_ALIAS_FLAG :抗锯齿标志
     * Paint.FILTER_BITMAP_FLAG : 使位图过滤的位掩码标志
     * Paint.DITHER_FLAG : 使位图进行有利的抖动的位掩码标志
     * Paint.UNDERLINE_TEXT_FLAG : 下划线
     * Paint.STRIKE_THRU_TEXT_FLAG : 中划线
     * Paint.FAKE_BOLD_TEXT_FLAG : 加粗
     * Paint.LINEAR_TEXT_FLAG : 使文本平滑线性扩展的油漆标志
     * Paint.SUBPIXEL_TEXT_FLAG : 使文本的亚像素定位的绘图标志
     * Paint.EMBEDDED_BITMAP_TEXT_FLAG : 绘制文本时允许使用位图字体的绘图标志
     */
    @SuppressLint("UseSparseArrays")
    public MyRingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyRingView, defStyleAttr, 0);

        ringWidth = ta.getFloat(R.styleable.MyRingView_ringWidth, 50);
        ringNormalColor = ta.getColor(R.styleable.MyRingView_ringNormalColor, context.getResources().getColor(R.color.colorTran));

        ta.recycle();

        dataPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        dataPaint.setAntiAlias(true);
        dataPaint.setStyle(Paint.Style.STROKE);
        dataPaint.setStrokeWidth(ringWidth);

        assetsPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        assetsPaint.setAntiAlias(true);
        assetsPaint.setStyle(Paint.Style.STROKE);
        assetsPaint.setStrokeWidth(ringWidth);

        setWillNotDraw(false);

        paints = new ArrayList<>();
        paintColor = new ArrayList<>();
        paintData = new ArrayList<>();
        paths = new ArrayList<>();
        regions = new ArrayList<>();
        dataMap = new ArrayMap<>();
    }

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

        int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
        enter = width / 2;
        setMeasuredDimension(width, width);
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        viewWidth = getWidth();
        viewHeight = getHeight();
        rectF = new RectF(
                ringWidth / 2,
                ringWidth / 2,
                viewWidth - ringWidth / 2,
                viewHeight - ringWidth / 2);
    }


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

        drawRing(canvas);
    }


    /**
     * MotionEvent.ACTION_DOWN:当屏幕检测到第一个触点按下之后就会触发到这个事件。
     * MotionEvent.ACTION_MOVE:当触点在屏幕上移动时触发,触点在屏幕上停留也是会触发的,主要是由于它的灵敏度很高,
     * 而我们的手指又不可能完全静止(即使我们感觉不到移动,但其实我们的手指也在不停地抖动)。
     * MotionEvent.ACTION_POINTER_DOWN:当屏幕上已经有触点处于按下的状态的时候,再有新的触点被按下时触发。
     * MotionEvent.ACTION_POINTER_UP:当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)触发。
     * MotionEvent.ACTION_UP:当触点松开时被触发。
     * MotionEvent.ACTION_OUTSIDE: 表示用户触碰超出了正常的UI边界.
     * MotionEvent.ACTION_SCROLL:android3.1引入,非触摸滚动,主要是由鼠标、滚轮、轨迹球触发。
     * MotionEvent.ACTION_CANCEL:不是由用户直接触发,由系统在需要的时候触发,例如当父view通过使函数
     */
    private int x, y;
    private float enter;
    private int level, number;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                x = (int) event.getX();
                y = (int) event.getY();
                for (int i = 0; i < regions.size(); i++) {
                    if (regions.get(i).contains(x, y)) {
                        if (number < 100){
                            if (i == level - 1){
                                Toast.makeText(getContext(), "无数据", Toast.LENGTH_SHORT).show();
                            }else {
                                listener.onClickListener(i);
                            }
                        }
                        if(number >= 100){
                            listener.onClickListener(i);
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_MOVE:
                int x1 = (int) event.getX();
                int y1 = (int) event.getY();

                float k1 = (x - enter) / (y - enter);
                float k2 = (x1 - enter) / (y1 - enter);

                if (ringStartAngle >= 360 || ringStartAngle <= -360) {
                    ringStartAngle = 0;
                }
                if (k1 > k2) {  //顺时针
                    ringStartAngle = ringStartAngle + 3;
                } else {    //逆时针
                    ringStartAngle = ringStartAngle - 3;
                }
                x = (int) event.getX();
                y = (int) event.getY();
                if (rotate){
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
//                invalidate();
                break;
        }
        return true;
    }

    /**
     * path.arcTo 添加弧形
     * path.addOval 添加扇形
     */
    private void drawRing(Canvas canvas) {
        paintColor.clear();
        paintData.clear();
        paints.clear();
        paths.clear();
        regions.clear();

        startAngle = ringStartAngle;

        for (Integer integer : dataMap.keySet()) {
            paintColor.add(integer);
            paintData.add(dataMap.get(integer));
            paints.add(assetsPaint);

            paths.add(new Path());
            regions.add(new Region());
        }

        if (dataMap.size() <= 0) {
            dataPaint.setColor(ringNormalColor);
            canvas.drawArc(rectF, startAngle, ringAngle, false, dataPaint);
        } else {
            level = paintColor.size();
            number = 0;
            for (int i = 0; i < level; i++) {
                number += paintData.get(i);
            }
            if (number < 100) {
                paintColor.add(Color.parseColor("#bbbbbb"));
                paintData.add(100 - number);
                paints.add(assetsPaint);

                paths.add(new Path());
                regions.add(new Region());
            }

            level = paintColor.size();
            for (int i = 0; i < level; i++) {
                paints.get(i).setColor(paintColor.get(i));
                if (i == level - 1) {
                    canvas.drawArc(rectF, startAngle, ringAngle - startAngle + ringStartAngle, false, paints.get(i));
                    paths.get(i).arcTo(rectF, startAngle, ringAngle - startAngle + ringStartAngle);
                    regions.get(i).setPath(paths.get(i), new Region((int) rectF.left, (int) rectF.top,
                            (int) rectF.right, (int) rectF.bottom));
                } else {
                    canvas.drawArc(rectF, startAngle, Math.round((float) (paintData.get(i) * 3.6)), false, paints.get(i));
                    paths.get(i).arcTo(rectF, startAngle, Math.round((float) (paintData.get(i) * 3.6)));
                    regions.get(i).setPath(paths.get(i), new Region((int) rectF.left, (int) rectF.top,
                            (int) rectF.right, (int) rectF.bottom));
                    startAngle += Math.round((float) (paintData.get(i) * 3.6));
                }
            }
        }
    }

    //设置数据
    public void setMapData(Map map){
        dataMap.clear();
        dataMap.putAll(map);
    }

    //设置是否旋转
    public void setRotate(boolean rot){
        rotate = rot;
    }

    public interface setOnClickListener{
        void onClickListener(int index);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值