当前位置: 首页 > news >正文

可以做卷子的网站办理培训机构需要具备的条件

可以做卷子的网站,办理培训机构需要具备的条件,安平网站建设培训,南宁app开发公司哪个好很多场景下都用到这种进度条,有的还带动画效果, 今天我也来写一个。 写之前先拆解下它的组成: 底层圆形上层弧形中间文字 那我们要做的就是: 绘制底层圆形;在同位置绘制上层弧形,但颜色不同&#xff…

很多场景下都用到这种进度条,有的还带动画效果,
在这里插入图片描述

今天我也来写一个。

写之前先拆解下它的组成:

  • 底层圆形
  • 上层弧形
  • 中间文字

那我们要做的就是:

  1. 绘制底层圆形;
  2. 在同位置绘制上层弧形,但颜色不同;
  3. 在中心点绘制文本,显示进度。

按照这个目标,学习下自定义View的流程。

1.基础

新建一个类,继承 View ,重写构造函数,如,

package com.test.luodemo.customerview;import android.content.Context;
import android.util.AttributeSet;
import android.view.View;import androidx.annotation.Nullable;public class CircleProgressBar extends View {public CircleProgressBar(Context context) {super(context);}public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);}public CircleProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}
}

在 xml 中使用,LinearLayout 加了背景颜色,方便看出所在位置。

<LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:layout_width="300dp"android:layout_height="300dp"/></LinearLayout>

此时运行,是没效果的,因为这个View还没有绘制,啥也没有。

2.绘制底层圆形

初始化3个图形的画笔 ,底层圆形和上层弧形的画笔宽度一致、颜色不一致,方便区分

重写 onDraw(Canvas canvas) 方法,用 canvas.drawCircle 绘制底层圆形,

package com.test.luodemo.customerview;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;import androidx.annotation.Nullable;public class CircleProgressBar extends View {private Paint paintCircleBottom = new Paint();private Paint paintArcTop = new Paint();private Paint paintText = new Paint();public CircleProgressBar(Context context) {super(context);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){//初始化文本的画笔paintText.setFlags(Paint.ANTI_ALIAS_FLAG);paintText.setColor(Color.BLACK);paintText.setTextAlign(Paint.Align.CENTER);paintText.setTextSize(80f);//初始化底层圆形的画笔paintCircleBottom.setFlags(Paint.ANTI_ALIAS_FLAG);paintCircleBottom.setColor(Color.LTGRAY);paintCircleBottom.setStrokeWidth(10f);paintCircleBottom.setStrokeCap(Paint.Cap.ROUND);paintCircleBottom.setStyle(Paint.Style.STROKE);//初始化弧形的画笔paintArcTop.setFlags(Paint.ANTI_ALIAS_FLAG);paintArcTop.setColor(Color.MAGENTA);paintArcTop.setStrokeWidth(10f);paintArcTop.setStrokeCap(Paint.Cap.ROUND);paintArcTop.setStyle(Paint.Style.STROKE);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆形canvas.drawCircle(300, 300, 200, paintCircleBottom);}
}

效果,
在这里插入图片描述

3.绘制上层弧形

在之前的基础上绘制上层弧形,弧形的中心和圆心一致。

用 canvas.drawArc 绘制弧形。这里直接指定绘制的角度是 90° ,后续会动态指定。

	@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆形canvas.drawCircle( 300, 300, 200, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°_angle = 90;canvas.drawArc(100,100,500,500,270, _angle,false, paintArcTop);}

效果,
在这里插入图片描述

4.绘制文本

用 canvas.drawText 绘制文本,

使用 DecimalFormat 格式化输入,保留小数点后两位,如果小数点后两位都是0则不显示小数点后两位。

	@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆形canvas.drawCircle(300, 300, 200, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°_angle = 90;canvas.drawArc(100,100,500,500,270, _angle,false, paintArcTop);//绘制文本DecimalFormat dt = new DecimalFormat("0.##");canvas.drawText(dt.format(100 * _angle/360)+"%", 300 , 300, paintText);}

效果,
在这里插入图片描述

可以看到,文本虽然居中,但是文本是显示在中心线上,
在这里插入图片描述

期望结果是文本的水平中心线和圆心重合,改为,

		//绘制文本,文字中心和圆心保持一致Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= 300 + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", 300, baseline, paintText);//文字中心和圆心一致        

效果,复合预期。
在这里插入图片描述

5.添加动画

创建一个设置进度的接口,供外部调用。

使用 ValueAnimator ,监听动画过程,然后逐渐刷新角度值。使用 AccelerateInterpolator 插值器,动画速度开始慢、逐渐加速。

	@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//绘制底层圆形canvas.drawCircle(300, 300, 200, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°canvas.drawArc(100,100,500,500,270, _angle,false, paintArcTop);//绘制文本,文字中心和圆心保持一致DecimalFormat dt = new DecimalFormat("0.##");Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= 300 + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", 300, baseline, paintText);//文字中心和圆心一致}/*** 设置进度,展现动画* */public void setProgress(int progress){ValueAnimator animator = ValueAnimator.ofFloat(0,100f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float cur = (float) animation.getAnimatedValue();_angle = cur/100 * 360 * progress/100;invalidate(); //刷新 View}});animator.setDuration(3000);animator.setInterpolator(new AccelerateInterpolator());animator.start();}

注意要去掉 3.绘制上层弧形 中固定90°的逻辑。

外部调用,

CircleProgressBar mCircleProgressBar1 = (CircleProgressBar) findViewById(R.id.circle_progress_bar1);
mCircleProgressBar1.setProgress((int) (100 * Math.random()));

随机生成一个 0.0 - 0.1 的数值,乘以 100 设置为进度。
效果,
在这里插入图片描述
可以看到动画效果, 虽然 git 丢帧了 ~ 。

6.调整位置、宽高

前文我是设定了 View 宽高都是 300dp ,并且绘制图形是随意指定的坐标。

实际开发时,不可能用这些值,所以要优化下绘制的逻辑。

实际使用时,可能宽度高度一样,宽度大于高度 ,宽度小于高度,

采用这个逻辑:

  • 取宽度、高度的最小值,作为圆的直径,除以 2 得到半径。
  • 对角线交汇点作为圆心。

简言之,以对角线为圆心画最大内切圆。

重写 onMeasure 方法,重绘 View 的宽高,这部分参考《Android 开发艺术探索》,

	private int DEFAULT_WIDTH = 100;//默认宽度private int DEFAULT_HEIGHT = 100;//默认宽度private int DEFAULT_RADIUS = 50;//默认半径@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);} else if (widthMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, heightMeasureSpec);} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthMeasureSpec, DEFAULT_HEIGHT);}}

修改 onDraw 绘制逻辑 ,

	@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 圆心坐标是(centerX,centerY)int centerX = getWidth()/2;int centerY = getHeight()/2;//确定半径float radius = Math.min(centerX, centerY) - paintCircleBottom.getStrokeWidth();//绘制底层圆形canvas.drawCircle(centerX, centerY, radius, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走 _anglecanvas.drawArc(centerX - radius,centerY-radius,centerX + radius,centerY + radius,270, _angle,false, paintArcTop);//绘制文本,文字中心和圆心保持一致Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= centerY + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", centerX, baseline, paintText);//文字中心和圆心一致}

分别写了 3 个布局,布局依次是 宽度等于高度 、宽度大宇高度、宽度小于高度,效果,
在这里插入图片描述
至此,基本是一个还可以的版本了。

附代码

贴下当前代码,

CircleProgressBar.java

package com.test.luodemo.customerview;import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;import androidx.annotation.Nullable;import java.text.DecimalFormat;public class CircleProgressBar extends View {private Paint paintCircleBottom = new Paint();private Paint paintArcTop = new Paint();private Paint paintText = new Paint();private int DEFAULT_WIDTH = 100;//默认宽度private int DEFAULT_HEIGHT = 100;//默认宽度private int DEFAULT_RADIUS = 50;//默认半径private float _angle;//弧形的角度public CircleProgressBar(Context context) {super(context);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){//初始化文本的画笔paintText.setFlags(Paint.ANTI_ALIAS_FLAG);paintText.setColor(Color.BLACK);paintText.setTextAlign(Paint.Align.CENTER);paintText.setTextSize(80f);//初始化底层圆形的画笔paintCircleBottom.setFlags(Paint.ANTI_ALIAS_FLAG);paintCircleBottom.setColor(Color.LTGRAY);paintCircleBottom.setStrokeWidth(10f);paintCircleBottom.setStrokeCap(Paint.Cap.ROUND);paintCircleBottom.setStyle(Paint.Style.STROKE);//初始化弧形的画笔paintArcTop.setFlags(Paint.ANTI_ALIAS_FLAG);paintArcTop.setColor(Color.MAGENTA);paintArcTop.setStrokeWidth(10f);paintArcTop.setStrokeCap(Paint.Cap.ROUND);paintArcTop.setStyle(Paint.Style.STROKE);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);} else if (widthMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, heightMeasureSpec);} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthMeasureSpec, DEFAULT_HEIGHT);}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 圆心坐标是(centerX,centerY)int centerX = getWidth()/2;int centerY = getHeight()/2;//确定半径float radius = Math.min(centerX, centerY) - paintCircleBottom.getStrokeWidth();//绘制底层圆形canvas.drawCircle(centerX, centerY, radius, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°canvas.drawArc(centerX - radius,centerY-radius,centerX + radius,centerY + radius,270, _angle,false, paintArcTop);//绘制文本,文字中心和圆心保持一致DecimalFormat dt = new DecimalFormat("0.##");Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= centerY + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", centerX, baseline, paintText);//文字中心和圆心一致}/*** 设置进度,展现动画* */public void setProgress(int progress){ValueAnimator animator = ValueAnimator.ofFloat(0,100f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float cur = (float) animation.getAnimatedValue();_angle = cur/100 * 360 * progress/100;invalidate();}});animator.setDuration(3000);animator.setInterpolator(new AccelerateInterpolator());animator.start();}
}

布局文件

<?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:orientation="vertical"tools:context=".customerview.CircleProgressBarActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar1"android:layout_width="300dp"android:layout_height="300dp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/teal_200"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar2"android:layout_width="300dp"android:layout_height="200dp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/teal_700"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar3"android:layout_width="200dp"android:layout_height="300dp" /></LinearLayout><!--<LinearLayoutandroid:layout_width="50dp"android:layout_height="70dp"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"/></LinearLayout>--></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:visibility="visible"><Buttonandroid:id="@+id/button_cpb1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button1" /><Buttonandroid:id="@+id/button_cpb2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button2" /><Buttonandroid:id="@+id/button_cpb3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button3" /><Buttonandroid:id="@+id/button_cpb_all"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button All" /></LinearLayout></LinearLayout>

Activity 调用

public class CircleProgressBarActivity extends AppCompatActivity {private CircleProgressBar mCircleProgressBar1 , mCircleProgressBar2 , mCircleProgressBar3;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_circle_progress_bar);Objects.requireNonNull(getSupportActionBar()).setTitle("CircleProgressBarActivity");mCircleProgressBar1 = (CircleProgressBar) findViewById(R.id.circle_progress_bar1);mCircleProgressBar2 = (CircleProgressBar) findViewById(R.id.circle_progress_bar2);mCircleProgressBar3 = (CircleProgressBar) findViewById(R.id.circle_progress_bar3);}public void onCPBButtonClick(View view) {switch (view.getId()) {case R.id.button_cpb1:mCircleProgressBar1.setProgress((int) (100 * Math.random()));break;case R.id.button_cpb2:mCircleProgressBar2.setProgress((int) (100 * Math.random()));break;case R.id.button_cpb3:mCircleProgressBar3.setProgress((int) (100 * Math.random()));break;case R.id.button_cpb_all:mCircleProgressBar1.setProgress((int) (100 * Math.random()));mCircleProgressBar2.setProgress((int) (100 * Math.random()));mCircleProgressBar3.setProgress((int) (100 * Math.random()));break;default:break;}}
}

7.自定义属性 attr

需求是不停的,会有这些需求:可指定画笔(宽度、颜色等)、可指定动画时长等。

这些可以通过在自定义的View中创建 Java 接口来设置,但我要学自定义View,就要用 attr

7.1 创建 res/values/attrs.xml

如果已有就不用创建,直接用就行了。

写入如下内容,

<?xml version="1.0" encoding="utf-8"?>
<resources><!-- 圆形进度条 --><declare-styleable name="CircleProgressBar"><attr name="circleWidth" format="float" /> <!--底层圆形宽度--><attr name="circleColor" format="color" /> <!--底层圆形颜色--><attr name="arcWidth" format="float" /> <!--上层弧形宽度--><attr name="arcColor" format="color" /><!--上层弧形颜色--><attr name="textColor" format="color" /><!--文本颜色--><attr name="textSize" format="float" /><!--文本字体大小--><attr name="initProgress" format="integer" /><!--进度--></declare-styleable>
</resources>

<declare-styleable name="CircleProgressBar"> 中 CircleProgressBar 就是自定义 View 的名字,要保持一致。

不一致AS会报黄,

By convention, the custom view (CircleProgressBar) and the declare-styleable (CircleProgressBar111) should have the same name (various editor features rely on this convention)

<attr name="circleWidth" format="float" /> 是 CircleProgressBar 的属性,可指定类型

类型说明
boolean布尔类型,true 或 false
color颜色值,如 @android:color/white
dimensiondp 值,如 20dp
enum枚举
flags位或运算,如 app:cus_view_gravity=“top|right”
fraction百分比,如 30%
floatfloat 型
integerint 型
reference引用资源,如 @drawable/pic
string字符串

7.2 使用 TypedArray 获取 attrs

在构造函数中,通过 TypedArray 获取自定义的属性。基本逻辑就是有设置 attr 就用设置的值,没有就用默认值。

使用后一定要调用 TypedArray.recycle();

	public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar);textColor = typedArray.getColor(R.styleable.CircleProgressBar_textColor, Color.BLACK);textSize = typedArray.getFloat(R.styleable.CircleProgressBar_textSize, 80f);circleColor = typedArray.getColor(R.styleable.CircleProgressBar_circleColor, Color.LTGRAY);circleWidth = typedArray.getFloat(R.styleable.CircleProgressBar_circleWidth, 10f);arcColor = typedArray.getColor(R.styleable.CircleProgressBar_arcColor, Color.MAGENTA);arcWidth = typedArray.getFloat(R.styleable.CircleProgressBar_arcWidth, 10f);progress = typedArray.getInt(R.styleable.CircleProgressBar_initProgress, 0);typedArray.recycle();init();}

有两个带 AttributeSet 参数的构造函数,

  • public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {}
  • public CircleProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {}

为什么用后面这个? 因为我们是在 xml 中定义的 CircleProgressBar 。参考源码说明,

	/*** Constructor that is called when inflating a view from XML. This is called* when a view is being constructed from an XML file, supplying attributes* that were specified in the XML file. This version uses a default style of* 0, so the only attribute values applied are those in the Context's Theme* and the given AttributeSet.** <p>* The method onFinishInflate() will be called after all children have been* added.** @param context The Context the view is running in, through which it can*        access the current theme, resources, etc.* @param attrs The attributes of the XML tag that is inflating the view.* @see #View(Context, AttributeSet, int)*/public View(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}/*** Perform inflation from XML and apply a class-specific base style from a* theme attribute. This constructor of View allows subclasses to use their* own base style when they are inflating. For example, a Button class's* constructor would call this version of the super class constructor and* supply <code>R.attr.buttonStyle</code> for <var>defStyleAttr</var>; this* allows the theme's button style to modify all of the base view attributes* (in particular its background) as well as the Button class's attributes.** @param context The Context the view is running in, through which it can*        access the current theme, resources, etc.* @param attrs The attributes of the XML tag that is inflating the view.* @param defStyleAttr An attribute in the current theme that contains a*        reference to a style resource that supplies default values for*        the view. Can be 0 to not look for defaults.* @see #View(Context, AttributeSet)*/public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}

7.3 在 xml 中初始化 attr

xml 关键代码如下,

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto" <!-- 注释1-->	xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".customerview.CircleProgressBarActivity"><!-- ... -->	<LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar1"android:layout_width="300dp"android:layout_height="300dp"<!-- 注释2-->	app:circleColor="@android:color/white"app:circleWidth="30"app:arcColor="@color/my_red"app:arcWidth="15"app:textColor="@android:color/holo_orange_dark"app:initProgress="30"<!-- 注释2-->/></LinearLayout><!-- ... -->
</LinearLayout>

注释2处就是初始化 attr ,以为 app: 开头是对应注释1处。

7.4 效果

左一是自定义 attr 的效果,左二、左三是没有自定义 attr 的效果。
差异有:底层圆形的颜色、画笔大小;上层弧形的颜色、画笔大小、开始的角度;中间文字的颜色。
说明自定义 attr 起效了。
在这里插入图片描述

附代码V2

CircleProgressBar.java

package com.test.luodemo.customerview;import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;import androidx.annotation.Nullable;import com.test.luodemo.R;import java.text.DecimalFormat;public class CircleProgressBar extends View {private Paint paintCircleBottom = new Paint();private Paint paintArcTop = new Paint();private Paint paintText = new Paint();private int DEFAULT_WIDTH = 100;//默认宽度private int DEFAULT_HEIGHT = 100;//默认宽度private int DEFAULT_RADIUS = 50;//默认半径private float _angle;//弧形的角度/***************************** attr *******************************/int textColor;float textSize;int circleColor ;int arcColor;float circleWidth;float arcWidth;int progress;/***************************** attr *******************************/public CircleProgressBar(Context context) {super(context);init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar);textColor = typedArray.getColor(R.styleable.CircleProgressBar_textColor, Color.BLACK);textSize = typedArray.getFloat(R.styleable.CircleProgressBar_textSize, 80f);circleColor = typedArray.getColor(R.styleable.CircleProgressBar_circleColor, Color.LTGRAY);circleWidth = typedArray.getFloat(R.styleable.CircleProgressBar_circleWidth, 10f);arcColor = typedArray.getColor(R.styleable.CircleProgressBar_arcColor, Color.MAGENTA);arcWidth = typedArray.getFloat(R.styleable.CircleProgressBar_arcWidth, 10f);progress = typedArray.getInt(R.styleable.CircleProgressBar_initProgress, 0);typedArray.recycle();init();}public CircleProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init(){//初始化文本的画笔paintText.setFlags(Paint.ANTI_ALIAS_FLAG);paintText.setStyle(Paint.Style.FILL);paintText.setColor(textColor);//设置自定义属性值paintText.setTextAlign(Paint.Align.CENTER);paintText.setTextSize(textSize);//初始化底层圆形的画笔paintCircleBottom.setFlags(Paint.ANTI_ALIAS_FLAG);paintCircleBottom.setStrokeCap(Paint.Cap.ROUND);paintCircleBottom.setStyle(Paint.Style.STROKE);paintCircleBottom.setColor(circleColor);//设置自定义属性值paintCircleBottom.setStrokeWidth(circleWidth);//设置自定义属性值//初始化弧形的画笔paintArcTop.setFlags(Paint.ANTI_ALIAS_FLAG);paintArcTop.setStrokeCap(Paint.Cap.ROUND);paintArcTop.setStyle(Paint.Style.STROKE);paintArcTop.setColor(arcColor);//设置自定义属性值paintArcTop.setStrokeWidth(arcWidth);//设置自定义属性值_angle = progress;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, DEFAULT_HEIGHT);} else if (widthMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WIDTH, heightMeasureSpec);} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthMeasureSpec, DEFAULT_HEIGHT);}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 圆心坐标是(centerX,centerY)int centerX = getWidth()/2;int centerY = getHeight()/2;//确定半径float radius = Math.min(centerX, centerY) - paintCircleBottom.getStrokeWidth();//绘制底层圆形canvas.drawCircle(centerX, centerY, radius, paintCircleBottom);//绘制上层弧形,从顶部开始,顺时针走90°canvas.drawArc(centerX - radius,centerY-radius,centerX + radius,centerY + radius,270, _angle,false, paintArcTop);//绘制文本,文字中心和圆心保持一致DecimalFormat dt = new DecimalFormat("0.##");Paint.FontMetrics fontMetrics = paintText.getFontMetrics();float distance =(fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;float baseline= centerY + distance;canvas.drawText(dt.format(100 * _angle/360)+"%", centerX, baseline, paintText);//文字中心和圆心一致}/*** 设置进度,展现动画* */public void setProgress(int progress){ValueAnimator animator = ValueAnimator.ofFloat(0,100f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float cur = (float) animation.getAnimatedValue();_angle = cur/100 * 360 * progress/100;invalidate();}});animator.setDuration(3000);animator.setInterpolator(new AccelerateInterpolator());animator.start();}
}

布局文件

<?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:orientation="vertical"tools:context=".customerview.CircleProgressBarActivity"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/purple_200"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar1"android:layout_width="300dp"android:layout_height="300dp"app:circleColor="@android:color/white"app:circleWidth="30"app:arcColor="@color/my_red"app:arcWidth="15"app:textColor="@android:color/holo_orange_dark"app:initProgress="30"/></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/teal_200"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar2"android:layout_width="300dp"android:layout_height="200dp" /></LinearLayout><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/teal_700"><com.test.luodemo.customerview.CircleProgressBarandroid:id="@+id/circle_progress_bar3"android:layout_width="200dp"android:layout_height="300dp" /></LinearLayout></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:visibility="visible"><Buttonandroid:id="@+id/button_cpb1"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button1" /><Buttonandroid:id="@+id/button_cpb2"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button2" /><Buttonandroid:id="@+id/button_cpb3"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button3" /><Buttonandroid:id="@+id/button_cpb_all"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onCPBButtonClick"android:text="Button All" /></LinearLayout></LinearLayout>

Activity 调用

和之前一样。

attrs

<?xml version="1.0" encoding="utf-8"?>
<resources><!-- 圆形进度条 --><declare-styleable name="CircleProgressBar"><attr name="circleWidth" format="float" /> <!--底层圆形宽度--><attr name="circleColor" format="color" /> <!--底层圆形颜色--><attr name="arcWidth" format="float" /> <!--上层弧形宽度--><attr name="arcColor" format="color" /><!--上层弧形颜色--><attr name="textColor" format="color" /><!--文本颜色--><attr name="textSize" format="float" /><!--文本字体大小--><attr name="initProgress" format="integer" /><!--进度--></declare-styleable>
</resources>

参考资料:

Android属性动画深入分析:让你成为动画牛人_singwhatiwanna的博客-CSDN博客
Android Canvas的使用_南国樗里疾的博客-CSDN博客
Android Canvas的drawText()和文字居中方案 - 简书


文章转载自:
http://thromboembolism.dztp.cn
http://reprehension.dztp.cn
http://sconce.dztp.cn
http://irresistible.dztp.cn
http://tessitura.dztp.cn
http://navigational.dztp.cn
http://dichotomise.dztp.cn
http://fabulist.dztp.cn
http://ballasting.dztp.cn
http://nuts.dztp.cn
http://amok.dztp.cn
http://spermatophyte.dztp.cn
http://unassimilable.dztp.cn
http://stonechat.dztp.cn
http://shakuhachi.dztp.cn
http://antiadministration.dztp.cn
http://iron.dztp.cn
http://aslope.dztp.cn
http://scrimp.dztp.cn
http://endoscope.dztp.cn
http://blende.dztp.cn
http://nritta.dztp.cn
http://reefy.dztp.cn
http://trisaccharide.dztp.cn
http://motordrome.dztp.cn
http://spindly.dztp.cn
http://spermatorrhoea.dztp.cn
http://streptodornase.dztp.cn
http://goon.dztp.cn
http://ms.dztp.cn
http://chronometrical.dztp.cn
http://acidfast.dztp.cn
http://pulka.dztp.cn
http://overside.dztp.cn
http://araliaceous.dztp.cn
http://adventurist.dztp.cn
http://sweated.dztp.cn
http://hornwort.dztp.cn
http://edwina.dztp.cn
http://micritic.dztp.cn
http://coverage.dztp.cn
http://lipid.dztp.cn
http://surfmanship.dztp.cn
http://belittle.dztp.cn
http://cpi.dztp.cn
http://jacklight.dztp.cn
http://ginza.dztp.cn
http://immunodiagnosis.dztp.cn
http://cleruch.dztp.cn
http://dingdong.dztp.cn
http://lamellar.dztp.cn
http://fucoxanthin.dztp.cn
http://thatchy.dztp.cn
http://schrank.dztp.cn
http://ruffianlike.dztp.cn
http://reformist.dztp.cn
http://toothful.dztp.cn
http://jaguarondi.dztp.cn
http://supernate.dztp.cn
http://fid.dztp.cn
http://mutably.dztp.cn
http://yep.dztp.cn
http://ability.dztp.cn
http://rumly.dztp.cn
http://musculamine.dztp.cn
http://premed.dztp.cn
http://ox.dztp.cn
http://euripus.dztp.cn
http://bimanous.dztp.cn
http://adenocarcinoma.dztp.cn
http://oeo.dztp.cn
http://loanee.dztp.cn
http://extasy.dztp.cn
http://ichthyomorphic.dztp.cn
http://chamber.dztp.cn
http://suzuribako.dztp.cn
http://bibitory.dztp.cn
http://kickapoo.dztp.cn
http://ratomorphic.dztp.cn
http://blush.dztp.cn
http://unessential.dztp.cn
http://desterilize.dztp.cn
http://eponymous.dztp.cn
http://surprisal.dztp.cn
http://jitterbug.dztp.cn
http://opodeldoc.dztp.cn
http://bitumastic.dztp.cn
http://proficience.dztp.cn
http://sparkplug.dztp.cn
http://preexposure.dztp.cn
http://dissimilitude.dztp.cn
http://blotting.dztp.cn
http://sorel.dztp.cn
http://ruche.dztp.cn
http://absorbedly.dztp.cn
http://rhizoctonia.dztp.cn
http://exoteric.dztp.cn
http://delay.dztp.cn
http://anandrous.dztp.cn
http://tuesdays.dztp.cn
http://www.dt0577.cn/news/66748.html

相关文章:

  • 网站建设珠海哪个软件可以自动排名
  • 最火的网站开发语言常州网站建设制作
  • 湖北响应式网站建设关键词排名代发
  • 竭诚网络网站建设市场调研
  • 有没有手机做任务赚钱的网站seo网站推广简历
  • 做pc端网站方案百度账号
  • 网站开发的app广州网络推广策划公司
  • 订阅号可以做网站链接吗网站推广优化的公司
  • 广元百度做网站多少钱域名注册网
  • 网站核检单定制化网站建设
  • 做赌博游戏网站违法长春网站优化页面
  • 成都网站建设 urkeji甘肃seo技术
  • 移动网站开发基础知识seo推广的特点
  • 下载网站软件免费安装其中包括
  • 抢购网站源码长春百度关键词优化
  • 免费网站开发软件平台深圳网站建设系统
  • 万州哪里有做网站的深圳网络整合营销公司
  • 网站开发用户需求qq群推广方法
  • 移动互联网营销的目标是( )seo优化公司排名
  • 妇女儿童心理咨询网站建设seo1新地址在哪里
  • 丰台网站开发站长工具怎么关闭
  • 青岛建网站选青岛博采网络宁波网站推广优化公司怎么样
  • 手机网站 英文百度官方官网
  • 上海集酷网站上海有哪些优化网站推广公司
  • 凡科做的网站百度不到信息流广告投放渠道
  • 网络科技有限公司属于什么行业文大侠seo
  • 做网站都需要租服务器吗十大广告公司排名
  • 个人业务网站制作站长seo综合查询
  • 专业做网站建设的公司免费站推广网站在线
  • 12306网站是谁做的中国疾控卫生应急服装