测量-measure
布局-layout
绘制-draw
三大流程
练练手
来点自定义属性:
xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomView">
<attr name="customColor" format="color" />
<attr name="customText" format="string" />
<attr name="customSize" format="dimension" />
</declare-styleable>
</resources>
再来点代码时鲜:
kotlin
package com.mircles.test.ui
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import androidx.annotation.StyleRes
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.withStyledAttributes
import com.mircles.test.R
import kotlin.math.min
class CustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var mPaint: Paint = Paint().apply {
color = Color.BLACK
style = Paint.Style.FILL
}
//定义变量来存储自定义属性值
private var customColor: Int = Color.BLUE
private var customSize: Float = 100f
private var customText: String = "Say"
init {
//context.obtainStyledAttributes()
//withStyledAttributes是obtainStyledAttributes的扩展函数,可以学学谷歌的工程师是怎么做扩展的
context.withStyledAttributes(
attrs,
R.styleable.CustomView,
defStyleAttr = defStyleAttr
) {
customColor = getColor(R.styleable.CustomView_customColor, Color.BLACK)
customSize = getDimension(R.styleable.CustomView_customSize, 100f)
customText = getString(R.styleable.CustomView_customText) ?: "hello"
//应用到画笔中
mPaint.color = customColor
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val desiredWidth = 200 // 你期望的默认宽度(像素)
val desiredHeight = 200 // 你期望的默认高度(像素)
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
// 测量宽度
val width = when (widthMode) {
MeasureSpec.EXACTLY -> widthSize
MeasureSpec.AT_MOST -> min(desiredWidth, widthSize)
else -> desiredWidth
}
// 测量高度
val height = when (heightMode) {
MeasureSpec.EXACTLY -> heightSize
MeasureSpec.AT_MOST -> min(desiredHeight, heightSize)
else -> desiredHeight
}
setMeasuredDimension(width, height)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// 绘制一个圆形
val centerX = width / 2f
val centerY = height / 2f
val radius = min(centerX, centerY) - 10
canvas.drawCircle(centerX, centerY, radius, mPaint)
// 绘制文字
val textPaint = Paint().apply {
color = Color.BLUE
textSize = customSize
textAlign = Paint.Align.CENTER
}
//计算文字基线位置,使文字垂直居中:可以留意计算的思路
val textBounds = Rect()
textPaint.getTextBounds(customText,0,customText.length,textBounds)
val textY = centerY - (textBounds.top+textBounds.bottom)/2f
canvas.drawText(customText, centerX, textY, textPaint)
}
//提供设置方法以便在代码中动态修改
fun setCustomColor(color:Int){
customColor = color
mPaint.color = color
invalidate()
}
fun setCustomText(text: String){
customText = text
invalidate()
}
fun setCustomSize(size: Float){
customSize = size
invalidate()
}
// 如果你想要从样式资源中获取属性
fun applyStyle(@StyleRes styleRes: Int) {
context.withStyledAttributes(styleRes, R.styleable.CustomView) {
customColor = getColor(R.styleable.CustomView_customColor, customColor)
customSize = getDimension(R.styleable.CustomView_customSize, customSize)
customText = getString(R.styleable.CustomView_customText) ?: customText
mPaint.color = customColor
invalidate()
}
}
}
@Composable
fun CircleOO() {
AndroidView(
factory = { context ->
CustomView(context).apply {
setCustomText("卧槽")
setCustomSize(50f)
setCustomColor(Color.DKGRAY)
}
},
update = { cv ->
}
)
}
@Preview(showBackground = true)
@Composable
fun PreviewUI() {
CircleOO()
}
还可以添加更多的属性,文字的规格样式、背景的规格样式等等。 还有onLayout()方法没用起来,继续进阶学习...