Android的自定义View

95 阅读2分钟

测量-measure
布局-layout
绘制-draw
三大流程

练练手

来点自定义属性:


<?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>

再来点代码时鲜:

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()方法没用起来,继续进阶学习...