前语
许多时候我们会运用发展条,而Android默认的发展条是长条的,从左至右。而在日常开发中,有时候UI为了让页面更美丽,就需求用到圆环发展条,那么本文就是通过自定义写一个圆环发展条,首要看一下作用图:
正文
关于自定义View的基础知识就不再做过多的讲解了,我们直接进入正题,这一次我们不需求再去创建项目了,就用我之前创建的EasyView。
一、XML样式
根据上面的作用图,我们首要来承认XML中的特色样式,批改attrs.xml的代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--文字颜色-->
<attr name="textColor" format="color|reference" />
<!--文字大小-->
<attr name="textSize" format="dimension" />
<!--蓝牙地址输入控件-->
<declare-styleable name="MacAddressEditText">
<!-- 方框大小,宽高一起 -->
<attr name="boxWidth" format="dimension" />
<!-- 方框布景颜色 -->
<attr name="boxBackgroundColor" format="color|reference" />
<!-- 方框描边颜色 -->
<attr name="boxStrokeColor" format="color|reference" />
<!-- 方框描边宽度 -->
<attr name="boxStrokeWidth" format="dimension" />
<!--文字颜色-->
<attr name="textColor" />
<!--文字大小-->
<attr name="textSize" />
<!--分隔符,: 、- -->
<attr name="separator" format="string|reference" />
</declare-styleable>
<!--圆形发展条控件-->
<declare-styleable name="CircularProgressBar">
<!--半径-->
<attr name="radius" format="dimension" />
<!--发展条宽度-->
<attr name="strokeWidth" format="dimension" />
<!--发展条布景颜色-->
<attr name="progressbarBackgroundColor" format="color|reference" />
<!--发展条发展颜色-->
<attr name="progressbarColor" format="color|reference" />
<!--最大发展-->
<attr name="maxProgress" format="integer" />
<!--其时发展-->
<attr name="progress" format="integer" />
<!--文字-->
<attr name="text" format="string" />
<!--文字颜色-->
<attr name="textColor" />
<!--文字大小-->
<attr name="textSize" />
</declare-styleable>
</resources>
这儿你会发现一个改动,那就是文字颜色和文字大小的特色从之前的declare-styleable
中抽出来了,因为我们或许多个自定义控件会用到同样的特色,那么根据特色不可重名的原则,我们需求抽离出来,然后在declare-styleable
引用。
二、结构方法
现在特色样式已经有了,下一步就是写自定义View的结构方法了,在com.llw.easyview
包下新建一个CircularProgressBar
类,里边的代码如下所示:
public class CircularProgressBar extends View {
/**
* 半径
*/
private int mRadius;
/**
* 发展条宽度
*/
private int mStrokeWidth;
/**
* 发展条布景颜色
*/
private int mProgressbarBgColor;
/**
* 发展条发展颜色
*/
private int mProgressColor;
/**
* 初步角度
*/
private int mStartAngle = 0;
/**
* 其时角度
*/
private float mCurrentAngle = 0;
/**
* 结束角度
*/
private int mEndAngle = 360;
/**
* 最大发展
*/
private float mMaxProgress;
/**
* 其时发展
*/
private float mCurrentProgress;
/**
* 文字
*/
private String mText;
/**
* 文字颜色
*/
private int mTextColor;
/**
* 文字大小
*/
private float mTextSize;
/**
* 动画的实行时长
*/
private long mDuration = 1000;
/**
* 是否实行动画
*/
private boolean isAnimation = false;
public CircularProgressBar(Context context) {
this(context, null);
}
public CircularProgressBar(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CircularProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CircularProgressBar);
mRadius = array.getDimensionPixelSize(R.styleable.CircularProgressBar_radius, 80);
mStrokeWidth = array.getDimensionPixelSize(R.styleable.CircularProgressBar_strokeWidth, 8);
mProgressbarBgColor = array.getColor(R.styleable.CircularProgressBar_progressbarBackgroundColor, ContextCompat.getColor(context, R.color.teal_700));
mProgressColor = array.getColor(R.styleable.CircularProgressBar_progressbarColor, ContextCompat.getColor(context, R.color.teal_200));
mMaxProgress = array.getInt(R.styleable.CircularProgressBar_maxProgress, 100);
mCurrentProgress = array.getInt(R.styleable.CircularProgressBar_progress, 0);
String text = array.getString(R.styleable.CircularProgressBar_text);
mText = text == null ? "" : text;
mTextColor = array.getColor(R.styleable.CircularProgressBar_textColor, ContextCompat.getColor(context, R.color.black));
mTextSize = array.getDimensionPixelSize(R.styleable.CircularProgressBar_textSize, (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_SP, 14, getResources().getDisplayMetrics()));
array.recycle();
}
}
这儿声明了一些变量,然后写了3个结构方法,在第三个结构方法中进行特色的赋值。
三、测量
这儿测量就比较简单了,当然这是相对于之前的那个Mac地址输入框来说的,代码如下所示:
@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;
RectF rectF = new RectF();
rectF.left = mStrokeWidth;
rectF.top = mStrokeWidth;
rectF.right = centerX * 2 - mStrokeWidth;
rectF.bottom = centerX * 2 - mStrokeWidth;
//制造发展条布景
drawProgressbarBg(canvas, rectF);
//制造发展
drawProgress(canvas, rectF);
//制造中心文本
drawCenterText(canvas, centerX);
}
在制造之前首要要承认中心点,因为我们是一个圆环,实际上也是一个圆,圆的宽高相同,所以中心点的x、y轴的方位就是相同的,然后是承认一个矩形的左上和右下两个方位的坐标点,通过这两个点就能制造一个矩形,接下来就是制造发展条布景。
① 制造发展条布景
/**
* 制造发展条布景
*/
private void drawProgressbarBg(Canvas canvas, RectF rectF) {
Paint mPaint = new Paint();
//画笔的填充样式,Paint.Style.STROKE 描边
mPaint.setStyle(Paint.Style.STROKE);
//圆弧的宽度
mPaint.setStrokeWidth(mStrokeWidth);
//抗锯齿
mPaint.setAntiAlias(true);
//画笔的颜色
mPaint.setColor(mProgressbarBgColor);
//画笔的样式 Paint.Cap.Round 圆形
mPaint.setStrokeCap(Paint.Cap.ROUND);
//初步画圆弧
canvas.drawArc(rectF, mStartAngle, mEndAngle, false, mPaint);
}
因为布景是一个圆环,所以这儿的画笔设置就比较留心一些,看一下就会了,这儿最重要的是drawArc
,用于制造圆弧,像下图这样,画了4/1的布景。
下面制造发展
② 制造发展
/**
* 制造发展
*/
private void drawProgress(Canvas canvas, RectF rectF) {
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(mStrokeWidth);
paint.setColor(mProgressColor);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
if (!isAnimation) {
mCurrentAngle = 360 * (mCurrentProgress / mMaxProgress);
}
canvas.drawArc(rectF, mStartAngle, mCurrentAngle, false, paint);
}
这儿的发展值就要根据其时的参数进行处理了,这儿有一个变量进行判别处理,首要作用就是是否进行动画制造。
③ 制造文字
/**
* 制造中心文字
*/
private void drawCenterText(Canvas canvas, int centerX) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(mTextColor);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(mTextSize);
Rect textBounds = new Rect();
paint.getTextBounds(mText, 0, mText.length(), textBounds);
canvas.drawText(mText, centerX, textBounds.height() / 2 + getHeight() / 2, paint);
}
制造文字的规则仍是和之前相同。
五、API方法
还需求供给一些方法在代码中调用,下面是这些方法的代码:
/**
* 设置其时发展
*/
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(0, mCurrentAngle);
}
/**
* 设置文本
*/
public void setText(String text) {
mText = text;
}
/**
* 设置文本的颜色
*/
public void setTextColor(int color) {
if (color <= 0) {
throw new IllegalArgumentException("Color value can not be less than 0");
}
mTextColor = color;
}
/**
* 设置文本的大小
*/
public void setTextSize(float textSize) {
if (textSize <= 0) {
throw new IllegalArgumentException("textSize can not be less than 0");
}
mTextSize = textSize;
}
/**
* 设置动画
*
* @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就完成了,下面我们能够在MainActivity中运用了。
六、运用
先批改activity_main.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"
android:padding="16dp"
tools:context=".MainActivity">
<com.easy.view.MacAddressEditText
android:id="@+id/mac_et"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:boxBackgroundColor="@color/white"
app:boxStrokeColor="@color/black"
app:boxStrokeWidth="2dp"
app:boxWidth="48dp"
app:separator=":"
app:textColor="@color/black"
app:textSize="16sp" />
<Button
android:id="@+id/btn_mac"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="获取地址" />
<com.easy.view.CircularProgressBar
android:id="@+id/cpb_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
app:maxProgress="100"
app:progress="10"
app:progressbarBackgroundColor="@color/purple_500"
app:progressbarColor="@color/purple_200"
app:radius="80dp"
app:strokeWidth="16dp"
app:text="10%"
app:textColor="@color/teal_200"
app:textSize="28sp" />
<Button
android:id="@+id/btn_set_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="随机设置发展" />
</LinearLayout>
首要要留心看是否能够预览,我这儿是能够预览的,如下图所示:
在MainActivity中运用,批改onCreate()
方法中的代码,如下所示:
//圆形发展条操作
CircularProgressBar cpbTest = findViewById(R.id.cpb_test);
Button btnSetProgress = findViewById(R.id.btn_set_progress);
btnSetProgress.setOnClickListener(v -> {
int progress = Math.abs(new Random().nextInt() % 100);
Toast.makeText(this, "" + progress, Toast.LENGTH_SHORT).show();
cpbTest.setText(progress + "%");
cpbTest.setProgress(progress);
});
运行作用如下图所示:
七、源码
趁便说一下,我方案把这个项目做成一个开源库房,提交到mavenCentral()
中,后边运用的话就能够通过这个引进依托的方式进行调用,会很便利,后边会单独出一篇文章叙说这个库房。
如果对你有所帮助的话,无妨 Star 或 Fork,天长地久,后会有期~
源码地址:EasyView