美文网首页
扇形进度条 可点击可滑动

扇形进度条 可点击可滑动

作者: azu_test | 来源:发表于2020-11-30 15:16 被阅读0次
扇形进度条.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)

相关文章

  • 扇形进度条 可点击可滑动

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

  • Android自定义环形进度条

    手把手教你实现一个炫酷的环形进度条。 支持纯色/渐变,可选线条样式,动画时长。 可实现扇形进度条、多个进度叠加、最...

  • 底部菜单fragmentTabHost和viewpager

    想要实现底部菜单使用fragmentTabHost,可以点击可滑动 上代码 ![Uploading Paste_I...

  • IOS开发 UISlider和UIProgressView

    进度条和滑动动条控件 本节学习内容: 1.进度条和滑动条的概念 2.进度条和滑动条的属性 3.进度条和滑动条的使用...

  • 背景图片跟随手势滑动的ViewPager

    背景图片跟随手势滑动的ViewPager,可各方向滑动或点击切换页面。 效果参考 ANA Portuguese A...

  • 进度条案例

    点击开始按钮,进度条可以开始,如果当前进度不为0,开始按钮就没有作用;点击暂停按钮,进度可以停下来;点击继续按钮可...

  • IOS-UISlider&UIProgressView

    滑动条和进度条,滑动条是主动的,进度条是被动的。

  • 扇形进度条

    分享一个扇形进度条,核心思想是用贝塞尔曲线绘制扇形形成进度条子单位,然后通过修改layer的fillcolor实现...

  • 小程序中图片点击全屏-可滑动

    在小程序中,如果页面中有多张图片,那么用户可能会习惯向在朋友圈中一样打开图片,这里微信也提供了类似的api,可以直...

  • 扇形进度条 - iOS

    绘制扇形进度条 背景:上传文件的时候,需要有上传进度,这次需要一个扇形的进度条,示例图如下: 废话不多说,上代码:...

网友评论

      本文标题:扇形进度条 可点击可滑动

      本文链接:https://www.haomeiwen.com/subject/hkgrwktx.html