如何剪辑或剪切可组合项?

2023-11-22

enter image description here

如何剪辑或剪切可组合内容以使图像、按钮或可组合项具有自定义形状?这个问题不是关于使用Modifier.clip(),更像是用替代方法来完成任务,这些方法允许产生不可能的结果,或者当很难创建像云或方圆这样的形状时。

This is 分享您的知识,问答式问题其灵感来自于 M3 BottomAppBar 或 BottomNavigation 没有切口形状,找不到问题,并绘制了一个Squircle形状很困难,如这个问题.

我们非常欢迎更多更好的剪切或自定义形状和可组合项的方法。


无需创建自定义可组合项即可实现剪切或裁剪可组合项的方法之一是使用

Modifier.drawWithContent{}有一层和一个BlendMode or 波特达夫模式.

使用 Jetpack Compose 要使这些模式正常工作,您需要将 alpha 设置为小于 1f 或使用层作为答案here.

我选择图层解决方案,因为我不想更改内容 Alpha

fun ContentDrawScope.drawWithLayer(block: ContentDrawScope.() -> Unit) {
    with(drawContext.canvas.nativeCanvas) {
        val checkPoint = saveLayer(null, null)
        block()
        restoreToCount(checkPoint)
    }
}

block lambda 是绘制范围Modifier.drawWithContent{}进行剪辑

和另一个进一步简化的扩展

fun Modifier.drawWithLayer(block: ContentDrawScope.() -> Unit) = this.then(
    Modifier.drawWithContent {
        drawWithLayer {
            block()
        }
    }
)

剪辑按钮位于左侧

First let's draw the button that is cleared a circle at left side
@Composable
private fun WhoAteMyButton() {
    val circleSize = LocalDensity.current.run { 100.dp.toPx() }
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .drawWithLayer {
                // Destination
                drawContent()
               
                // Source
                drawCircle(
                    center = Offset(0f, 10f),
                    radius = circleSize,
                    blendMode = BlendMode.SrcOut,
                    color = Color.Transparent
                )
            }
    ) {
        Button(
            modifier = Modifier
                .padding(horizontal = 10.dp)
                .fillMaxWidth(),
            onClick = { /*TODO*/ }) {
            Text("Hello World")
        }
    }
}

我们只是简单地画了一个圆,但是因为BlendMode.SrcOut目的地的交集被删除。

剪辑按钮和带有自定义图像的图像

对于松鼠按钮,我从网上找到了一张图片

并使用此图像剪切按钮和图像

@Composable
private fun ClipComposables() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceEvenly
    ) {
        val imageBitmap = ImageBitmap.imageResource(id = R.drawable.squircle)

        Box(modifier = Modifier
            .size(150.dp)
            .drawWithLayer {

                // Destination
                drawContent()

                // Source
                drawImage(
                    image = imageBitmap,
                    dstSize = IntSize(width = size.width.toInt(), height = size.height.toInt()),
                    blendMode = BlendMode.DstIn
                )

            }
        ) {

            Box(
                modifier = Modifier
                    .size(150.dp)
                    .clickable { }
                    .background(MaterialTheme.colorScheme.inversePrimary),
                contentAlignment = Alignment.Center
            ) {
                Text(text = "Squircle", fontSize = 20.sp)
            }
        }

        Box(modifier = Modifier
            .size(150.dp)
            .drawWithLayer {
                // Destination
                drawContent()

                // Source
                drawImage(
                    image = imageBitmap,
                    dstSize = IntSize(width = size.width.toInt(), height = size.height.toInt()),
                    blendMode = BlendMode.DstIn
                )

            }
        ) {

            Image(
                painterResource(id = R.drawable.squirtle),
                modifier = Modifier
                    .size(150.dp),
                contentScale = ContentScale.Crop,
                contentDescription = ""
            )
        }

    }
}

这里有两点需要注意

1-混合模式是BlendMode.DstIn因为我们想要的纹理Destination形状为Source2- 使用 dstSize 在 ContentDrawScope 内绘制图像以匹配可组合大小。默认情况下,它是使用上面发布的 png 大小绘制的。

创建带有切口形状的 BottomNavigation

@Composable
private fun BottomBarWithCutOutShape() {
    val density = LocalDensity.current
    val shapeSize = density.run { 70.dp.toPx() }

    val cutCornerShape = CutCornerShape(50)
    val outline = cutCornerShape.createOutline(
        Size(shapeSize, shapeSize),
        LocalLayoutDirection.current,
        density
    )

    val icons =
        listOf(Icons.Filled.Home, Icons.Filled.Map, Icons.Filled.Settings, Icons.Filled.LocationOn)

    Box(
        modifier = Modifier.fillMaxWidth()
    ) {
        BottomNavigation(
            modifier = Modifier
                .drawWithLayer {
                    with(drawContext.canvas.nativeCanvas) {

                        val checkPoint = saveLayer(null, null)
                        val width = size.width

                        val outlineWidth = outline.bounds.width
                        val outlineHeight = outline.bounds.height

                        // Destination
                        drawContent()

                        // Source
                        withTransform(
                            {
                                translate(
                                    left = (width - outlineWidth) / 2,
                                    top = -outlineHeight / 2
                                )
                            }
                        ) {
                            drawOutline(
                                outline = outline,
                                color = Color.Transparent,
                                blendMode = BlendMode.Clear
                            )
                        }

                        restoreToCount(checkPoint)
                    }
                },
            backgroundColor = Color.White
        ) {

            var selectedIndex by remember { mutableStateOf(0) }

            icons.forEachIndexed { index, imageVector: ImageVector ->
                if (index == 2) {
                    Spacer(modifier = Modifier.weight(1f))
                    BottomNavigationItem(
                        icon = { Icon(imageVector, contentDescription = null) },
                        label = null,
                        selected = selectedIndex == index,
                        onClick = {
                            selectedIndex = index
                        }
                    )
                } else {
                    BottomNavigationItem(
                        icon = { Icon(imageVector, contentDescription = null) },
                        label = null,
                        selected = selectedIndex == index,
                        onClick = {
                            selectedIndex = index
                        }
                    )
                }
            }
        }

        // This is size fo BottomNavigationItem
        val bottomNavigationHeight = LocalDensity.current.run { 56.dp.roundToPx() }

        FloatingActionButton(
            modifier = Modifier
                .align(Alignment.TopCenter)
                .offset {
                    IntOffset(0, -bottomNavigationHeight / 2)
                },
            shape = cutCornerShape,
            onClick = {}
        ) {
            Icon(imageVector = Icons.Default.Add, contentDescription = null)
        }
    }
}

这段代码有点长,但我们基本上像往常一样创建一个形状并创建一个要剪辑的轮廓

    val cutCornerShape = CutCornerShape(50)
    val outline = cutCornerShape.createOutline(
        Size(shapeSize, shapeSize),
        LocalLayoutDirection.current,
        density
    )

在剪切之前,我们将此形状部分向上移动高度的一半,以仅剪切轮廓的一半

withTransform(
{
    translate(
        left = (width - outlineWidth) / 2,
        top = -outlineHeight / 2
    )
}
) {
    drawOutline(
        outline = outline,
        color = Color.Transparent,
        blendMode = BlendMode.Clear
    )
}

还要有一个 BottomNavigation,例如 BottomAppBar,将子项放置在两侧 我用了垫片

icons.forEachIndexed { index, imageVector: ImageVector ->
    if (index == 2) {
        Spacer(modifier = Modifier.weight(1f))
        BottomNavigationItem(
            icon = { Icon(imageVector, contentDescription = null) },
            label = null,
            selected = selectedIndex == index,
            onClick = {
                selectedIndex = index
            }
        )
    } else {
        BottomNavigationItem(
            icon = { Icon(imageVector, contentDescription = null) },
            label = null,
            selected = selectedIndex == index,
            onClick = {
                selectedIndex = index
            }
        )
    }
}

然后我们只需添加一个 FloatingActionButton,我使用了偏移量,但您可以创建一个更大的父级并将我们的自定义 BottomNavigation 和按钮放入其中。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何剪辑或剪切可组合项? 的相关文章

  • 如何对这个字符串进行子串化

    我想得到这个字符串的 4 个部分 String string 10 trillion 896 billion 45 million 56873 我需要的4个部分是 10万亿 8960亿 4500万 和 56873 我所做的是删除所有空格 然
  • 在包“android”中找不到属性“backgroundTint”的资源标识符

    我发现了一些视图 xml 属性 例如backgroundTint backgroundTintMode 但是当我使用它作为视图属性定义时 Eclipse 显示错误 No resource identifier found for attri
  • React Native 从 JavaScript 代码内部访问 strings.xml

    有没有办法访问当前值android app src main res values strings xml从 JavaScript 代码内部 我想为每个构建放置不同的端点 URL 但我什至无法检测到反应本机代码内的构建类型 而不必求助于 D
  • 在 Android Studio 中,为什么我必须在模拟器中单击“运行应用程序”两次才能启动应用程序?

    在 Android Studio 中 当我按播放按钮在 Android 模拟器上安装并运行应用程序时 大约 5 10 秒后 我在屏幕底部收到一条消息 显示 安装成功 但应用程序实际上并未运行在模拟器上 我必须再次按下播放按钮 这是非常令人沮
  • 如何以编程方式检查 AndroidManifest.xml 中是否声明了服务?

    我正在编写一个库 该库提供了一项服务 其他开发人员可以通过将其包含在他们的项目中来使用该服务 因此 我无法控制 AndroidManifest xml 我在文档中解释了要做什么 但一个常见的问题是人们忽略了将适当的 标记添加到其清单中 或者
  • 如何在android中获取Camera2 API的当前曝光

    In android hardware Camera旧的 我使用下面的代码获取当前曝光并获取它Camera Camera Parameters param mCamera getParameters currentExposure para
  • Android 模拟器插件无法初始化后端 EGL 显示

    我在 Cloudbees 上设置了 Jenkins 作业 并且可以在那里成功签出并编译我的 Android 项目 现在我想在 android 模拟器中运行一些 JUnit 测试并添加 Android 模拟器插件 我将 显示模拟器窗口 选项设
  • Android 中 Kotlin 协程的正确使用方式

    我正在尝试使用异步更新适配器内的列表 我可以看到有太多的样板 这是使用 Kotlin 协程的正确方法吗 这个可以进一步优化吗 fun loadListOfMediaInAsync async CommonPool try Long runn
  • 带有 EditText 和 Spinner 的对话框

    我有一个按钮 单击后会弹出一个对话框 我希望对话框有一个EditText and a Spinner对话框内 我不知道如何设置它的视图 我有一个代码AlertDialog它有效 只是EditText and Spinner我需要将其放入其中
  • 如何使用 IF 检查 TextView 可见性

    我有一个 onCheckedChangeListener 来根据选择的单选按钮显示文本视图 我有 1 个疑问和 1 个难题 想知道是否有人可以帮助我 问题 您能否将单选组默认检查值设置为 否 单选按钮 以便一开始就不会检查任何内容 问题 如
  • 如何默认在 ActionOpenDocument 意图中显示“内部存储”选项

    我需要用户选择一个自定义文件类型的文件 并将其从 Windows 文件资源管理器拖到 Android 设备上 但默认情况下内部存储选项不可用 当我使用以下命令启动意图时 var libraryIntent new Intent Intent
  • 字符串数组文本格式化

    我有这个字符串 String text Address 1 Street nr 45 Address 2 Street nr 67 Address 3 Street nr 56 n Phone number 000000000 稍后将被使用
  • 我的设备突然没有显示在“Android 设备选择器”中

    我正在使用我的三星 Galaxy3 设备来测试过去两个月的应用程序 它运行良好 但从今天早上开始 当我将设备连接到系统时 它突然没有显示在 Android 设备选择器 窗口中 我检查过 USB 调试模式仅在我的设备中处于选中状态 谁能猜出问
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • .isProviderEnabled(LocationManager.NETWORK_PROVIDER) 在 Android 中始终为 true

    我不知道为什么 但我的变量isNetowrkEnabled总是返回 true 我的设备上是否启用互联网并不重要 这是我的GPSTracker class public class GPSTracker extends Service imp
  • Android 套接字和 asynctask

    我即将开始制作一个应该充当 tcp 聊天客户端的应用程序 我一直在阅读和阅读 我得出的结论是最好 如果不需要 将我的套接字和异步任务中的阅读器 问题是我不确定从哪里开始 因为我是 Android 新手 这至少对我来说是一项艰巨的任务 但据我
  • 一次显示两条Toast消息?

    我希望在一个位置显示一条 Toast 消息 并在另一位置同时显示另一条 Toast 消息 多个 Toast 消息似乎总是按顺序排队和显示 是否可以同时显示两条消息 是否有一种解决方法至少可以提供这种外观并且不涉及扰乱活动布局 Edit 看来
  • 如何在Xamarin中删除ViewTreeObserver?

    假设我需要获取并设置视图的高度 在 Android 中 众所周知 只有在绘制视图之后才能获取视图高度 如果您使用 Java 有很多答案 最著名的方法之一如下 取自这个答案 https stackoverflow com a 24035591
  • 将 Intent 包装在 LabeledIntent 中以用于显示目的

    要求 我的应用程序中有一个 共享 按钮 我需要通过 Facebook 分享 我需要选择是否安装原生 Facebook 应用程序 我们的决定是 如果未安装该应用程序 则将用户发送到 facebook com 进行分享 当前状态 我可以检测何时
  • 按日期对 RecyclerView 进行排序

    我正在尝试按日期对 RecyclerView 进行排序 但我尝试了太多的事情 我不知道现在该尝试什么 问题就出在这条线上适配器 notifyDataSetChanged 因为如果我不放 不会显示错误 但也不会更新 recyclerview

随机推荐

  • n 行的平均值

    我有一个包含三列的数据框 Id Date and Value并希望按平均值对其进行下采样 取接下来的 20 行 构建平均值Value从这 20 行中提取数据并将其添加到具有相同结构的新数据框中 Date应该是 20 行的第一个值 我尝试了这
  • WPF 中的 MVVM - 如何提醒 ViewModel 模型中的更改...还是应该?

    我正在阅读一些 MVVM 文章 主要是this and this 我的具体问题是 如何将模型更改从模型传递到视图模型 在乔什的文章中 我没有看到他这样做 ViewModel 总是向 Model 询问属性 在 Rachel 的例子中 她确实有
  • 使用单独的委托/数据源时的 UITableView 问题

    一般说明 首先 我有一个UITableView它已被放置到使用 Interface Builder 的 Xcode 生成的视图上 视图的文件所有者设置为 Xcode 生成的子类UIViewController 对于这个子类 我添加了以下工作
  • 如何在 Magento 2 中安装语言包?

    我尝试按照以下说明进行操作https mage2 pro t topic 270 and http devdocs magento com guides v2 0 config guide cli config cli subcommand
  • 放大动画

    我在用RotateAnimation对于图像 但我也想用动画放大图像 意味着当我的图像旋转时图像也 会缩放 如何使用旋转动画进行缩放 在 anim xml 中 您可以像这样使用比例
  • Windows 10 操作系统上的 Windows 窗体图形问题

    当我在 Windows 10 中运行任何 Windows 窗体应用程序时 窗口内的图形似乎扭曲 在设计时这不会发生 有人经历过这个吗 请打开图片以便看得更清楚 NET Framework gt 4 7 的更新答案 打开 app config
  • 当设备可以在应用程序级别强制使用深色主题时,如何避免在我的应用程序中强制使用深色主题?

    有些设备 例如Poco F2 Pro 可以在不兼容深色主题的应用程序中强制使用深色主题 例如 我的应用程序有这个主题 并且与深色主题不兼容
  • 在gdb中,如何将字符串写入内存?

    使用 gdb 将整数或十六进制写入内存地址非常简单 gdb set int 0x08040000 42 gdb set int 0x08040000 0xffffffff 但是我怎样才能以类似简单的方式将字符或整个字符串写入内存呢 现在我必
  • 如何使用反射将事件处理程序附加到事件?

    我知道关于EventInfo AddEventHandler 可用于将处理程序附加到事件的方法 但是 如果我什至无法定义事件处理程序的正确签名 例如 我什至没有引用处理程序所需的事件参数 该怎么办 我将用正确的代码解释问题 当我的解决方案中
  • 如何在 PdfPCell 中居中对齐模板元素

    我正在构建一个垂直的月份列表 以及每个月的水平天数列表 我每天都会添加一个尺寸和颜色的矩形 大小和颜色取决于数据库查询的值 我在用PdfPTable PdfPCell and cbCreateTemplate提供于这个答案 除了矩形的位置之
  • 无法运行 Robolectric 测试

    我继续得到 java lang NoClassDefFoundError android content pm PackageManager NameNotFoundException java lang ClassNotFoundExce
  • 使用基于 Java 密钥存储中的别名的单个证书

    我有一个密钥库 其中添加了多个密钥和证书 我想使用基于密钥存储中的别名的证书并将其用于 SSL 我尝试设置以下系统属性但没有任何帮助 System setProperty javax net ssl keyAlias abcd System
  • 哪个 iOS 类/代码返回磁北?

    我想获取设备与磁北的偏差 以度为单位 并在我正在编写的一些代码中使用该值 我不想使用设备的定位服务 因此我对获取真北不感兴趣 而是对磁北感兴趣 仅使用设备的磁力计 哪个类 或编码过程 可以为我提供该值 仅依赖于磁力计 CLLocationM
  • PHP 中可以有嵌套类吗?

    我不是在谈论继承 我不是在谈论嵌套对象 我在说话 System Web Templating 一种筑巢 这些是您不应该创建实例的类 所以 No 但是 您可以通过在 getInstance 中返回实例化对象来执行类似的操作 myClass g
  • 谷歌地图使用 PHP 在 MySQL 中保存多边形和点

    现在我有一个应用程序 允许用户在谷歌地图上绘制多边形 我需要使用 PHP 和 MySQL 保存这个多边形 但我不确定最佳实践 我应该启用空间扩展并保存几何图形吗 我应该将每个垂直 纬度 经度对 保存在数组中吗 我不知道的另一种方法 我想知道
  • Internet Explorer 中触发 window.resize 事件

    如您所知 在 Internet Explorer 中 当页面上的任何元素调整大小时 将触发 window resize 事件 页面元素是否通过分配 更改其高度或样式属性 通过简单地向其添加子元素或其他方式来调整页面元素的大小并不重要 即使元
  • C# 数据集访问数据库

    我有一个从 csv 文件动态创建的数据集 我想要做的是将行插入到我的 MS Access 表中 但我不知道从哪里开始 数据集中数据的标头可能会因顺序而异 但标头的名称将始终与 Access 数据库匹配 我是否必须在插入命令中静态调用标头名称
  • WPF 将用户控件属性绑定到父属性

    我创建了一个用户控件 它有 2 个依赖属性 我想将这些依赖属性绑定到 mainViewModel 的属性 以便每当用户控件中的某些内容发生更改时 父级的属性都会更新 我尝试过 可以正常绑定 但没有成功 如何将用户控件的 DP 绑定到父级的属
  • javascript中的第n个孩子

    这是我的 jquery 代码 我需要 javascript 代码来选择第 n 个孩子 是否可以使用 javascript 选择第 n 个孩子
  • 如何剪辑或剪切可组合项?

    如何剪辑或剪切可组合内容以使图像 按钮或可组合项具有自定义形状 这个问题不是关于使用Modifier clip 更像是用替代方法来完成任务 这些方法允许产生不可能的结果 或者当很难创建像云或方圆这样的形状时 This is 分享您的知识 问