PopupWindow定位问题

PopupWindow定位问题

神马笔记录音功能采用PopupWindow作为容器实现录音编辑器功能,在适配刘海屏时出现了定位问题。

一、目标

在荣耀畅玩7上,显示一切正常,完美定位。

二、刘海屏问题

完全相同的代码,在红米6Pro上出现了问题。

只有在打开“隐藏屏幕刘海”和采用“经典导航键”时,相当于关闭了所谓“全面屏”功能后,界面显示是正常的。

隐藏屏幕刘海 系统导航方式 截图
关闭 全面屏手势
关闭 经典导航键
打开 全面屏手势
打开 经典导航键

三、问题分析

1. 实现思路

控件采用右下对齐方式,通过设置paddingBottom实现对齐到anchor。

2. 实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int getPaddingBottom(View anchor) {

int y;

int bottom;
View rootView = anchor.getRootView();

// root view relative to display
{
DisplayMetrics dm = this.displayMetrics;
Display d = context.getWindowManager().getDefaultDisplay();
d.getMetrics(dm);
// d.getRealMetrics(dm); // must consider navigation bar
int realHeight = dm.heightPixels;

Rect visibleDisplayFrame = this.bounds;
rootView.getWindowVisibleDisplayFrame(visibleDisplayFrame);

y = realHeight - visibleDisplayFrame.bottom;
bottom = visibleDisplayFrame.bottom;
}

// anchor relative to root
if (rootView instanceof ViewGroup) {
Rect anchorRect = this.bounds;
anchorRect.set(0, 0, anchor.getWidth(), anchor.getHeight());

((ViewGroup)rootView).offsetDescendantRectToMyCoords(anchor, anchorRect);

int offset = (bottom - anchorRect.bottom);
y += offset;
}

return y;
}

3. 问题所在

paddingBottom的计算分成2步

  1. 计算根控件的paddingBottom
  2. 计算anchor与根控件的paddingBottom

2个步骤的计算都可能存在问题。

Display提供了2个接口,getMetricsgetRealMetrics,非全面屏必须使用getMetrics接口获取的才是去掉导航条的高度。

其次,anchor与根控件的paddingBottom的计算也可能存在问题。

虽然通过offsetDescendantRectToMyCoords计算出anchor相对于根控件的位置,但根控件的高度是多少,并无法明确确定。

四、解决方案

采用paddingTop的方案。

paddingTop方案能准确计算出相对位置,问题是必须首先明确自身的高度才可能。

采用固定控件高度的方式来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int getPaddingTop(View anchor) {

int y = 0;

View rootView = anchor.getRootView();

// anchor relative to root
if (rootView instanceof ViewGroup) {
Rect anchorRect = this.bounds;
anchorRect.set(0, 0, anchor.getWidth(), anchor.getHeight());

((ViewGroup)rootView).offsetDescendantRectToMyCoords(anchor, anchorRect);

y = anchorRect.bottom;
}

if (y > 0) {
int height = context.getResources().getDimensionPixelSize(R.dimen.tapeActionHeight);
y -= height;

y -= this.statusBarHeight;
}

y = y < 0? 0: y;
return y;
}

五、遗留问题

相较于paddingTop方案,更喜欢paddingBottom的方案。

使用paddingBottom可以不用固化控件高度,更加自由一些。

未来再针对刘海屏、水滴屏进行更多测试,希望能改回paddingBottom方案。

六、接下来

继续测试,正确明天7月1日发布2.7.0的正式版本。

七、Finally

尔时世尊。食时。 著衣持钵。入舍卫大城乞食。

于其城中。次第乞已。还至本处。 饭食讫。 收衣钵。 洗足已。 敷座而坐。