1.自定义属性
attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyProgressBar">
<attr name="android:color" />
<attr name="android:lineHeight" />
<attr name="android:radius" />
<attr name="android:progress" />
<attr name="android:textSize" />
</declare-styleable>
</resources>
2.MyProgressBar
测量保存是固定写法
class MyProgressBar : View {
private var mRadius = 0
private var mColor = 0
private var mLineHeight = 0
private var mTextSize = 0
private var mProgress = 0
private val mPaint: Paint by lazy { Paint() }
constructor(context: Context?) : super(context)
constructor(
context: Context,
attrs: AttributeSet?
) : super(context, attrs) {
//TypedArray 获取layout中属性
val ta = context.obtainStyledAttributes(attrs, R.styleable.MyProgressBar)
mRadius =
ta.getDimension(R.styleable.MyProgressBar_android_radius, Util.dp2px(30, this)).toInt()
mColor = ta.getColor(R.styleable.MyProgressBar_android_color, -0x10000)
mLineHeight =
ta.getDimension(
R.styleable.MyProgressBar_android_lineHeight, Util.dp2px(3, this)
).toInt()
mTextSize =
ta.getDimension(R.styleable.MyProgressBar_android_textSize, Util.dp2px(36, this))
.toInt()
mProgress = ta.getInt(R.styleable.MyProgressBar_android_progress, 30)
//TypedArray池化,需要回收(频繁的创建array会影响性能)
ta.recycle()
//画笔
mPaint.isAntiAlias = true
mPaint.color = mColor
//只描边不填充
mPaint.style = Paint.Style.STROKE
//描边的宽度
mPaint.strokeWidth = mLineHeight * 1.0f / 4
}
//MyViewActivity定义了点击后关联属性progress的动画
var progress: Int
//layout xml中获取的progress
get() = mProgress
//MyViewActivity定义的属性progress赋值给typerarray的mprogress,再重绘
set(progress) {
mProgress = progress
//重绘,调用ondraw方法
invalidate()
}
//测量,固定写法
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val needWidth = measureWidth() + paddingLeft + paddingRight
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val needHeight = measureHeight() + paddingTop + paddingBottom
//EXACTLY=精确,AT_MOST=wrap content,UNSPECIFIED=子类想要多大就可以多大
val width = when (widthMode) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> min(needWidth, widthSize)
MeasureSpec.UNSPECIFIED -> needWidth
else -> min(needWidth, widthSize)
}
val height = when (heightMode) {
MeasureSpec.EXACTLY -> heightSize
MeasureSpec.AT_MOST -> min(needHeight, heightSize)
MeasureSpec.UNSPECIFIED -> needHeight
else -> min(needHeight, heightSize)
}
setMeasuredDimension(width, height)
}
private fun measureHeight(): Int {
return mRadius * 2
}
private fun measureWidth(): Int {
return mRadius * 2
}
//ondraw->画布->画笔
override fun onDraw(canvas: Canvas) {
//画细圆
canvas.drawCircle(
width / 2.toFloat(), height / 2.toFloat(),
width / 2 - paddingLeft - mPaint.strokeWidth / 2, mPaint
)
//移动画布
canvas.translate(paddingLeft.toFloat(), paddingTop.toFloat())
//画粗圆
mPaint.strokeWidth = mLineHeight.toFloat()
//根据progress绘制的粗圆
val angle = mProgress * 1.0f / 100 * 360
canvas.drawArc(
RectF(
0.0f, 0.0f, (width - paddingLeft * 2).toFloat(),
(height - paddingLeft * 2).toFloat()
), 0f, angle, false, mPaint
)
val text = "$mProgress%"
// text = "张鸿洋";
//画字的这只笔,只描边不填充
mPaint.strokeWidth = 0f
//这只笔在定义的x,y上水平居中
mPaint.textAlign = Paint.Align.CENTER
mPaint.textSize = mTextSize.toFloat()
//获得文字的高度
val bound = Rect()
mPaint.getTextBounds(text, 0, text.length, bound)
val textHeight = bound.height()
//绘制text,索引0到text.length,原点为1/2宽-移动距离,1/2高+1/2文字高度-移动距离,画笔
canvas.drawText(
text, 0, text.length,
+width / 2 - paddingLeft.toFloat(),
height / 2 - paddingTop + textHeight / 2.toFloat(), mPaint
)
//这里是静态效果,点击后动态效果是写在MyViewActivity中的动画
}
//存储与恢复,xml中需添加id,固定写法
override fun onSaveInstanceState(): Parcelable? {
val bundle = Bundle()
bundle.putInt("key_progress", mProgress)
bundle.putParcelable("instance", super.onSaveInstanceState())
return bundle
}
override fun onRestoreInstanceState(state: Parcelable) {
if (state is Bundle) {
val parcelable =
state.getParcelable<Parcelable>("instance")
super.onRestoreInstanceState(parcelable)
mProgress = state.getInt("key_progress")
return
}
super.onRestoreInstanceState(state)
}
}
3.使用
<com.******.MyProgressBar
android:id="@+id/id_pb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:padding="10dp"
android:progress="0"
android:textSize="18sp"
android:color="#ea22e4"
android:radius="36dp" />
-----------------------------------------------------------------------------
class MyViewActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_view)
id_pb.setOnClickListener {
ObjectAnimator.ofInt(id_pb, "progress", 0, 100).setDuration(3000).start()
}
}
}
-----------------------------------------------------------------------------
object Util {
fun dp2px(dpVal: Int, view: View): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, dpVal.toFloat(), view.resources.displayMetrics
)
}
}