使用相机流与地图时无法动态调整ConstraintLayout子控件的z-index
我最近在做一个搭载DJI MSDK5相机流和MapBox地图的Android应用,碰到个挺棘手的问题,想跟大伙唠唠,也给有类似需求的朋友提个醒。
我的布局是基于ConstraintLayout做的,核心有两个控件:
- 一个是
WidgetFPV,内部带个Surface专门用来渲染DJI的实时相机流画面 - 另一个是
mapCard卡片控件,里面嵌套了MapBox的地图Fragment,默认缩在屏幕左下角当小窗口
我做了个交互逻辑:用户点击小地图,它就会铺满整个屏幕,同时把FPV流窗口缩小到角落。但实际测试时发现,当地图完全展开、FPV缩成小窗口后,FPV的画面居然还压在地图上面,把地图的一部分给挡住了,完全不符合预期。
我一开始以为只是约束没调好,反复检查了ConstraintSet的代码,确认大小和位置都设对了,但层级的问题就是没解决。后来才反应过来,ConstraintLayout的子控件层级默认是按XML里的声明顺序来的——先写的控件在底层,后写的在上层,动态调整大小不会自动改变这个层级。
折腾了大半天,我试出了两个亲测有效的解决办法:
直接调用View的层级调整方法
在处理地图/FPV切换的点击事件里,用bringToFront()和sendToBack()直接把需要显示在上面的控件调到最上层:// 地图最大化时,把地图移到最上层,FPV移到下层 mapCard.bringToFront() widgetFPV.sendToBack() // 切回FPV最大化时,反过来操作 widgetFPV.bringToFront() mapCard.sendToBack()这里要注意,调用完这两个方法后,必须让父布局重新请求布局并刷新,不然可能不会立即生效:
val parentLayout = widgetFPV.parent as ViewGroup parentLayout.requestLayout() parentLayout.invalidate()通过调整elevation属性控制层级
Android里控件的elevation值会直接影响显示层级,值越大的控件越靠上。我在切换逻辑里给两个控件设置不同的elevation:// 地图展开时,给地图设更高的elevation mapCard.elevation = 12f widgetFPV.elevation = 2f // 切回FPV时,交换两者的elevation值 widgetFPV.elevation = 12f mapCard.elevation = 2f这个方法更优雅,不用手动触发父布局刷新,系统会自动处理层级,要是你的控件带阴影效果,视觉上还会更自然。
另外要是你也用了Fragment嵌套地图,万一上面两个方法都不管用,可能是Fragment的视图层级和普通View不一样,这时候可以试试暂时把FPV控件设为INVISIBLE,等地图切换完成后再设为VISIBLE——不过这个方法会导致相机流短暂中断,对实时性要求高的场景不太推荐。
内容来源于stack exchange

