扇形进度条 可点击可滑动

2020-11-30  本文已影响0人  azu_test
扇形进度条.png
package com.wuyzh.test;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/***
 * 1/4扇形进度条
 * */
public class PieChartSeekBar extends View {

    //饼状图半径
    private float mRadius = DensityUtils.dip2px(getContext(), 320);
    private RectF mSectorRectF = new RectF();
    //扇形外圈宽度
    private float mRadiusOutlineWidth = 5f;
    private RectF mIncludeOutLineRectF = new RectF();
    //初始画弧所在的角度
    private float mCurrentDegree = 135f;
    //空白间隙占整个圆形的大小比例
    private double mSpacePercent = 0.002f;
    //每块扇形占整个圆的大小比例
    private double mSectorPercent;
    //扇形块个数
    private int mSize = 8;
    //当前进度
    private int mCurrentLevel = 4;
    //画笔
    private Paint mPaint;
    private Xfermode mDstOutXfermode;
    private PorterDuff.Mode mDstOutPorterDuffMode = PorterDuff.Mode.DST_OUT;
    private Xfermode mSrcOverXfermode;
    private PorterDuff.Mode mSrcOverPorterDuffMode = PorterDuff.Mode.SRC_OVER;
    //设置监听回调
    private OnPieChartSeekBarChangeListener  mOnPieChartSeekBarChangeListener;
    public PieChartSeekBar(Context context) {
        super(context);
        init();
    }

    public PieChartSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PieChartSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    //初始化画笔和效果动画
    private void init() {
        mDstOutXfermode = new PorterDuffXfermode(mDstOutPorterDuffMode);
        mSrcOverXfermode = new PorterDuffXfermode(mSrcOverPorterDuffMode);
        initRectF();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mSectorPercent = (0.25 - (mSize-1)*mSpacePercent)/mSize;
    }

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

        int saveCount = canvas.saveLayer(mIncludeOutLineRectF, mPaint, Canvas.ALL_SAVE_FLAG);
        mCurrentDegree = 135;
        float pieSweep;
        for (int i = 1; i <= mSize; i++){
            if (i <= mCurrentLevel){
                //画外部进度指示线 颜色:0xFFcca267
                pieSweep = (float) (mSectorPercent *360);
                mPaint.setShader(null);
                mPaint.setColor(0xFFcca267);
                canvas.drawArc(mIncludeOutLineRectF, mCurrentDegree, pieSweep, true, mPaint);

                //切掉内部圆
                mPaint.setColor(Color.BLACK);
                mPaint.setXfermode(mDstOutXfermode);
                canvas.drawArc(mSectorRectF, mCurrentDegree-0.002f*360,  pieSweep+0.003f*360,true, mPaint);
                mPaint.setXfermode(null);

                //画扇形 颜色:0xFF02DAFD
                RadialGradient gradient =new RadialGradient(mRadius,mRadius,mRadius-mRadiusOutlineWidth,new int[]{Color.TRANSPARENT,Color.TRANSPARENT,0xFF02DAFD},new float[]{0,(mRadius-100)/mRadius,1},Shader.TileMode.MIRROR);
                mPaint.setShader(gradient);
                mPaint.setXfermode(mSrcOverXfermode);
                canvas.drawArc(mSectorRectF, mCurrentDegree, pieSweep, true, mPaint);
                mPaint.setXfermode(null);
                mCurrentDegree = mCurrentDegree + pieSweep;
            }else {
                //画外部进度指示线  颜色:0xFF65fbe4
                pieSweep = (float) (mSectorPercent *360);
                mPaint.setShader(null);
                mPaint.setColor(0xFF65fbe4);
                canvas.drawArc(mIncludeOutLineRectF, mCurrentDegree, pieSweep, true, mPaint);

                //切掉内部圆
                mPaint.setColor(Color.BLACK);
                mPaint.setXfermode(mDstOutXfermode);
                canvas.drawArc(mSectorRectF, mCurrentDegree-0.002f*360,  pieSweep+0.003f*360,true, mPaint);
                mPaint.setXfermode(null);

                //画扇形  颜色:0xFFFFFFFF
                RadialGradient gradient =new RadialGradient(mRadius,mRadius,mRadius-mRadiusOutlineWidth,new int[]{Color.TRANSPARENT,Color.TRANSPARENT,0xFFFFFFFF},new float[]{0,(mRadius-100)/mRadius,1},Shader.TileMode.MIRROR);
                mPaint.setShader(gradient);
                mPaint.setXfermode(mSrcOverXfermode);
                canvas.drawArc(mSectorRectF, mCurrentDegree, pieSweep, true, mPaint);
                mPaint.setXfermode(null);
                mCurrentDegree = mCurrentDegree + pieSweep;
            }
            //画透明间隔
            if (i != mSize){
                pieSweep = (float) (mSpacePercent * 360);
                mPaint.setColor(Color.TRANSPARENT);
                canvas.drawArc(mIncludeOutLineRectF, mCurrentDegree, pieSweep, true, mPaint);
                mCurrentDegree = mCurrentDegree + pieSweep;
            }
        }

        //切掉内部圆
        mPaint.setShader(null);
        mPaint.setColor(Color.BLACK);
        mPaint.setXfermode(mDstOutXfermode);
        canvas.drawCircle(mRadius, mRadius,  mRadius-100, mPaint);
        mPaint.setXfermode(null);
        canvas.restoreToCount(saveCount);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int length = (int) (2 * mRadius);
        setMeasuredDimension(length, length);
    }

    /**
     * 初始化绘制弧形所在矩形的四点坐标
     **/
    private void initRectF() {
        mSectorRectF.left = 0 +mRadiusOutlineWidth;
        mSectorRectF.top = 0 + mRadiusOutlineWidth;
        mSectorRectF.right = 2 * mRadius - mRadiusOutlineWidth;
        mSectorRectF.bottom = 2 * mRadius - mRadiusOutlineWidth;

        mIncludeOutLineRectF.left = 0;
        mIncludeOutLineRectF.top = 0;
        mIncludeOutLineRectF.right = 2 * mRadius;
        mIncludeOutLineRectF.bottom = 2 * mRadius;
    }

    private int mTouchPosition;
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mTouchPosition = doOnSpecialTypeClick(event);
                if (mTouchPosition == -1){
                    return false;
                }else {
                    mCurrentLevel = mTouchPosition;
                    if (mOnPieChartSeekBarChangeListener != null){
                        mOnPieChartSeekBarChangeListener.onStartTrackingTouch(this);
                    }
                    invalidate();
                    return true;
                }
            case MotionEvent.ACTION_MOVE:
                mTouchPosition = doOnSpecialTypeClick(event);
                if (mTouchPosition != -1){
                    mCurrentLevel = mTouchPosition;
                    if (mOnPieChartSeekBarChangeListener != null){
                        mOnPieChartSeekBarChangeListener.onProgressChanged(this,mCurrentLevel,true);
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mOnPieChartSeekBarChangeListener != null){
                    mOnPieChartSeekBarChangeListener.onStopTrackingTouch(this);
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    public int getProgress(){
        return mCurrentLevel;
    }

    public void setProgress(int level){
        mCurrentLevel = level;
        if (mOnPieChartSeekBarChangeListener != null){
            mOnPieChartSeekBarChangeListener.onProgressChanged(this,mCurrentLevel,true);
        }
        invalidate();
    }

    private int doOnSpecialTypeClick(MotionEvent event) {
        float eventX = event.getX();
        float eventY = event.getY();
        double alfa = 0;

        //点击的位置到圆心距离的平方
        double distance = Math.pow(eventX - mRadius, 2) + Math.pow(eventY - mRadius, 2);
        //判断点击的坐标是否在环内
        if (distance < Math.pow(mRadius, 2) && distance > Math.pow(0.72 * mRadius, 2)) {
            int which = touchOnWhichPart(event);
            switch (which) {
                case PART_ONE:
                    alfa = Math.atan2(eventX - mRadius, mRadius - eventY) * 180 / PI;
                    break;
                case PART_TWO:
                    alfa = Math.atan2(eventY - mRadius, eventX - mRadius) * 180 / PI + 90;
                    break;
                case PART_THREE:
                    alfa = Math.atan2(mRadius - eventX, eventY - mRadius) * 180 / PI + 180;
                    break;
                case PART_FOUR:
                    alfa = Math.atan2(mRadius - eventY, mRadius - eventX) * 180 / PI + 270;
                    break;
            }
        }

        int position = (int) Math.ceil((alfa-225)/(90.0/mSize));
        if (0 <= position&& position <= mSize){
            return position;
        }else {
            return -1;
        }
    }

    /**
     *    4 |  1
     * -----|-----
     *    3 |  2
     * 圆被分成四等份,判断点击在园的哪一部分
     */
    private static final int PART_ONE = 1;
    private static final int PART_TWO = 2;
    private static final int PART_THREE = 3;
    private static final int PART_FOUR = 4;
    //圆周率
    private static final float PI = 3.1415f;
    private int touchOnWhichPart(MotionEvent event) {
        if (event.getX() > mRadius) {
            if (event.getY() > mRadius) return PART_TWO;
            else return PART_ONE;
        } else {
            if (event.getY() > mRadius) return PART_THREE;
            else return PART_FOUR;
        }
    }

    public void setOnPieChartSeekBarChangeListener(OnPieChartSeekBarChangeListener onPieChartSeekBarChangeListener){
        mOnPieChartSeekBarChangeListener = onPieChartSeekBarChangeListener;
    }

    public interface OnPieChartSeekBarChangeListener {
        void onProgressChanged(PieChartSeekBar pieChartSeekBar, int level, boolean isFromUser);

        void onStartTrackingTouch(PieChartSeekBar pieChartSeekBar);

        void onStopTrackingTouch(PieChartSeekBar pieChartSeekBar);
    }
}

自定义属性颜色啥的自己来吧。。。。
使用

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/seat_set_background"
    tools:context=".MainActivity">

    <com.wuyazh.test.PieChartSeekBar
        android:id="@+id/pie_chart_seek_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>

</RelativeLayout>
```![扇形进度条.png](https://img.haomeiwen.com/i11008949/a3fee86894cb4717.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
上一篇 下一篇

猜你喜欢

热点阅读