Compose 自定义 - 绘制 Draw

news2024/11/18 3:42:39

一、概念

  • 所有的绘制操作都是通过调整像素大小来执行的。若要确保项目在不同的设备密度和屏幕尺寸上都能采用一致的尺寸,请务必使用 .toPx() 对 dp 进行转换或者采用小数尺寸。 

二、Modifier 修饰符绘制

官方页面

在修饰的可组合项之上或之下绘制。

.drawWithContent

fun Modifier.drawWithContent(
    onDraw: ContentDrawScope.() -> Unit
)

在 Lambda 中调用 drawContent() 就是绘制所修饰的内容,由此控制先后顺序,后绘制的会显示在上面。

.drawBehind

fun Modifier.drawBehind(
    onDraw: DrawScope.() -> Unit
)

修饰的内容会显示在 Lambda 内容之上(底层是先绘制 Lambda 内容再绘制所修饰的内容,后绘制的会显示在上面)。

.drawWithCache

fun Modifier.drawWithCache(
    onBuildDrawCache: CacheDrawScope.() -> DrawResult
)

当绘制复杂效果时,不希望因为重组而重新创建 Lambda 中用于绘制的实例如 Bush、Path 等,这可能会产生内存抖动。在 Lambada 中调用 onDrawWithContent()、onDrawBehind() 就类似于上面两个修饰符的功能。

 

@Composable
fun Demo() {
    Row(
        modifier = Modifier.size(150.dp),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Image(
            painterResource(id = R.drawable.logo_wechat_square),
            contentDescription = null,
            modifier = Modifier
                .size(50.dp)
                .drawWithContent {
                    drawContent()
                    drawRedDot()    //后绘制的会显示在上面
                }
        )
        Image(
            painterResource(id = R.drawable.logo_wechat_square),
            contentDescription = null,
            modifier = Modifier.padding(start = 10.dp).size(50.dp).drawBehind {
                drawRedDot()
            }
        )
    }
}

fun DrawScope.drawRedDot() {
    drawCircle(
        color = Color.Red,
        radius = 18F,
        center = Offset(drawContext.size.width, 0f)
    )
}

三、Canvas() 可组合项绘制

是一个可组合项。Compose 作为跨平台 UI 框架,所使用的 Canvas() 函数只是一个封装,最终还是调用具体平台即 Android 原生的 Canvas。

fun Canvas(

        modifier: Modifier,

        onDraw: DrawScope.() -> Unit

) = Spacer(modifier.drawBehind(onDraw))

发现该方法只是一个封装,真正绘制的是调用 drawBehind()。绘制内容是显示在 Spacer 下面的,由于 Spacer 是透明的,因此我们所绘制内容得以全部显示。

四、Brush

官方页面

用于绘制颜色(只指定一种颜色就是纯色)。

linearGradient

线性渐变

fun linearGradient(
        colors: List<Color>,        //渐变颜色
        start: Offset = Offset.Zero,        //开始的位置
        end: Offset = Offset.Infinite,        //结束的位置
        tileMode: TileMode = TileMode.Clamp        //重复模式
): Brush

水平渐变和垂直渐变底层就是调用的线性渐变。

horizontalGradient

水平方向渐变

fun horizontalGradient(
        colors: List<Color>,
        startX: Float = 0.0f,
        endX: Float = Float.POSITIVE_INFINITY,
        tileMode: TileMode = TileMode.Clamp
): Brush

verticalGradient

垂直方向渐变

fun verticalGradient(
        colors: List<Color>,
        startY: Float = 0.0f,
        endY: Float = Float.POSITIVE_INFINITY,
        tileMode: TileMode = TileMode.Clamp
): Brush

radialGradient

放射渐变

fun radialGradient(
        colors: List<Color>,
        center: Offset = Offset.Unspecified,        //中心位置
        radius: Float = Float.POSITIVE_INFINITY,        //半径
        tileMode: TileMode = TileMode.Clamp
): Brush

sweepGradient

扫描渐变

fun sweepGradient(
        colors: List<Color>,
        center: Offset = Offset.Unspecified
): Brush

4.2.1 使用 colorStop 更改颜色分布

自定义颜色在渐变中的显示方式,可以调整每种颜色的 colorStop 值,0 ~ 1 之间的小数。

val colorStops = arrayOf(
    0.0f to Color.Yellow,
    0.2f to Color.Red,
    1f to Color.Blue
)
Box(modifier = Modifier
        .requiredSize(200.dp)
        .background(Brush.horizontalGradient(colorStops = colorStops))
)

 4.2.2 使用 TileMode 让图案重复显示

当未指定 Brush 的开始位置 start 和结束位置 end 时,默认会填满整个区域,只有在区域 > Brush 时 TileMode 才会在渐变中平铺。以下举例 HorizontalGradient 的效果。

TileMode.Repeated将区域剩余空间绘制为重复的顺序颜色。
TileMode.Mirror将区域剩余空间绘制为重复的反转颜色。
TileMode.Clamp将区域剩余空间绘制为结束颜色。
TileMode.Decal将区域剩余空间绘制为透明色。(仅适用于 API 31 及更高版本。可使用 TileMode.isSupported() 确定设备是否支持 TileMode。如果使用了不受支持的 TileMode,系统会应用默认的 TileMode.Clamp。)

 4.2.3 更改 Brush 大小

当知道绘制区域大小时(如在 DrawScope 中通过 size 获取)可以按照 TileMode 方式平铺,在不知道的情况下(如将 Brush 分配给文字)可以扩展 Shader 重写 createShader() 函数利用绘制区域大小 size 形参。对于 radialGradient 如果未指定中心位置 center 和半径radius,渐变将占据整个 DrawScope 但是是以宽高较小的那边为直径,此时自定义大小会获得更好的效果(发散到屏幕外边去)。

val listColors = listOf(Color.Yellow, Color.Red, Color.Blue)
val customBrush = remember {
    object : ShaderBrush() {
        override fun createShader(size: Size): Shader {
            return LinearGradientShader(
                colors = listColors,
                from = Offset.Zero,
                to = Offset(size.width / 4f, 0f),
                tileMode = TileMode.Mirror
            )
        }
    }
}
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(customBrush)
)

4.2.4 使用图片作为 Brush

如需使用 ImageBitmap 作为 Brush,请以 ImageBitmap 的形式加载相应图片,然后创建 ImageShader Brush。可以应用于一下几种类型的绘制:背景、文字、画布。

val imageBrush =
    ShaderBrush(ImageShader(ImageBitmap.imageResource(id = R.drawable.dog)))

//用于 background
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(imageBrush)
)

//用于 TextStyle
Text(
    text = "Hello Android!",
    style = TextStyle(
        brush = imageBrush,
        fontWeight = FontWeight.ExtraBold,
        fontSize = 36.sp
    )
)

//用于 DrawScope#drawCircle()
Canvas(onDraw = {
    drawCircle(imageBrush)
}, modifier = Modifier.size(200.dp))

五、DrawScope

官方页面

在 DrawScope 中,可以访问到 drawContext 成员,它存储了以下信息:绘制尺寸size、封装的canvas、用来旋转缩放移动的transform,而通过 canvas.nativeCanvas 就能获取具体平台的实现,即可以调用 Android 原生的 Canvas 来实现更多需求。

绘制drawLine 画线
drawRect 画矩形
drawRoundRect 画圆角矩形
drawImage 绘制图片
drawCircle 画圆形
drawOval 画椭圆形
drawArc 画弧度跟扇形
drawPath 画路径
drawPoints 画点
行为inset 将DrawScope坐标空间平移
translate 平移坐标

rotate(旋转坐标)讲的是旋转了多少角度

rotateRad(旋转坐标)讲的是旋转了多少弧度

scale 缩放坐标
clipRect 裁剪矩形区域,绘制在裁剪好的矩形区域内。ClipOp.Difference从当前剪辑中减去提供的矩形。
clipPath 裁剪路径
drawIntoCanvas 直接提供底层画布
withTransform 组合转换

5.1 绘制

5.1.1 画线 drawLine()

fun drawLine(
        color: Color,
        start: Offset,
        end: Offset,
        strokeWidth: Float = Stroke.HairlineWidth,
        cap: StrokeCap = Stroke.DefaultCap,
        pathEffect: PathEffect? = null,
        /*FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )

cap

线条两头的形状

StrokeCap.Butt 平的(默认)
StrokeCap.Square 也是平的但是长一截
StrokeCap.Round 圆的

pathEffect

线条效果

PathEffect.cornerPathEffect(radius: Float)

将线段之间的锐角替换为指定半径的圆角 radius是半径

PathEffect.dashPathEffect(intervals: FloatArray, phase: Float = 0f)

将形状绘制为具有给定间隔的一系列破折号。比如虚线 例如interval={20,5},第一个参数表示虚线的长度是20,5是虚线之间的间隔是5. phase 偏移

PathEffect.chainPathEffect(outer: PathEffect, inner: PathEffect)

创建一个PathEffect,将内部效果应用于路径,然后应用外部效果

PathEffect.stampedPathEffect(shape: Path, advance: Float, phase: Float,style: StampedPathEffectStyle)

用path表示的指定形状冲压绘制的路径.  shape要踩踏的路径,advance 每个冲压形状之间的前进间距, phase 在压印第一个形状之前要偏移的相位量, style如何在每个位置转换形状,因为它是冲压. style有三种取值 StampedPathEffectStyle.Translate 平移 ,StampedPathEffectStyle.Rotate 旋转,StampedPathEffectStyle.Morph 变形

 

5.2 行为

5.2.1 缩放 scale()

5.2.2 平移 translate()

5.2.3 旋转 rotate()

5.2.4 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1142304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器视觉3D项目评估的基本要素及测量案例分析

目录 一. 检测需求确认 1、产品名称&#xff1a;【了解是什么产品上的零件&#xff0c;功能是什么】 2、*产品尺寸&#xff1a;【最大兼容尺寸】 3、*测量项目&#xff1a;【确认清楚测量点位】 4、*精度要求&#xff1a;【若客户提出的精度值过大或者过小&#xff0c;可以和客…

论坛介绍 | COSCon'23 开源文化(GL)

众多开源爱好者翘首期盼的开源盛会&#xff1a;第八届中国开源年会&#xff08;COSCon23&#xff09;将于 10月28-29日在四川成都市高新区菁蓉汇举办。本次大会的主题是&#xff1a;“开源&#xff1a;川流不息、山海相映”&#xff01;各位新老朋友们&#xff0c;欢迎到成都&a…

hadoop使用简介

git clone hadoop源码地址&#xff1a;https://gitee.com/CHNnoodle/hadoop.git git clone错误&#xff1a; Filename too long错误&#xff0c;使用git config --global core.longpaths true git clone https://gitee.com/CHNnoodle/hadoop.git -b rel/release-3.2.2 拉取指定…

Spigot 通过 BuildTools 构建 MineCraft Spigot 官方服务端文件

文章目录 从 Spigot 官方下载 BuildTools spigotmc / buildtools确保你有正确版本的 Java&#xff08;例如构建 1.20.2 的服务端一般需要有 Java17&#xff09;在 BuildTools.jar 同名文件夹打开 cmd 命令行&#xff08;点击红色圈圈区域输入 cmd 按 enter 即可&#xff09; …

如何查找特定基因集合免疫基因集 炎症基因集

温故而知新&#xff0c;再次看下Msigdb数据库。它更新了很多内容。给我们提供了一个查询基因集的地方。 关注微信&#xff1a;生信小博士 比如纤维化基因集&#xff1a; 打开网址&#xff1a;https://www.gsea-msigdb.org/gsea/msigdb/index.jsp 2.点击search 3.比如我对纤维…

第四章 文件管理 十、文件系统的全局结构

目录 一、文件系统的建立 1、原始磁盘 2、物理格式化后 3、逻辑格式化后 二、文件系统在内存中的结构 三、系统调用背后的过程 一、文件系统的建立 1、原始磁盘 2、物理格式化后 物理格式化&#xff0c;即低级格式化――划分扇区&#xff0c;检测坏扇区&#xff0c;并用…

[Java]前中后序遍历二叉树/递归与非递归

一、递归方法 首先&#xff0c;树形结构都是由递归方式定义的。那么递归是怎么用的&#xff1f; 1、终止条件&#xff1b;2、调用自身 分析 1、什么时候停止&#xff1f; 当结点值为空的时候&#xff0c;返回null&#xff1b; 2、如何调用自身&#xff1f; 以前序遍历为例&…

应用案例|基于三维机器视觉的机器人引导电动汽车充电头自动插拔应用方案

Part.1 项目背景 人类对减少温室气体排放、提高能源效率以及减少对化石燃料的依赖&#xff0c;加速了电动汽车的普及&#xff0c;然而&#xff0c;电动汽车的充电依然面临一些挑战。传统的电动汽车充电通常需要人工干预&#xff0c;插入和拔出充电头&#xff0c;这不仅可能导致…

2.MySQL的调控按钮——启动选项和系统变量

2.MySQL的调控按钮——启动选项和系统变量 1.启动选项和配置文件1.1 在命令行上使用选项1.2 配置文件中使用选项1.2.1 配置文件路径1.2.2 配置文件的内容1.2.3 特定 MySQL 版本的专用选项组1.2.4 配置文件的优先级1.2.5 同一个配置文件中多个组的优先级1.2.6 defaults-file 的使…

【杂记】Ubuntu20.04装系统,安装CUDA等

装20.04系统 安装系统的过程中&#xff0c;ROG的B660G主板&#xff0c;即使不关掉Secure boot也是可以的&#xff0c;不会影响正常安装&#xff0c;我这边出现问题的主要原因是使用了Ventoy制作的系统安装盘&#xff0c;导致每次一选择使用U盘的UEFI启动&#xff0c;就会跳回到…

利用经典热门电视剧写爆款公众号爆文10万+阅读量

热门经典的影视综艺真的是微信公众号的流量宝地啊&#xff01; 比如《甄嬛传》、《知否知否应是绿肥红瘦》、《琅琊榜》&#xff0c;这些都是有着10万阅读量的高产区。 写这类剧评的人也非常多&#xff0c;需要有观点、有态度&#xff0c;才能抓住人的眼球。一般都是那些大号…

SpringMVC(四)域对象共享数据

pageContext:表示的是jsp页面的范围 HttpServletRequest:表示的是一次请求的范围 HttpSession:表示的是一次会话的范围 ServletContext:表示的是整个应用的范围 一、向请求域中共享数据&#xff1a; 1.1使用ServletAPI向request域对象共享数据 RequestMapping("test…

什么是Spring Web MVC

Spring Web MVC 概念 Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架&#xff0c;从⼀开始就包含在 Spring 框架中。它的 正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc)&#xff0c;但它通常被称为"Spring MVC". 什么是Servlet Servlet 是…

时间复杂度(补充)和 空间复杂度

大家好啊&#xff0c;我今天来给大家分享有关空间复杂度的知识。感谢大家对我的支持&#xff0c;我会继续加油更新博客&#xff0c;努力提高博客质量的。 我们在这里先补充时间复杂度的一些实例&#xff1a; 补充实例1&#xff1a; // 计算BinarySearch的时间复杂度&#xff…

零资源的大语言模型幻觉预防

零资源的大语言模型幻觉预防 摘要1 引言2 相关工作2.1 幻觉检测和纠正方法2.2 幻觉检测数据集 3 方法论3.1 概念提取3.2 概念猜测3.2.1 概念解释3.2.2 概念推理 3.3 聚合3.3.1 概念频率分数3.3.2 加权聚合 4 实验5 总结 摘要 大语言模型&#xff08;LLMs&#xff09;在各个领域…

笔记:电子设备接地,接的到底是什么地?

电路中有“地”&#xff0c;设备中有“地”&#xff1b;都是“地”&#xff0c;此地非彼地。 混淆的原因 有些混淆&#xff0c;是以为中文翻译造成的&#xff0c;英文所有Ground都统一翻译为“地”&#xff1b; 例1&#xff1a;英文Circuit Ground&#xff0c;应该翻译为电路…

RedissonCach的源码流程

上&#xff1a; https://blog.csdn.net/Michelle_Zhong/article/details/126384566 中&#xff1a; https://blog.csdn.net/michelle_zhong/category_11874153.html 下&#xff1a; https://blog.csdn.net/Michelle_Zhong/article/details/126391915?ops_request_misc%257B%…

细说RTSP、RTMP和GB28181区别

好多流媒体初学者&#xff0c;对RTSP、RTMP和GB28181三者容易混淆&#xff0c;不了解他们的使用场景和区别&#xff0c;本文抛砖引玉&#xff0c;大概介绍下三者的区别。 RTSP&#xff08;Real-Time Streaming Protocol&#xff09;、RTMP&#xff08;Real-Time Messaging Pro…

实战|如何低成本训练一个可以超越 70B Llama2 的模型 Zephyr-7B

每一周&#xff0c;我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新&#xff0c;包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等&#xff0c;我们将其称之为「Hugging News」。快来看看有哪些近期更新吧&#xff01;&#x1…

Flutter的The file name ‘xxxx.dart‘ isn‘t a snake_case identifier警告

文章目录 警告原因分析解决方法dart的一些命名规则变量和函数命名&#xff1a;类和类型命名&#xff1a;常量和枚举&#xff1a;文件命名&#xff1a;包命名&#xff1a;注释&#xff1a;命名一致性&#xff1a;避免缩写&#xff1a;可搜索的命名&#xff1a; 一些好习惯 警告 …