本文主演参照前一篇文章打造了一个自定义的进度条,仅作代码记录。
实现效果:
1645079823700.gif
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ProcessBarView">
<attr name="innerColor" format="color"/>
<attr name="outerColor" format="color"/>
<attr name="borderWidth" format="dimension"/>
<attr name="NumTextColor" format="color"/>
<attr name="NumTextSize" format="dimension"/>
</declare-styleable>
</resources>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<com.example.view04.ProcessBarView
app:NumTextSize="22sp"
app:outerColor="#C0C0C0"
android:id="@+id/processBarView"
android:layout_width="200dp"
android:layout_height="200dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:text="开始"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startAnimator"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/processBarView"
tools:ignore="OnClick" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private ProcessBarView mProcessView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProcessView=findViewById(R.id.processBarView);
}
public void startAnimator(View view){
mProcessView.setSweepAngle(0);
ValueAnimator valueAnimator= ObjectAnimator.ofFloat(0,100);
valueAnimator.setDuration(10000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float mSweepAngle= (float) animation.getAnimatedValue();
//保留两位小数
BigDecimal b = new BigDecimal(mSweepAngle);
double f1 = b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
mProcessView.setSweepAngle((float) f1);
}
});
valueAnimator.start();
}
}
ProcessBarView.java
public class ProcessBarView extends View {
private Context mContext;
private int mOutColor;
private int mInnerColor;
private int mBorderWidth;
private int mNumTextSize;
private int mNumTextColor;
private Paint mOutPaint;
private Paint mInnerPaint;
private Paint mTextPaint;
private float mSweepAngle=0;
private int mMaxAngle=100;
public ProcessBarView(Context context) {
this(context, null);
}
public ProcessBarView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ProcessBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ProcessBarView);
mOutColor = typedArray.getColor(R.styleable.ProcessBarView_outerColor, Color.RED);
mInnerColor = typedArray.getColor(R.styleable.ProcessBarView_innerColor, Color.BLUE);
mBorderWidth = (int) typedArray.getDimension(R.styleable.ProcessBarView_borderWidth, 20);
mNumTextColor = typedArray.getColor(R.styleable.ProcessBarView_NumTextColor, Color.RED);
mNumTextSize = typedArray.getDimensionPixelSize(R.styleable.ProcessBarView_NumTextSize, 20);
mOutPaint=new Paint();
mOutPaint.setAntiAlias(true);
mOutPaint.setStrokeWidth(mBorderWidth);
mOutPaint.setColor(mOutColor);
mOutPaint.setStyle(Paint.Style.STROKE);
mOutPaint.setStrokeCap(Paint.Cap.ROUND);
mInnerPaint=new Paint();
mInnerPaint.setAntiAlias(true);
mInnerPaint.setStrokeWidth(mBorderWidth);
mInnerPaint.setColor(mInnerColor);
mInnerPaint.setStyle(Paint.Style.STROKE);
mInnerPaint.setStrokeCap(Paint.Cap.ROUND);
mTextPaint=new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setColor(mNumTextColor);
mTextPaint.setTextSize(mNumTextSize);
typedArray.recycle();
}
public ProcessBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mContext=context;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSize > heightSize ? heightSize : widthSize, widthSize > heightSize ? heightSize : widthSize);
}
@Override
public void draw(Canvas canvas) {
//画外圆
super.draw(canvas);
RectF rectf=new RectF(50,50,getWidth()-2*mBorderWidth,getHeight()-2*mBorderWidth);
canvas.drawArc(rectf,0,360,false,mOutPaint);
//画内圆
canvas.drawArc(rectf,0,mSweepAngle/mMaxAngle*360,false,mInnerPaint);
//画文字
String stepText = mSweepAngle+"%";
Rect textBounds = new Rect();
mTextPaint.getTextBounds(stepText, 0, stepText.length(), textBounds);//测量文字画笔长度
int dx = getWidth() / 2 - textBounds.width() / 2;
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
int baseLine = getHeight() / 2 + dy;
canvas.drawText(stepText, dx, baseLine, mTextPaint);
}
public void setSweepAngle(float sweepAngle){
this.mSweepAngle=sweepAngle;
invalidate();
}
}
下面是两个类的kotlin代码实现:
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
start.setOnClickListener { startAnimator() }
}
private fun startAnimator() {
processBarView!!.setSweepAngle(0.00)
val valueAnimator = ObjectAnimator.ofFloat(0f, 100f)
valueAnimator.duration = 10000
valueAnimator.addUpdateListener { animation ->
val mSweepAngle = animation.animatedValue
//保留两位小数
val b: BigDecimal = BigDecimal(mSweepAngle.toString())
val f1 = b.setScale(2, BigDecimal.ROUND_HALF_UP).toDouble()
processBarView!!.setSweepAngle(f1)
}
valueAnimator.start()
}
}
ProcessBarView
class ProcessBarView : View {
private var mContext: Context? = null
private var mOutColor = 0
private var mInnerColor = 0
private var mBorderWidth = 0
private var mNumTextSize = 0
private var mNumTextColor = 0
private var mOutPaint: Paint? = null
private var mInnerPaint: Paint? = null
private var mTextPaint: Paint? = null
private var mSweepAngle = 0.00
private val mMaxAngle = 100
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : super(
context,
attrs,
defStyleAttr
) {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ProcessBarView)
mOutColor = typedArray.getColor(R.styleable.ProcessBarView_outerColor, Color.RED)
mInnerColor = typedArray.getColor(R.styleable.ProcessBarView_innerColor, Color.BLUE)
mBorderWidth = typedArray.getDimension(R.styleable.ProcessBarView_borderWidth, 20f).toInt()
mNumTextColor = typedArray.getColor(R.styleable.ProcessBarView_NumTextColor, Color.RED)
mNumTextSize = typedArray.getDimensionPixelSize(R.styleable.ProcessBarView_NumTextSize, 20)
mOutPaint = Paint()
mOutPaint!!.isAntiAlias = true
mOutPaint!!.strokeWidth = mBorderWidth.toFloat()
mOutPaint!!.color = mOutColor
mOutPaint!!.style = Paint.Style.STROKE //该属性代表非实心圆
mOutPaint!!.strokeCap = Paint.Cap.ROUND
mInnerPaint = Paint()
mInnerPaint!!.isAntiAlias = true
mInnerPaint!!.strokeWidth = mBorderWidth.toFloat()
mInnerPaint!!.color = mInnerColor
mInnerPaint!!.style = Paint.Style.STROKE
mInnerPaint!!.strokeCap = Paint.Cap.ROUND
mTextPaint = Paint()
mTextPaint!!.isAntiAlias = true
mTextPaint!!.color = mNumTextColor
mTextPaint!!.textSize = mNumTextSize.toFloat()
typedArray.recycle()
}
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
mContext = context
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(widthSize.coerceAtMost(heightSize), widthSize.coerceAtMost(heightSize))
}
override fun draw(canvas: Canvas) {
//画外圆
super.draw(canvas)
val rectf = RectF(
50F,
50F,
(width - 2 * mBorderWidth).toFloat(),
(height - 2 * mBorderWidth).toFloat()
)
canvas.drawArc(rectf, 0f, 360f, false, mOutPaint!!)
//画内圆
canvas.drawArc(rectf, 0f, (mSweepAngle / mMaxAngle * 360).toFloat(), false, mInnerPaint!!)
//画文字
val stepText = "$mSweepAngle%"
val textBounds = Rect()
mTextPaint!!.getTextBounds(stepText, 0, stepText.length, textBounds) //测量文字画笔长度
val dx = width / 2 - textBounds.width() / 2
val fontMetricsInt = mTextPaint!!.fontMetricsInt
val dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom
val baseLine = height / 2 + dy
canvas.drawText(stepText, dx.toFloat(), baseLine.toFloat(), mTextPaint!!)
}
fun setSweepAngle(sweepAngle: Double) {
mSweepAngle = sweepAngle
invalidate()
}
}
使用kotlin代码时,别忘了在app目录下的build.gradle中添加插件
id 'kotlin-android-extensions'
网友评论