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

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

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

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

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

如需创建自定义 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
    }
}
相关推荐
海兰16 小时前
【 Python 量化交易】第10章:八大经典策略
android·python·kotlin
Rytter16 小时前
Android诈骗裸聊软件逆向分析
android
JMchen12317 小时前
【Android 性能优化实战 60 讲】04 Memory Profiler 高阶玩法:深剖堆内存原理,实战定位 Kotlin 闭包隐式泄漏
android·性能优化·kotlin·实战·源码分析·内存泄漏·memory profiler
Android-Flutter17 小时前
Java线程池 - 内部线程管理机制详解
android
开开心心就好17 小时前
手机悬屏翻译工具外语游戏漫画APP全覆盖
android·前端·javascript·python·游戏·pdf·html
恋猫de小郭17 小时前
AI 时代,也许你的 Flutter 需要一套 Dartastic OpenTelemetry 监控
android·前端·flutter
Kapaseker18 小时前
我为什么喜欢 Compose ?
android·kotlin
JMchen12318 小时前
【Android 性能优化实战 60 讲】03 Perfetto 现代分析利器:SQL 查询 Trace 数据,微秒级定位函数 CPU 耗时
android·sql·性能优化·实战·源码分析·perfetto·启动优化
开开心心就好18 小时前
手机精确倒计时工具悬浮窗口秒数实时显示
android·前端·javascript·jupyter·智能手机·pdf·html
墨狂之逸才18 小时前
一次 Android 工位机黑屏卡死排查:根因不是 App,而是 scrcpy 与 Rockchip 编码器的 DMA-BUF 泄漏
android·android studio