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
    }
}
相关推荐
执明wa14 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio
404_coder14 小时前
源码视角下的 Android 开机流程:从 Zygote、SystemServer 到 Launcher
android
二流小码农14 小时前
鸿蒙开发:以登录案例了解代码架构MVVM
android·ios·harmonyos
用户693717500138415 小时前
从代码生产者到 AI 协作者:软件工程师的角色重构
android·前端·后端
GitLqr15 小时前
别在 Flutter 的 main() 里乱锁屏幕方向,小心 iPad 分屏功能被你搞没了
android·flutter·ios
sg_knight15 小时前
MySQL 存储过程详解:从入门到实战
android·数据库·mysql·database·dba·关系型数据库·db
爱笑鱼15 小时前
Binder(四):ioctl(BINDER_WRITE_READ) 之后,事务怎样到达目标进程?
android
AFinalStone16 小时前
Android 7系统休眠唤醒(二)开机全链路—BootROM到Launcher
android·电源管理·休眠唤醒
Mr YiRan16 小时前
Android NDK开发之统计到未被回收的图片
android