背景
学习了上一节的窗口位置变化相关的内容后,在窗口移动过程过程中发现有一个限制问题,大家可以看一下如下动态图:
已经尽力把窗口想要拖到屏幕外面,但是一直拖到不生效,只能在屏幕内部进行移动,这个到其实很奇怪,因为对于LayoutParam的x,y坐标是有进行设置的,为啥设置了就不生效呢?
这里看看是不是应用层面还是哪里限制了,可以通过wms的Relayout的Attribute属性看看,明显看到有负数坐标,即app层面已经把移除屏幕的x坐标传递到了wms,但是最后wms并没有让这个传递来的坐标生效。
分析为啥不可以超出屏幕边界
这里要分析就一样也要日志中开始分析
frameworks/base/core/java/android/view/WindowLayout.java
前面分析位置变化就这里Gravity.apply坐标就有了变化,这里我们看看日志:
可以看到这里的Gravity.apply执行完成后确实x方向坐标是负的,但是继续往下看看computeFrames自带的日志:
却得到如下结果:
可以看到x的坐标其实从-93变成了0,这里最后的打印也就是实际显示的,那么到底是哪里吧-93变成0呢?
这里再查阅代码发现有个二fitToDisplay,即这里看着有个适配屏幕操作,也会改变这个坐标,这里也加入一下打印
从日志也可以看出来:
剖析一下fitToDisplay
final boolean fitToDisplay = !inMultiWindowMode
|| ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
注意可以看到这里有多个限制,如果想让fitToDisplay不为true,其实还是有较多方法,这里可以考虑noLimits
final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
再看看applyDisplay方法
public static void applyDisplay(int gravity, @NonNull Rect display, @NonNull Rect inoutObj) {
if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) {
if (inoutObj.top < display.top) inoutObj.top = display.top;
if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom;
} else {
int off = 0;
if (inoutObj.top < display.top) off = display.top-inoutObj.top;
else if (inoutObj.bottom > display.bottom) off = display.bottom-inoutObj.bottom;
if (off != 0) {
if (inoutObj.height() > (display.bottom-display.top)) {
inoutObj.top = display.top;
inoutObj.bottom = display.bottom;
} else {
inoutObj.top += off;
inoutObj.bottom += off;
}
}
}
if ((gravity&DISPLAY_CLIP_HORIZONTAL) != 0) {
if (inoutObj.left < display.left) inoutObj.left = display.left;
if (inoutObj.right > display.right) inoutObj.right = display.right;
} else {
int off = 0;
if (inoutObj.left < display.left) off = display.left-inoutObj.left;
else if (inoutObj.right > display.right) off = display.right-inoutObj.right;
if (off != 0) {
if (inoutObj.width() > (display.right-display.left)) {
inoutObj.left = display.left;
inoutObj.right = display.right;
} else {
inoutObj.left += off;
inoutObj.right += off;
}
}
}
}
明显可以看出这里会对设置的frame的left和display的left,这样就把我们最后窗口显示限制在display以内。
那么问题就清楚了,其实本质就是因为computeFrames的里面有个applyDisplay方法会判断是否超出屏幕,从而让window限制显示在屏幕里面。
解决超出屏幕限制方法:
上面分析fitToDisplay有提到这个标志就是限制根本
final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0;
final boolean fitToDisplay = !inMultiWindowMode
|| ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits);
但是这个标志实际上app层面是可以控制的,考虑把这里的noLimits对应的FLAG_LAYOUT_NO_LIMITS带上,尝试看看
再运行测试如下:
完美运行,可以正常超出屏幕边界。
投屏专题部分:
https://mp.weixin.qq.com/s/IGm6VHMiAOPejC_H3N_SNg
更多framework详细代码和资料参考如下链接
hal+perfetto+surfaceflinger
https://mp.weixin.qq.com/s/LbVLnu1udqExHVKxd74ILg
其他课程七件套专题:
点击这里
https://mp.weixin.qq.com/s/Qv8zjgQ0CkalKmvi8tMGaw
视频试看:
https://www.bilibili.com/video/BV1wc41117L4/
参考相关链接:
https://blog.csdn.net/zhimokf/article/details/137958615
更多framework假威风耗:androidframework007