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

相关推荐
JIngJaneIL23 分钟前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
敲敲了个代码30 分钟前
隐式类型转换:哈基米 == 猫 ? true :false
开发语言·前端·javascript·学习·面试·web
澄江静如练_40 分钟前
列表渲染(v-for)
前端·javascript·vue.js
JustHappy1 小时前
「chrome extensions🛠️」我写了一个超级简单的浏览器插件Vue开发模板
前端·javascript·github
Loo国昌1 小时前
Vue 3 前端工程化:架构、核心原理与生产实践
前端·vue.js·架构
sg_knight1 小时前
拥抱未来:ECMAScript Modules (ESM) 深度解析
开发语言·前端·javascript·vue·ecmascript·web·esm
LYFlied1 小时前
【每日算法】LeetCode 17. 电话号码的字母组合
前端·算法·leetcode·面试·职场和发展
开发者小天1 小时前
react中useEffect的用法,以及订阅模式的原理
前端·react.js·前端框架
前端白袍2 小时前
Vue:如何实现一个具有复制功能的文字按钮?
前端·javascript·vue.js