Skip to content

Commit 04ccdff

Browse files
committed
重构屏幕旋转后悬浮窗重新计算坐标的代码设计
1 parent 18323fe commit 04ccdff

4 files changed

Lines changed: 110 additions & 104 deletions

File tree

library/src/main/java/com/hjq/window/EasyWindow.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,6 @@ public X setWindowLocation(int gravity, @Px int x, @Px int y) {
291291
mWindowParams.x = x;
292292
mWindowParams.y = y;
293293
delayUpdate();
294-
sendTask(() -> {
295-
if (mWindowDraggableRule != null) {
296-
mWindowDraggableRule.refreshLocationCoordinate();
297-
}
298-
});
299294
return (X) this;
300295
}
301296

@@ -837,7 +832,7 @@ public void showAsDropDown(@NonNull View anchorView, int showGravity, int xOff,
837832
Rect windowVisibleRect = new Rect();
838833
anchorView.getWindowVisibleDisplayFrame(windowVisibleRect);
839834

840-
mWindowParams.gravity = Gravity.TOP | Gravity.START;
835+
mWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
841836
mWindowParams.x = anchorViewLocation[0] - windowVisibleRect.left + xOff;
842837
mWindowParams.y = anchorViewLocation[1] - windowVisibleRect.top + yOff;
843838

@@ -1172,22 +1167,22 @@ public View getContentView() {
11721167
* 获取当前窗口视图的宽度
11731168
*/
11741169
public int getWindowViewWidth() {
1175-
ViewGroup windowRootLayout = getRootLayout();
1176-
if (windowRootLayout == null) {
1170+
ViewGroup rootLayout = getRootLayout();
1171+
if (rootLayout == null) {
11771172
return 0;
11781173
}
1179-
return windowRootLayout.getWidth();
1174+
return rootLayout.getWidth();
11801175
}
11811176

11821177
/**
11831178
* 获取当前窗口视图的高度
11841179
*/
11851180
public int getWindowViewHeight() {
1186-
ViewGroup windowRootLayout = getRootLayout();
1187-
if (windowRootLayout == null) {
1181+
ViewGroup rootLayout = getRootLayout();
1182+
if (rootLayout == null) {
11881183
return 0;
11891184
}
1190-
return windowRootLayout.getHeight();
1185+
return rootLayout.getHeight();
11911186
}
11921187

11931188
/**

library/src/main/java/com/hjq/window/draggable/AbstractWindowDraggableRule.java

Lines changed: 93 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
import android.os.Build;
1010
import android.os.Build.VERSION;
1111
import android.os.Build.VERSION_CODES;
12+
import android.os.Looper;
1213
import android.util.DisplayMetrics;
1314
import android.util.TypedValue;
1415
import android.view.Display;
1516
import android.view.DisplayCutout;
1617
import android.view.Gravity;
1718
import android.view.MotionEvent;
1819
import android.view.View;
19-
import android.view.View.OnLayoutChangeListener;
2020
import android.view.View.OnTouchListener;
2121
import android.view.ViewGroup;
2222
import android.view.Window;
@@ -64,9 +64,6 @@ public abstract class AbstractWindowDraggableRule implements OnTouchListener {
6464
private int mCurrentScreenInvisibleWidth;
6565
private int mCurrentScreenInvisibleHeight;
6666

67-
private int mCurrentViewOnScreenX;
68-
private int mCurrentViewOnScreenY;
69-
7067
/** 当前屏幕的物理尺寸 */
7168
private double mScreenPhysicalSize;
7269

@@ -90,11 +87,9 @@ public void start(@NonNull EasyWindow<?> easyWindow) {
9087
return;
9188
}
9289
mRootLayout.setOnTouchListener(this);
93-
mRootLayout.post(() -> {
94-
refreshWindowInfo();
95-
refreshScreenPhysicalSize();
96-
refreshLocationCoordinate();
97-
});
90+
91+
refreshWindowInfo();
92+
refreshScreenPhysicalSize();
9893
}
9994

10095
/**
@@ -125,7 +120,6 @@ public final boolean onTouch(@NonNull View view, @NonNull MotionEvent event) {
125120
// Github issue 地址:https://github.com/getActivity/EasyWindow/issues/69
126121
refreshWindowInfo();
127122
refreshScreenPhysicalSize();
128-
refreshLocationCoordinate();
129123

130124
mConsumeTouchView = null;
131125
View consumeTouchEventView = findNeedConsumeTouchView(rootLayout, event);
@@ -185,11 +179,11 @@ public void offsetMotionEventLocation(@NonNull View parentView, @NonNull View ch
185179
* 窗口拖拽回调方法
186180
*
187181
* @param easyWindow 当前窗口对象
188-
* @param windowRootLayout 当前窗口视图
182+
* @param rootLayout 当前窗口视图
189183
* @param event 当前触摸事件
190184
* @return 根据返回值决定是否拦截该事件
191185
*/
192-
public abstract boolean onDragWindow(@NonNull EasyWindow<?> easyWindow, @NonNull ViewGroup windowRootLayout, @NonNull MotionEvent event);
186+
public abstract boolean onDragWindow(@NonNull EasyWindow<?> easyWindow, @NonNull ViewGroup rootLayout, @NonNull MotionEvent event);
193187

194188
@Nullable
195189
public EasyWindow<?> getEasyWindow() {
@@ -263,20 +257,6 @@ public int getWindowViewHeight() {
263257
return mEasyWindow.getWindowViewHeight();
264258
}
265259

266-
/**
267-
* 获取 View 在当前屏幕的 X 坐标
268-
*/
269-
public int getViewOnScreenX() {
270-
return mCurrentViewOnScreenX;
271-
}
272-
273-
/**
274-
* 获取 View 在当前屏幕的 Y 坐标
275-
*/
276-
public int getViewOnScreenY() {
277-
return mCurrentViewOnScreenY;
278-
}
279-
280260
/**
281261
* 刷新当前 Window 信息
282262
*/
@@ -356,109 +336,134 @@ public void refreshScreenPhysicalSize() {
356336
mScreenPhysicalSize = Math.sqrt(Math.pow(screenWidthInInches, 2) + Math.pow(screenHeightInInches, 2));
357337
}
358338

359-
/**
360-
* 刷新当前 View 在屏幕的坐标信息
361-
*/
362-
public void refreshLocationCoordinate() {
363-
ViewGroup windowRootLayout = getRootLayout();
364-
if (windowRootLayout == null) {
365-
return;
366-
}
367-
368-
int[] location = new int[2];
369-
windowRootLayout.getLocationOnScreen(location);
370-
mCurrentViewOnScreenX = location[0];
371-
mCurrentViewOnScreenY = location[1];
372-
}
373-
374339
/**
375340
* 屏幕方向发生了改变
376341
*/
377342
public void onScreenOrientationChange() {
378343
// Log.i(getClass().getSimpleName(), "屏幕方向发生了改变");
379-
ViewGroup windowRootLayout = getRootLayout();
380-
if (windowRootLayout == null) {
344+
final ViewGroup rootLayout = getRootLayout();
345+
if (rootLayout == null) {
381346
return;
382347
}
383348

384-
long refreshDelayMillis = 100;
385-
386-
if (!isFollowScreenRotationChanges()) {
387-
EasyWindow<?> easyWindow = getEasyWindow();
388-
if (easyWindow != null) {
389-
easyWindow.sendTask(() -> {
390-
refreshWindowInfo();
391-
refreshScreenPhysicalSize();
392-
refreshLocationCoordinate();
393-
}, refreshDelayMillis);
394-
}
349+
final EasyWindow<?> easyWindow = getEasyWindow();
350+
if (easyWindow == null) {
395351
return;
396352
}
397353

398-
// Log.i(getClass().getSimpleName(), "当前 ViewWidth = " + viewWidth + ",ViewHeight = " + viewHeight);
354+
final WindowManager.LayoutParams windowParams = easyWindow.getWindowParams();
355+
356+
final long refreshDelayMillis = 100;
399357

400-
int startX = mCurrentViewOnScreenX - mCurrentScreenInvisibleWidth;
401-
int startY = mCurrentViewOnScreenY - mCurrentScreenInvisibleHeight;
358+
if (!isFollowScreenRotationChanges()) {
359+
easyWindow.sendTask(() -> {
360+
refreshWindowInfo();
361+
refreshScreenPhysicalSize();
362+
}, refreshDelayMillis);
363+
return;
364+
}
402365

403366
// 这里为什么用 getMinTouchDistance(),而不是 0?
404367
// 因为其实用 getLocationOnScreen 测量出来的值不太准,有时候是 0,有时候是 1,有时候 2
405368
// 但大多数情况是 0 和 1,这里为了兼容这种误差,使用了最小触摸距离来作为基准值
406-
float minTouchDistance = getMinTouchDistance();
369+
final float minTouchDistance = getMinTouchDistance();
370+
// Log.i(getClass().getSimpleName(), "最小触摸距离 minTouchDistance = " + minTouchDistance);
371+
372+
final int screenWidth = getScreenWidth();
373+
final int screenHeight = getScreenHeight();
374+
// Log.i(getClass().getSimpleName(), "屏幕旋转前 screenWidth = " + screenWidth + ",screenHeight = " + screenHeight);
375+
376+
final int screenInvisibleWidth = getScreenInvisibleWidth();
377+
final int screenInvisibleHeight = getScreenInvisibleHeight();
378+
// Log.i(getClass().getSimpleName(), "屏幕旋转前 screenInvisibleWidth = " + screenInvisibleWidth + ",screenInvisibleHeight = " + screenInvisibleHeight);
379+
380+
final int windowViewWidth = getWindowViewWidth();
381+
final int windowViewHeight = getWindowViewHeight();
382+
// Log.i(getClass().getSimpleName(), "屏幕旋转前 windowViewWidth = " + windowViewWidth + ",windowViewHeight = " + windowViewHeight);
383+
384+
final int windowGravity;
385+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
386+
windowGravity = Gravity.getAbsoluteGravity(windowParams.gravity, rootLayout.getLayoutDirection());
387+
} else {
388+
windowGravity = windowParams.gravity;
389+
}
390+
final int windowHorizontalOffset = windowParams.x;
391+
final int windowVerticalOffset = windowParams.y;
392+
393+
int windowHorizontalGravity = windowGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
394+
final int currentViewOnScreenX;
395+
if (windowHorizontalGravity == Gravity.LEFT) {
396+
currentViewOnScreenX = windowHorizontalOffset;
397+
} else if (windowHorizontalGravity == Gravity.RIGHT) {
398+
currentViewOnScreenX = (screenWidth - screenInvisibleWidth) - windowHorizontalOffset;
399+
} else {
400+
// Gravity.CENTER_HORIZONTAL
401+
currentViewOnScreenX = (screenWidth - screenInvisibleWidth - windowViewWidth) / 2 + windowHorizontalOffset;
402+
}
403+
404+
int windowVerticalGravity = windowGravity & Gravity.VERTICAL_GRAVITY_MASK;
405+
final int currentViewOnScreenY;
406+
if (windowVerticalGravity == Gravity.TOP) {
407+
currentViewOnScreenY = windowVerticalOffset;
408+
} else if (windowVerticalGravity == Gravity.BOTTOM) {
409+
currentViewOnScreenY = (screenHeight - screenInvisibleHeight) - windowVerticalOffset;
410+
} else {
411+
// Gravity.CENTER_VERTICAL
412+
currentViewOnScreenY = (screenHeight - screenInvisibleHeight - windowViewHeight) / 2 + windowVerticalOffset;
413+
}
414+
// Log.i(getClass().getSimpleName(), "currentViewOnScreenX = " + currentViewOnScreenX + ",currentViewOnScreenY = " + currentViewOnScreenY);
407415

408416
// 计算水平坐标中心点百分比
409-
double horizontalCoordinatePercent;
410-
if (startX <= minTouchDistance) {
417+
final double horizontalCoordinatePercent;
418+
if (currentViewOnScreenX <= minTouchDistance) {
411419
horizontalCoordinatePercent = 0;
412-
} else if (Math.abs(getScreenWidth() - (startX + getWindowViewWidth())) < minTouchDistance) {
420+
} else if (Math.abs(screenWidth - (currentViewOnScreenX + windowViewWidth)) <= minTouchDistance) {
413421
horizontalCoordinatePercent = 1;
414422
} else {
415423
// 这里因为要计算中心点位置,所以要加上 View 宽度的二分之一,因为坐标是左上开始计算的
416-
double centerX = startX + getWindowViewWidth() / 2f;
417-
horizontalCoordinatePercent = centerX / getScreenWidth();
424+
horizontalCoordinatePercent = (currentViewOnScreenX + windowViewWidth / 2f) / screenWidth;
418425
}
419-
420426
// 计算垂直坐标中心点百分比
421-
double verticalCoordinatePercent;
422-
if (startY <= minTouchDistance) {
427+
final double verticalCoordinatePercent;
428+
if (currentViewOnScreenY <= minTouchDistance) {
423429
verticalCoordinatePercent = 0;
424-
} else if (Math.abs(getScreenHeight() - (startY + getWindowViewHeight())) < minTouchDistance) {
430+
} else if (Math.abs(screenHeight - (currentViewOnScreenY + windowViewHeight)) <= minTouchDistance) {
425431
verticalCoordinatePercent = 1;
426432
} else {
427433
// 这里因为要计算中心点位置,所以要加上 View 高度的二分之一,因为坐标是左上开始计算的
428-
double centerY = startY + getWindowViewHeight() / 2d;
429-
verticalCoordinatePercent = centerY / getScreenHeight();
434+
verticalCoordinatePercent = (currentViewOnScreenY + windowViewHeight / 2d) / screenHeight;
430435
}
436+
// Log.i(getClass().getSimpleName(), "horizontalCoordinatePercent = " + horizontalCoordinatePercent + ",verticalCoordinatePercent = " + verticalCoordinatePercent);
431437

432438
// Github issue 地址:https://github.com/getActivity/EasyWindow/issues/49
433439
// 修复在竖屏状态下,先锁屏,再旋转到横屏,后进行解锁,出现的 View.getWindowVisibleDisplayFrame 计算有问题的 Bug
434440
// 这是因为屏幕在旋转的时候,视图正处于改变状态,此时通过 View 获取窗口可视区域是有问题,会获取到旧的可视区域
435441
// 解决方案是监听一下 View 布局变化监听,在收到回调的时候再去获取 View 获取窗口可视区域
436-
windowRootLayout.addOnLayoutChangeListener(new OnLayoutChangeListener() {
437-
@Override
438-
public void onLayoutChange(View view, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
439-
view.removeOnLayoutChangeListener(this);
440-
view.postDelayed(() -> {
441-
// 先刷新当前窗口信息
442-
refreshWindowInfo();
443-
// 刷新屏幕的物理尺寸
444-
refreshScreenPhysicalSize();
445-
int x = Math.max((int) (getScreenWidth() * horizontalCoordinatePercent - getWindowViewWidth() / 2d), 0);
446-
int y = Math.max((int) (getScreenHeight() * verticalCoordinatePercent - getWindowViewHeight() / 2d), 0);
447-
updateLocation(x, y);
448-
// 需要注意,这里需要延迟执行,否则会有问题
449-
view.post(() -> onScreenRotateInfluenceCoordinateChangeFinish());
450-
}, refreshDelayMillis);
451-
}
442+
Looper.myQueue().addIdleHandler(() -> {
443+
easyWindow.sendTask(() -> {
444+
// 先刷新当前窗口信息
445+
refreshWindowInfo();
446+
// 刷新屏幕的物理尺寸
447+
refreshScreenPhysicalSize();
448+
int newScreenWidth = getScreenWidth();
449+
int newScreenHeight = getScreenHeight();
450+
// Log.i(getClass().getSimpleName(), "屏幕旋转后 screenWidth = " + newScreenWidth + ",screenHeight = " + newScreenHeight);
451+
int newViewOnScreenX = Math.max((int) (newScreenWidth * horizontalCoordinatePercent - windowViewWidth / 2d), 0);
452+
int newViewOnScreenY = Math.max((int) (newScreenHeight * verticalCoordinatePercent - windowViewHeight / 2d), 0);
453+
// Log.i(getClass().getSimpleName(), "屏幕旋转后 newViewOnScreenX = " + newViewOnScreenX + ",newViewOnScreenY = " + newViewOnScreenY);
454+
updateLocation(newViewOnScreenX, newViewOnScreenY);
455+
// 需要注意,这里需要延迟执行,否则会有问题
456+
easyWindow.sendTask(this::onScreenRotateInfluenceCoordinateChangeFinish);
457+
}, refreshDelayMillis);
458+
return false;
452459
});
453460
}
454461

455462
/**
456463
* 屏幕旋转导致悬浮窗坐标发生变化完成方法
457464
*/
458465
protected void onScreenRotateInfluenceCoordinateChangeFinish() {
459-
refreshWindowInfo();
460-
refreshScreenPhysicalSize();
461-
refreshLocationCoordinate();
466+
// default implementation ignored
462467
}
463468

464469
/**
@@ -548,7 +553,6 @@ public void updateWindowCoordinate(int x, int y) {
548553
params.gravity = screenGravity;
549554

550555
mEasyWindow.update();
551-
refreshLocationCoordinate();
552556
}
553557

554558
/**

library/src/main/java/com/hjq/window/draggable/MovingWindowDraggableRule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class MovingWindowDraggableRule extends AbstractWindowDraggableRule {
2323

2424
@SuppressLint("ClickableViewAccessibility")
2525
@Override
26-
public boolean onDragWindow(@NonNull EasyWindow<?> easyWindow, @NonNull ViewGroup windowRootLayout, @NonNull MotionEvent event) {
26+
public boolean onDragWindow(@NonNull EasyWindow<?> easyWindow, @NonNull ViewGroup rootLayout, @NonNull MotionEvent event) {
2727
switch (event.getAction()) {
2828
case MotionEvent.ACTION_DOWN:
2929
// 记录按下的位置(相对 View 的坐标)

library/src/main/java/com/hjq/window/draggable/SpringBackWindowDraggableRule.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public SpringBackWindowDraggableRule(int springBackOrientation) {
5656

5757
@SuppressLint("ClickableViewAccessibility")
5858
@Override
59-
public boolean onDragWindow(@NonNull EasyWindow<?> easyWindow, @NonNull ViewGroup windowRootLayout, @NonNull MotionEvent event) {
59+
public boolean onDragWindow(@NonNull EasyWindow<?> easyWindow, @NonNull ViewGroup rootLayout, @NonNull MotionEvent event) {
6060
switch (event.getAction()) {
6161
case MotionEvent.ACTION_DOWN:
6262
// 记录按下的位置(相对 View 的坐标)
@@ -111,7 +111,14 @@ public boolean onDragWindow(@NonNull EasyWindow<?> easyWindow, @NonNull ViewGrou
111111
* 触发回弹 View 到屏幕边缘
112112
*/
113113
public void dispatchSpringBackViewToScreenEdge() {
114-
dispatchSpringBackViewToScreenEdge(getViewOnScreenX(), getViewOnScreenY());
114+
ViewGroup rootLayout = getRootLayout();
115+
if (rootLayout == null) {
116+
return;
117+
}
118+
119+
int[] location = new int[2];
120+
rootLayout.getLocationOnScreen(location);
121+
dispatchSpringBackViewToScreenEdge(location[0], location[1]);
115122
}
116123

117124
/**

0 commit comments

Comments
 (0)