Android的自定义View

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

相关推荐
前端工作日常6 分钟前
我学习到的A2UI的功能:纯粹的UI生成
前端
Jing_Rainbow10 分钟前
【 前端三剑客-37 /Lesson61(2025-12-09)】JavaScript 内存机制与执行原理详解🧠
前端·javascript·程序员
UIUV1 小时前
模块化CSS学习笔记:从作用域问题到实战解决方案
前端·javascript·react.js
aoi1 小时前
解决 Vue 2 大数据量表单首次交互卡顿 10s 的性能问题
前端·vue.js
Kakarotto1 小时前
使用ThreeJS绘制东方明珠塔模型
前端·javascript·vue.js
donecoding1 小时前
TypeScript `satisfies` 的核心价值:两个例子讲清楚
前端·javascript
德育处主任1 小时前
『NAS』在群晖部署一个文件加密工具-hat.sh
前端·算法·docker
cup1131 小时前
【原生 JS】支持加密的浏览器端 BYOK AI SDK,助力 Vibe Coding
前端
用户12039112947261 小时前
使用 Tailwind CSS 构建现代登录页面:从 Vite 配置到 React 交互细节
前端·javascript·react.js
杨进军1 小时前
模拟 Taro 实现编译多端样式文件
前端·taro