我正在参加「启航方案」

前语

  前面写了圆环进展条,这次咱们来写一个饼状进展条,首要看一下作用图:

正文

  作用图感觉怎么样呢?下面咱们来实现这个自定义View,依然是写在EasyView这个项目中,这是一个自定义View库,我会把自己写的自定义View都放在里边,文中假如代码不是很全的话,你能够找到文章最后的源码去查看,话不多说,咱们开端吧。

一、XML款式

  依据上面的作用图,咱们首要来确认XML中的特点款式,在attrs.xml中添加如下代码:

	<!--饼状进展条-->
    <declare-styleable name="PieProgressBar">
        <!--半径-->
        <attr name="radius" />
        <!--最大进展-->
        <attr name="maxProgress" />
        <!--当前进展-->
        <attr name="progress" />
        <!--进展条进展色彩-->
        <attr name="progressbarColor" />
        <!--进展条描边宽度-->
        <attr name="strokeWidth"/>
        <!--进展是否突变-->
        <attr name="gradient" />
        <!--突变色彩数组-->
        <attr name="gradientColorArray" />
        <!--自定义开端视点 0 ,90,180,270-->
        <attr name="customAngle">
            <enum name="right" value="0" />
            <enum name="bottom" value="90" />
            <enum name="left" value="180" />
            <enum name="top" value="270" />
        </attr>
    </declare-styleable>

  这儿的公共特点我就抽离了出来,由于之前写过圆环进展条,有一些特点是能够通用的,并且我在饼状进展条中添加了开端的视点,之前是默认是从0开端,现在能够依据特点设置开端的视点,并且我添加了突变色彩。

二、结构办法

  现在特点款式现已有了,下一步就是写自定义View的结构办法了,在com.easy.view包下新建一个PieProgressBar 类,里边的代码如下所示:

public class PieProgressBar extends View {
    /**
     * 半径
     */
    private int mRadius;
    /**
     * 进展条宽度
     */
    private int mStrokeWidth;
    /**
     * 进展条进展色彩
     */
    private int mProgressColor;
    /**
     * 开端视点
     */
    private int mStartAngle = 0;
    /**
     * 当前视点
     */
    private float mCurrentAngle = 0;
    /**
     * 完毕视点
     */
    private int mEndAngle = 360;
    /**
     * 最大进展
     */
    private float mMaxProgress;
    /**
     * 当前进展
     */
    private float mCurrentProgress;
    /**
     * 是否突变
     */
    private boolean isGradient;
    /**
     * 突变色彩数组
     */
    private int[] colorArray;
    /**
     * 动画的履行时长
     */
    private long mDuration = 1000;
    /**
     * 是否履行动画
     */
    private boolean isAnimation = false;
    public PieProgressBar(Context context) {
        this(context, null);
    }
    public PieProgressBar(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public PieProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PieProgressBar);
        mRadius = array.getDimensionPixelSize(R.styleable.PieProgressBar_radius, 80);
        mStrokeWidth = array.getDimensionPixelSize(R.styleable.PieProgressBar_strokeWidth, 8);
        mProgressColor = array.getColor(R.styleable.PieProgressBar_progressbarColor, ContextCompat.getColor(context, R.color.tx_default_color));
        mMaxProgress = array.getInt(R.styleable.PieProgressBar_maxProgress, 100);
        mCurrentProgress = array.getInt(R.styleable.PieProgressBar_progress, 0);
        //是否突变
        isGradient = array.getBoolean(R.styleable.PieProgressBar_gradient, false);
        //突变色彩数组
        CharSequence[] textArray = array.getTextArray(R.styleable.PieProgressBar_gradientColorArray);
        if (textArray != null) {
            colorArray = new int[textArray.length];
            for (int i = 0; i < textArray.length; i++) {
                colorArray[i] = Color.parseColor((String) textArray[i]);
            }
        }
        mStartAngle = array.getInt(R.styleable.PieProgressBar_customAngle, 0);
        array.recycle();
    }
}

  这儿声明了一些变量,然后写了3个结构办法,在第三个结构办法中进行特点的赋值。

三、测量

  这儿测量就比较简单了,和之前的圆环进展条差不多,代码如下所示:

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = 0;
        switch (MeasureSpec.getMode(widthMeasureSpec)) {
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:   //wrap_content
                width = mRadius * 2;
                break;
            case MeasureSpec.EXACTLY:   //match_parent
                width = MeasureSpec.getSize(widthMeasureSpec);
                break;
        }
        //Set the measured width and height
        setMeasuredDimension(width, width);
    }

  由于不需求进行子控件处理,所以咱们只需一个圆和描边就行了,下面看制作的办法。

四、制作

  制作这儿就是制作描边和进展,制作的代码如下所示:

    @Override
    protected void onDraw(Canvas canvas) {
        int centerX = getWidth() / 2;
        @SuppressLint("DrawAllocation")
        RectF rectF = new RectF(0,0,centerX * 2,centerX * 2);
        //制作描边
        drawStroke(canvas, centerX);
        //制作进展
        drawProgress(canvas, rectF);
    }

  在制作之前首要要确认中心点,由于咱们是一个圆环,实际上也是一个圆,圆的宽高一样,所以中心点的x、y轴的方位就是一样的,然后是确认一个矩形的左上和右下两个方位的坐标点,经过这两个点就能制作一个矩形,接下来就是制作进展条背景。

① 制作描边

    /**
     * 制作描边
     *
     * @param canvas 画布
     * @param centerX 中心点
     */
    private void drawStroke(Canvas canvas, int centerX) {
        Paint paint = new Paint();
        paint.setColor(mProgressColor);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(mStrokeWidth);
        paint.setAntiAlias(true);
        canvas.drawCircle(centerX, centerX, mRadius - (mStrokeWidth / 2), paint);
    }

  这儿的关键就是咱们需求设置画笔的类型为描边,然后设置描边宽度,这样咱们就能够画一个空心圆,就成了描边,然后咱们制作进展。

① 制作进展

    /**
     * 制作进展条背景
     */
    private void drawProgress(Canvas canvas, RectF rectF) {
        Paint paint = new Paint();
        //画笔的填充款式,Paint.Style.STROKE 描边
        paint.setStyle(Paint.Style.FILL);
        //抗锯齿
        paint.setAntiAlias(true);
        //画笔的色彩
        paint.setColor(mProgressColor);
        //是否设置突变
        if (isGradient && colorArray != null) {
            paint.setShader(new RadialGradient(rectF.centerX(), rectF.centerY(), mRadius, colorArray, null, Shader.TileMode.MIRROR));
        }
        if (!isAnimation) {
            mCurrentAngle = 360 * (mCurrentProgress / mMaxProgress);
        }
        //开端画圆弧
        canvas.drawArc(rectF, mStartAngle, mCurrentAngle, true, paint);
    }

  由于背景是一个圆环,所以这儿的画笔设置就比较留意一些,看一下就会了,这儿最重要的是drawArc,用于制作及视点圆,像下图这样,画了4/1的进展,同时添加是否突变的设置,这儿的开端视点是动态的。

Android 自定义View 之 饼状进度条

五、API办法

  还需求供给一些办法在代码中调用,下面是这些办法的代码:

    /**
     * 设置视点
     * @param angle 视点
     */
    public void setCustomAngle(int angle) {
        if (angle >= 0 && angle < 90) {
            mStartAngle = 0;
        } else if (angle >= 90 && angle < 180) {
            mStartAngle = 90;
        } else if (angle >= 180 && angle < 270) {
            mStartAngle = 180;
        } else if (angle >= 270 && angle < 360) {
            mStartAngle = 270;
        } else if (angle >= 360) {
            mStartAngle = 0;
        }
        invalidate();
    }
    /**
     * 设置是否突变
     */
    public void setGradient(boolean gradient) {
        isGradient = gradient;
        invalidate();
    }
    /**
     * 设置突变的色彩
     */
    public void setColorArray(int[] colorArr) {
        if (colorArr == null) return;
        colorArray = colorArr;
    }
    /**
     * 设置当前进展
     */
    public void setProgress(float progress) {
        if (progress < 0) {
            throw new IllegalArgumentException("Progress value can not be less than 0");
        }
        if (progress > mMaxProgress) {
            progress = mMaxProgress;
        }
        mCurrentProgress = progress;
        mCurrentAngle = 360 * (mCurrentProgress / mMaxProgress);
        setAnimator(mStartAngle, mCurrentAngle);
    }
    /**
     * 设置动画
     *
     * @param start  开端方位
     * @param target 完毕方位
     */
    private void setAnimator(float start, float target) {
        isAnimation = true;
        ValueAnimator animator = ValueAnimator.ofFloat(start, target);
        animator.setDuration(mDuration);
        animator.setTarget(mCurrentAngle);
        //动画更新监听
        animator.addUpdateListener(valueAnimator -> {
            mCurrentAngle = (float) valueAnimator.getAnimatedValue();
            invalidate();
        });
        animator.start();
    }

  那么到此为止这个自定义View就完成了,下面咱们能够在PieProgressBarActivity中使用了。

六、使用

   关于使用,我在写这个文章的时分这个自定义View现已加入到仓库中了,能够经过引进依靠的方法,例如在app模块中使用,则打开app模块下的build.gradle,在dependencies{}闭包下添加即可,之后记得要Sync Now

dependencies {
    implementation 'io.github.lilongweidev:easyview:1.0.4'
}

   或者你在自己的项目中完成了方才上述的一切过程,那么你就不用引进依靠了,直接调用就好了,不过要留意更改对应的包名,否则会爆红的。

  先修正activity_pie_progress_bar.xml的代码,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:gravity="center"
    android:orientation="vertical"
    tools:context=".used.PieProgressBarActivity">
    <com.easy.view.PieProgressBar
        android:id="@+id/progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:customAngle="right"
        app:gradient="false"
        app:gradientColorArray="@array/color"
        app:maxProgress="100"
        app:progress="5"
        app:progressbarColor="@color/green"
        app:radius="80dp" />
    <CheckBox
        android:id="@+id/cb_gradient"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="是否突变" />
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="开端视点:"
            android:textColor="@color/black" />
        <RadioGroup
            android:id="@+id/rg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
            <RadioButton
                android:id="@+id/rb_0"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="true"
                android:text="0%" />
            <RadioButton
                android:id="@+id/rb_90"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="90%" />
            <RadioButton
                android:id="@+id/rb_180"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="180%" />
            <RadioButton
                android:id="@+id/rb_270"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="270%" />
        </RadioGroup>
    </LinearLayout>
    <Button
        android:id="@+id/btn_set_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="随机设置进展" />
    <Button
        android:id="@+id/btn_set_progress_0"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="设置0%进展" />
    <Button
        android:id="@+id/btn_set_progress_100"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="设置100%进展" />
</LinearLayout>

在strings.xml中添加突变色,代码如下:

    <string-array name="color">
        <item>#00FFF7</item>
        <item>#FFDD00</item>
        <item>#FF0000</item>
    </string-array>

首要要留意看是否能够预览,我这儿是能够预览的,如下图所示:

Android 自定义View 之 饼状进度条

PieProgressBarActivity中使用,如下所示:

public class PieProgressBarActivity extends EasyActivity<ActivityPieProgressBarBinding> {
    @SuppressLint("NonConstantResourceId")
    @Override
    protected void onCreate() {
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        //是否突变
        binding.cbGradient.setOnCheckedChangeListener((buttonView, isChecked) -> {
            binding.cbGradient.setText(isChecked ? "突变" : "不突变");
            binding.progress.setGradient(isChecked);
        });
        //开端视点
        binding.rg.setOnCheckedChangeListener((group, checkedId) -> {
            int angle = 0;
            switch (checkedId) {
                case R.id.rb_0:
                    angle = 0;
                    break;
                case R.id.rb_90:
                    angle = 90;
                    break;
                case R.id.rb_180:
                    angle = 180;
                    break;
                case R.id.rb_270:
                    angle = 270;
                    break;
            }
            binding.progress.setCustomAngle(angle);
        });
        //设置随机进展值
        binding.btnSetProgress.setOnClickListener(v -> {
            int progress = Math.abs(new Random().nextInt() % 100);
            Toast.makeText(this, "" + progress, Toast.LENGTH_SHORT).show();
            binding.progress.setProgress(progress);
        });
        //设置0%进展值
        binding.btnSetProgress0.setOnClickListener(v -> binding.progress.setProgress(0));
        //设置100%进展值
        binding.btnSetProgress100.setOnClickListener(v -> binding.progress.setProgress(100));
    }
}

运行作用如下图所示:

Android 自定义View 之 饼状进度条

七、源码

假如对你有所协助的话,不妨 Star 或 Fork,天长地久,后会有期~

源码地址:EasyView