我在开发视频播放时,使用系统默认控件VideoView,然后增加MediaController来控制播放。然而,最近在MI 8(刘海屏手机)测试时,发现控制区域的进度条没有显示!开始以为是操作系统差异,没有去跟踪问题。后来在切换为横屏时,发现存在进度条,但是控件往右边偏移了一段距离。界面上还有其它控件,但是它们位置是正确的,只有MediaController位置有问题。
之后,我再找了一台手机(非刘海屏手机),发现控制区域显示正常,而且进度条不管在竖屏还是横屏都是统一的。这时基本确定问题在刘海屏。
播放视频的界面主题使用全屏。然而,发现全屏后,刘海区域自动填黑,主体布局偏移到刘海下面。当初iOS出现刘海屏后,很多播放器应用在全屏时内容会顶到刘海区域。由于显示内容被刘海遮挡,大家开始把刘海区域偏移开来。从MI 8这个表现来看,它也是采用这种兼容方案。系统在全屏时,自动把主体内容偏离刘海区域。现在的问题是,这种系统行为为何影响到主体内容。正常情况下,系统全局偏移,主体内容都应该偏离才是,但为何只有MediaController有问题,其它控件没有问题。
查看MediaController的源码,发现它的内容是附着在一个新的window。继续查看可以发现,它的位置通过anchor的更新事件来同步刷新,具体实现逻辑在updateFloatingWindowLayout()。查看VideoView源码(attachMediaController)可以发现,MediaController的anchor被设置为VideoView的父界面(存在的时候)。在anchor更新时,MediaController的坐标通过anchor的getLocationOnScreen()计算出来。
getLocationOnScreen()获取的坐标是相对屏幕。getLocationInWindow()获取的坐标是相对窗口。getLocationOnScreen()是getLocationInWindow()和AttachInfo一起计算出来的。调试发现,在全屏时,以上两个函数获取的坐标都是正确的。其中getLocationOnScreen()有明确偏离刘海区域,getLocationInWindow()则保持(0,0)。因此MediaController应该使用getLocationInWindow()来更新位置,而不是getLocationOnScreen()。这里提一点,向WindowManager增加布局时,坐标(0,0)并不一定是屏幕的左上角,系统的全局偏移可能会改变这个起点。MediaController的这种设计,其实认为窗口的起点就在屏幕左上角。
private void updateFloatingWindowLayout() {
int [] anchorPos = new int[2];
mAnchor.getLocationOnScreen(anchorPos);
// we need to know the size of the controller so we can properly position it
// within its space
mDecor.measure(MeasureSpec.makeMeasureSpec(mAnchor.getWidth(), MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(mAnchor.getHeight(), MeasureSpec.AT_MOST));
WindowManager.LayoutParams p = mDecorLayoutParams;
p.width = mAnchor.getWidth();
p.x = anchorPos[0] + (mAnchor.getWidth() - p.width) / 2;
p.y = anchorPos[1] + mAnchor.getHeight() - mDecor.getMeasuredHeight();
}
我的处理方案是新建一个VideoView的父布局,然后重写getLocationOnScreen()。该函数使用getLocationInWindow()的结果。当然,如果自定义MediaController,直接修改获取位置的逻辑即可。
@Override
public void getLocationOnScreen(int[] outLocation) {
super.getLocationInWindow(outLocation);
}