首页
开发技巧正文内容

如何使用Jetpack Compose创建可多选的照片网格

2023年11月10日
阅读时长 2 分钟
阅读量 5
如何使用Jetpack Compose创建可多选的照片网格

在Compose中打磨UI体验

许多应用程序提供了某种形式的多选行为,您可以通过拖动选择整个范围的元素。例如,Google相册允许您轻松选择一系列照片进行共享、添加到相册或删除。在本博文中,我们将实现类似的行为,目标是:

具有多选功能的精美图像网格

我们将采取以下步骤来实现这个最终结果:

  • 实现一个基本网格

  • 为网格元素添加选择状态

  • 添加手势处理,以便我们可以通过拖动选择/取消选择元素

  • 完善元素的外观,使其看起来像照片

只想看代码?这是完整代码

实现网格

我们将此网格实现为LazyVerticalGrid,以便应用程序在所有屏幕尺寸上都能正常工作。较大的屏幕将显示更多列,较小的屏幕将显示较少列。

尽管我们目前只显示一个简单的彩色Surface,但我们已经将元素称为photos。只需这几行代码,我们就已经有了一个可以滚动的漂亮网格:

一个非常基本的网格让我们开始

添加选择状态

然而,一个简单的网格在我们的多选旅程中并没有带给我们太多。我们需要跟踪当前选择的项目以及我们当前是否处于选择模式,并使我们的元素反映出这种状态。

首先,让我们将网格项提取到它们自己的组合中,以反映它们的选择状态。这个组合将:

  • 如果用户不处于选择模式,则为空

  • 当用户处于选择模式且元素未被选择时,显示一个空的单选按钮

  • 当用户处于选择模式且元素已被选择时,显示一个勾选标记

项目的各种选择状态

这个组合是_无状态的_,因为它不保存任何自己的状态。它只是反映您传递给它的状态。

为了使项目响应其选择状态,网格应该跟踪这些状态。此外,用户应该能够通过与网格中的项目交互来更改所选值。现在,当用户点击它时,我们将简单地切换项目的选择状态:

我们在一个集合中跟踪所选项目。当用户点击其中一个ImageItem实例时,将添加或从集合中删除该项目的ID。

我们通过检查是否有任何当前选择的元素来定义是否处于选择模式。每当所选ID的集合发生变化时,此变量将自动重新计算。

通过这个添加,我们现在可以通过点击来添加和删除选择的元素:

这看起来像一个花哨的井字游戏!

手势处理

现在我们正在跟踪状态,我们可以实现正确的手势,以添加和删除选择的元素。我们的要求如下:

  1. 通过长按元素进入选择模式

  2. 长按后拖动以添加或删除起始元素和目标元素之间的所有元素

  3. 在选择模式下,通过点击元素添加或删除元素

  4. 对已选择的元素进行长按不会产生任何效果

第二个要求是最棘手的。由于我们需要在拖动过程中调整所选ID的集合,我们需要将手势处理添加到网格中,而不是元素本身。我们需要进行自己的命中检测,以确定指针当前指向网格中的哪个元素。这可以通过LazyGridState和拖动变化位置的组合来实现。

首先,让我们将LazyGridState提取到懒网格之外,并将其传递给我们自定义的手势处理程序。这样我们就可以读取网格信息并在其他地方使用它。具体来说,我们可以使用它来确定用户当前指向网格中的哪个项目。

我们可以利用pointerInput修饰符和detectDragGesturesAfterLongPress方法来设置我们的拖动处理:

正如您在此代码片段中所看到的,我们在手势处理程序中内部跟踪initialKeycurrentKey。我们需要在拖动开始时设置初始键,并在用户使用指针移动到不同元素时更新当前键。

让我们首先实现onDragStart

逐步解释这个方法,它:

  1. 查找指针下方的项目的键(如果有)。这表示用户长按并将从该元素开始进行拖动手势。

  2. 如果找到一个项目(用户指向网格中的一个元素),则检查该项目是否仍未选择(从而满足要求4)。

  3. 将初始键和当前键都设置为此键值,并主动将其添加到所选元素列表中。

我们必须自己实现帮助方法gridItemKeyAtPosition

对于网格中的每个可见项目,此方法检查hitPoint是否落在其边界内。

现在我们只需要更新onDrag lambda,该lambda将在用户在屏幕上移动指针时定期调用:

只有在设置了初始键时才处理拖动。根据初始键和当前键,此lambda将更新所选项目的集合。它确保选择初始键和当前键之间的所有元素。

通过这个设置,我们现在可以拖动选择多个元素:

包括对勾的拖动支持

最后,我们需要替换单个元素的可点击行为,以便在选择模式下添加/删除它们。这也是开始考虑此手势处理的可访问性的正确时间。我们使用onLongClick 语义属性来为辅助功能服务的用户提供替代选择机制,让他们通过长按元素进入选择模式。我们通过设置semantics修饰符来实现这一点。

semantics修饰符允许您覆盖或添加辅助功能服务用于在不依赖触摸的情况下与屏幕交互的属性和操作处理程序。大多数情况下,Compose系统会自动为您处理这一点,但在这种情况下,我们需要显式添加长按行为。

此外,通过为项目使用toggleable修饰符(仅在用户处于选择模式时添加),我们确保Talkback可以向用户提供有关项目当前选择状态的信息。

在拖动期间添加滚动

如您在上面的屏幕录制中所见,我们目前无法拖动超过屏幕的顶部和底部边缘。这限制了选择机制的功能。我们希望在指针接近屏幕边缘时,网格可以滚动。此外,我们应该在用户将指针移动到屏幕边缘越近时滚动得更快。

期望的最终结果:

这么多对勾!

首先,我们将更改我们的拖动处理程序,以便根据与容器顶部或底部的距离设置滚动速度:

如您所见,我们根据阈值和距离更新滚动速度,并确保在拖动结束或取消时重置滚动速度。

现在,从手势处理程序更改此滚动速度值还没有任何效果。我们需要更新PhotoGrid组合,以在值更改时开始滚动网格:

每当滚动速度变量的值更改时,LaunchedEffect将重新触发,滚动将重新开始。

您可能想知道为什么我们没有直接从onDrag处理程序中更改滚动级别。原因是onDrag lambda 仅在用户实际移动指针时调用!因此,如果用户在屏幕上保持手指静止,滚动将停止。您可能在应用程序中注意到这个滚动错误,在那里您需要“刷”屏幕底部才能让它滚动。

最后的修饰

通过这最后的添加,我们的网格行为非常稳定。然而,它看起来与我们在博客文章开头的示例不太相似。让我们确保网格项反映实际的照片:

<script src="https://gist.github.com/JolandaVerhoef/564843ab167b16f8adc1271c5e12c6f6.js"></script>

如你所见,我们扩展了照片列表,除了id外还有一个URL。使用该URL,我们可以在网格项中加载一张图片。在切换选择模式时,该图片的填充和角形状会发生变化,我们使用动画使这种变化平滑过渡。

最终结果。是不是很漂亮?

结论

在这个GitHub代码片段中查看完整代码。我们只用了不到200行代码,就创建了一个包含丰富交互的强大用户界面。

代码片段许可证:

版权所有 2023 Google LLC。
SPDX-License-Identifier: Apache-2.0
免责声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

相关文章

探索多种软件架构模式及其实用应用
2024年11月22日19:06
本文深入探讨了多种软件架构模式,包括有界上下文、边车模式、发布-订阅模式、应用网关、微服务、命令职责分离(CQRS)等,介绍了它们的优点、使用场景以及具体应用实例。文章强调根据具体项目需求和团队能力选择最合适的架构,以构建高效和可维护的解决方案,同时展示了各架构模式间的综合应用,提供了丰富的案例和技术细节。
15个高级Python快捷键助您更快编程
2024年11月21日07:02
本文分享了 15 个高级的 Python 编程快捷键,包括上下文管理器、行内字典合并、函数参数解包、链式比较、dataclasses、海象运算符、反转列表、备忘录缓存、splitlines、enumerate、字典推导、zip 用于并行迭代、itertools.chain 扁平化列表、functools.partial 部分函数和 os.path 文件路径管理等,帮助开发者提高编程效率和代码简洁性。
揭示网页开发的 11 个迷思:停止相信这些误区
2024年11月19日22:05
网页开发充满误解,这篇博文针对11个常见迷思进行揭秘。包括网站开发后不需更新、需掌握所有技术、AI会取代开发者等。强调持续学习、专业化、用户体验的重要性,澄清误区如多任务处理的必要性和最新技术的必需性。文章提醒开发者注重实用而非追求完美代码,以务实态度面对开发工作。
你知道 CSS 的四种 Focus 样式吗?
2024年11月18日21:41
本文介绍了四种 CSS focus 样式::focus、:focus-visible、:focus-within 以及自定义的 :focus-visible-within,帮助提升网站用户体验。:focus 样式应用于被选中元素;:focus-visible 仅在键盘导航时显示;:focus-within 用于父元素;自定义 :focus-visible-within 结合两者效果。合理运用这些样式能使网站更方便键盘用户导航。
利用 Python 实现自动化图像裁剪:简单高效的工作流程
2024年11月11日20:49
使用 Python 和 OpenCV 自动裁剪图像,轻松实现 16:9 的完美构图。这个指南介绍了如何通过代码进行灰度化、模糊处理和边缘检测,最终识别出最重要的部分进行裁剪。特别适合需要批量处理图像的情况,节省大量时间。
每位资深前端开发人员都应了解的 TypeScript 高级概念
2024年11月11日02:07
资深前端开发者应了解 TypeScript 的高级概念,如联合类型、交叉类型、类型保护、条件类型、映射类型、模板字面量类型和递归类型。这些特性可提升代码的可维护性和可扩展性,确保在开发复杂应用时实现更高的类型安全性和效率。