当前位置: 首页 > ds >正文

Compose笔记(四十六)--Popup

         这一节主要了解一下Compose中的Popup,在Jetpack Compose中,Popup是一个用于在当前界面上方显示悬浮内容的组件,类似于原生Android中的PopupWindow。它可以在不影响底层布局的情况下展示临时内容,是实现提示、菜单、气泡等交互元素的核心组件,简单总结:

API
popupPositionProvider
用于计算弹窗相对于锚点的位置,是一个接口,需通过实现类提供位置逻辑,系统会在每次布局或弹窗状态变化时调用此提供者,确保弹窗位置实时更新。
核心方法:calculatePosition(),返回弹窗左上角相对于锚点的像素偏移量(IntOffset)。
onDismissRequest
当用户点击弹窗外部区域或按下返回键时触发,必须实现(否则弹窗可能无法关闭),通常用于将弹窗状态设为false。
properties
弹窗的附加属性,常用配置:
focusable:是否允许弹窗获取焦点(默认false,设为true可在弹窗显示时阻止底层组件交互)。
dismissOnBackPress:是否允许按返回键关闭(默认 true)。
dismissOnClickOutside:是否允许点击外部关闭(默认 true)。
content
弹窗内的内容,通常是Surface、Card等包裹的文本、图标或按钮组合。

场景
1 提示信息,如操作成功/失败的临时提示、表单输入的错误提示等,通常几秒后自动消失。
2 快捷操作菜单,点击图标(如右上角「更多」图标)后显示的操作菜单,类似下拉菜单。
3 气泡提示,长按或悬停在组件上时显示的解释性文字。
4 上下文菜单,长按列表项时显示的与该条目相关的操作(如编辑、删除)。
5 轻量选择器,如日期、时间的简易选择弹窗,无需跳转新页面。

栗子:

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties@Composable
fun PopupDemo() {var showPopup by remember { mutableStateOf(false) }val density = LocalDensity.currentval positionProvider = object : PopupPositionProvider {override fun calculatePosition(anchorBounds: IntRect,windowSize: IntSize,layoutDirection: androidx.compose.ui.unit.LayoutDirection,popupContentSize: IntSize): IntOffset {return with(density) {IntOffset(x = anchorBounds.left,y = anchorBounds.bottom + 8.dp.roundToPx() )}}}Column(modifier = Modifier.fillMaxSize().padding(20.dp),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {Text(text = "提示弹窗示例",style = MaterialTheme.typography.titleMedium,modifier = Modifier.padding(bottom = 20.dp))Button(onClick = { showPopup = !showPopup }) {Text(if (showPopup) "隐藏提示" else "显示提示")}if (showPopup) {Popup(popupPositionProvider = positionProvider,onDismissRequest = { showPopup = false }, properties = PopupProperties(focusable = false)) {Surface(color = Color(0xFFE3F2FD),shape = MaterialTheme.shapes.small,) {Text(text = "这是一个提示信息",modifier = Modifier.padding(12.dp),color = Color(0xFF0D47A1))}}}}
}
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupPositionProvider
import androidx.compose.ui.window.PopupProperties@Composable
fun PopupDemo() {var showMenu by remember { mutableStateOf(false) }val density = LocalDensity.currentval positionProvider = object : PopupPositionProvider {override fun calculatePosition(anchorBounds: IntRect,windowSize: IntSize,layoutDirection: androidx.compose.ui.unit.LayoutDirection,popupContentSize: IntSize): IntOffset {return with(density) {IntOffset(x = anchorBounds.right - popupContentSize.width,y = anchorBounds.top + 8.dp.roundToPx() )}}}Column(modifier = Modifier.fillMaxSize().padding(20.dp),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {Text(text = "操作菜单弹窗Demo",style = MaterialTheme.typography.titleMedium,modifier = Modifier.padding(bottom = 20.dp))Box(modifier = Modifier.size(48.dp)) {IconButton(onClick = { showMenu = !showMenu }) {Icon(imageVector = Icons.Default.MoreVert,contentDescription = "更多操作")}if (showMenu) {Popup(popupPositionProvider = positionProvider,onDismissRequest = { showMenu = false },properties = PopupProperties(focusable = true)) {Surface(modifier = Modifier.wrapContentSize(),shape = MaterialTheme.shapes.small,) {Column {Box(modifier = Modifier.fillMaxWidth().clickable {showMenu = false}.padding(12.dp)) {Text("复制")}Box(modifier = Modifier.fillMaxWidth().clickable {showMenu = false}.padding(12.dp)) {Text("分享")}Box(modifier = Modifier.fillMaxWidth().clickable {showMenu = false}.padding(12.dp)) {Text("删除", color = Color(0xFFD32F2F))}}}}}}}
}

注意
1 动态定位:popupPositionProvider需处理屏幕边界,避免弹窗被截断。
2 状态管理:确保onDismissRequest 正确更新弹窗状态,防止内存泄漏。
3 性能优化:避免在Popup内容中频繁重组。
4 处理焦点与交互冲突:若弹窗包含可点击元素,需将properties=PopupProperties(focusable=true),否则点击可能穿透到底层组件。底层组件需避免在弹窗显示时响应点击

http://www.xdnf.cn/news/18193.html

相关文章:

  • 廖雪峰-java教程-Part01
  • RK3588开发板Ubuntu系统烧录
  • 如何利用gemini-cli快速了解一个项目以及学习新的组件?
  • GitHub Copilot:AI编程助手的架构演进与真实世界影响
  • 【102页PPT】新一代数字化转型信息化总体规划方案(附下载方式)
  • 第七十九:AI的“急诊科医生”:模型失效(Loss Explode)的排查技巧——从“炸弹”到“稳定”的训练之路!
  • 为什么神经网络在长时间训练过程中会存在稠密特征图退化的问题
  • AI+预测3D新模型百十个定位预测+胆码预测+去和尾2025年8月17日第163弹
  • 内网穿透系列十一:NPS 是一款轻量级、高性能、功能强大的内网穿透工具,自带Web管理端,支持Docker快速部署
  • Win10快速安装.NET3.5
  • Web全栈项目中健康检查API的作用(现代云原生应用标准实践)(health check、healthcheck、livenessProbe、健康探针)
  • 博士招生 | 香港大学 机器增强认知实验室 招收博士生/实习生/访问学生
  • File 类的用法和 InputStream, OutputStream 的用法
  • Python列表与元组:数据存储的艺术
  • 车载诊断架构 --- 怎么解决对已量产ECU增加具体DTC的快照信息?
  • python---模块
  • CentOS7安装使用FTP服务
  • java内存模型:
  • 新字符设备驱动实验
  • DBngin:告别数据库多版本环境管理的烦恼
  • 后台管理系统-4-vue3之pinia实现导航栏按钮控制左侧菜单栏的伸缩
  • 如何解决C盘存储空间被占的问题,请看本文
  • 数据清洗:数据处理的基石
  • 【完整源码+数据集+部署教程】太阳能面板污垢检测系统源码和数据集:改进yolo11-RVB-EMA
  • IO流与单例模式
  • 【101页PPT】芯片半导体企业数字化项目方案汇报(附下载方式)
  • ArrayList的扩容源码分析
  • 1083. 数列极差问题
  • duiLib 实现鼠标拖动标题栏时,窗口跟着拖动
  • K8s核心组件全解析