android compose 自定义Painter绘制图形 使用

android compose 自定义Painter绘制图形 使用

在 Compose 中,Painter 对象用于表示可以绘制的内容(替代 Android 中定义的 Drawable API),并且会影响使用该对象的相应可组合项的测量和布局。BitmapPainter 采用 ImageBitmap,后者可以在屏幕上绘制 Bitmap。

对于大多数用例,使用 painterResource() 函数会为该资源返回正确的 Painter(例如 BitmapPainter 或 VectorPainter)。如需详细了解两者之间的差异,请参阅 ImageBitmap 与 ImageVector 部分。

Painter 与 DrawModifier 不同,后者在绘制时严格遵守为其指定的边界,并且不会影响可组合项的测量或布局。

如需创建自定义 Painter,请扩展 Painter 类并实现 onDraw 方法,从而允许访问 DrawScope 以绘制自定义图形。您还可以替换 intrinsicSize,它将用于影响其所属的可组合项:

官方网站:自定义 Painter | Jetpack Compose | Android Developers

复制代码
package com.wn.androidcomposedemo1.basegoogleimage

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.toSize
import com.wn.androidcomposedemo1.R
import com.wn.androidcomposedemo1.ui.theme.AndroidComposeDemo1Theme
import kotlin.math.roundToInt

/**
 * Author : wn
 * Email : maoning20080808@163.com
 * Date : 2026/7/3 11:35
 * Description : 自定义Painter绘制图形
 */
class ImagePainterActivity : ComponentActivity(){

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AndroidComposeDemo1Theme() {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ImagePainterExample()
                    //CustomPainterUsage()  官方的demo,也显示不正常。
                }
            }
        }

    }

    @Composable
    fun ImagePainterExample(){
        val rainbowImage = ImageBitmap.imageResource(id = R.drawable.rainbow)
        val dogImage = ImageBitmap.imageResource(id = R.drawable.dog)
        val customPainter = remember {
            OverlayImagePainter2(rainbowImage,dogImage)
        }

        Image(
            painter = customPainter,
            contentDescription = stringResource(R.string.image_description),
            contentScale = ContentScale.Crop,
            modifier = Modifier.wrapContentSize()
        )
    }

}

class OverlayImagePainter2 constructor(
    private val image : ImageBitmap,
    private val imageOverlay : ImageBitmap,
    private val srcOffset : IntOffset = IntOffset.Zero,
    private val srcSize : IntSize = IntSize(image.width, image.height),
    private val overlaySize : IntSize = IntSize(imageOverlay.width, imageOverlay.height)
) : Painter(){

    private val size : IntSize = validateSize(srcOffset, srcSize)

    override fun DrawScope.onDraw() {

        //绘制底图
        drawImage(
            image,
            srcOffset,
            srcSize,
            dstSize = IntSize(
                this@onDraw.size.width.roundToInt(),
                this@onDraw.size.height.roundToInt()
            )
        )

        //绘制叠加图
        drawImage(
            imageOverlay,
            srcOffset,
            overlaySize,
            dstSize = IntSize(
                this@onDraw.size.width.roundToInt(),
                this@onDraw.size.height.roundToInt()
            ),
            blendMode = BlendMode.SrcIn
        )
    }

    override val intrinsicSize: Size
        get() = size.toSize()

    private fun validateSize(srcOffset : IntOffset, srcSize : IntSize) : IntSize{
        require(
            srcOffset.x >= 0 &&
            srcOffset.y >= 0 &&
            srcSize.width >= 0 &&
            srcSize.height >= 0 &&
            srcSize.width <= image.width &&
            srcSize.height <= image.height
        )
        return srcSize
    }
}
相关推荐
金銀銅鐵4 小时前
一个 4 个常量的枚举,Kotlin 在字节码里生成了什么
jvm·kotlin
疯华整猫5 小时前
否决 Flutter 提案那天,我发现自己也说不清 Framework 管什么
android
传奇开心果编程18 小时前
【Flutter踩坑排雷】第25课:ScrollController 误用坑|跳转失效、监听泄漏与“控制器未附加”
android·flutter·ios·移动开发·跨平台·dart·声明式ui
慕容雪_21 小时前
Capacitor8-Android打包完整手册
android·打包·capacitor
阿杰m1 天前
Android源码第十三章:Bluetooth——沿一次发现走到控制器
android
booboosui1 天前
派活给另一个 AI:前后两次文件夹快照,就是交付清单
android
普通人61 天前
MySQL_FIND_IN_SET函数详解
android·数据库·mysql
好的佩奇2 天前
Dart 之集合元素
android·flutter·dart
Easyhood2 天前
Android 开发入门实践(十四):Compose 副作用入门与四个常用 API
android·开发语言·kotlin·android jetpack·compose
FlightYe2 天前
音视频修炼之容器与协议(四):RTSP详解
android·linux·ffmpeg·音视频·dash