-
-
Notifications
You must be signed in to change notification settings - Fork 190
Description
你觉得框架有什么不足之处?【必答】
我做了以下关键改动说明(便于你快速 review):
refreshWindowInfo():
使用 getRealMetrics() 获取真实屏幕宽高赋给 mCurrentWindowWidth / mCurrentWindowHeight(fallback 保持兼容)。
仍然用 getWindowVisibleDisplayFrame() 得到 mTempRect 来计算 mCurrentWindowInvisibleWidth / mCurrentWindowInvisibleHeight(用于在不允许进入刘海区时做限制)。
updateLocation(int x, int y, boolean allowMoveToScreenNotch):
如果允许进入刘海区:使用真实屏幕宽高作为边界(可以移动到屏幕任意位置)。
如果不允许进入刘海区:尝试使用 getSafeInsetRect()(若完整有效则用 real screen 与 inset 约束;若无则退回到 visible-area 约束)。
updateWindowCoordinate(...):
在写入 LayoutParams 时(API 28+),若允许进入刘海区则将 layoutInDisplayCutoutMode 置为 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES。
新增 getRealScreenWidth() / getRealScreenHeight() 便于获取真实尺寸。
issue 是否有人曾提过类似的建议?【必答】
否
框架文档是否提及了该问题【必答】
否
是否已经查阅框架文档但还未能解决的【必答】
是
你觉得该怎么去完善会比较好?【非必答】
package com.huoban.ai.huobanai.ease.window.draggable;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.webkit.WebView;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.SeekBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.NestedScrollingChild;
import androidx.core.view.NestedScrollingParent;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;
import com.huoban.ai.huobanai.ease.window.EasyWindow;
/**
- author : Android 轮子哥(基于你提供源码做“终极精准修复”)
- github : https://github.com/getActivity/EasyWindow
- time : 2019/01/04(修改:终极贴边版本)
- desc : 拖拽抽象类(已增强:真实屏幕尺寸 + 刘海适配)
*/
public abstract class AbstractWindowDraggableRule implements OnTouchListener {
@Nullable
private EasyWindow<?> mEasyWindow;
@Nullable
private ViewGroup mRootLayout;
/** 是否允许移动到挖孔屏区域 */
private boolean mAllowMoveToScreenNotch = true;
/** 拖拽回调监听对象(可能为空) */
@Nullable
private OnWindowDraggingListener mWindowDraggingListener;
@NonNull
private final Rect mTempRect = new Rect();
private int mCurrentWindowWidth;
private int mCurrentWindowHeight;
private int mCurrentViewOnScreenX;
private int mCurrentViewOnScreenY;
private int mCurrentWindowInvisibleWidth;
private int mCurrentWindowInvisibleHeight;
/** 当前屏幕物理尺寸 */
private double mPhysicalScreenSize;
/** 需要消费触摸事件的 View(可能为空)*/
@Nullable
private View mConsumeTouchView;
/**
* 判断当前是否处于触摸移动状态
*/
public abstract boolean isTouchMoving();
/**
* 窗口显示后回调这个方法
*/
@SuppressLint("ClickableViewAccessibility")
public void start(@NonNull EasyWindow<?> easyWindow) {
mEasyWindow = easyWindow;
mRootLayout = easyWindow.getRootLayout();
if (mRootLayout == null) {
return;
}
mRootLayout.setOnTouchListener(this);
mRootLayout.post(() -> {
refreshWindowInfo();
refreshPhysicalScreenSize();
refreshLocationCoordinate();
});
}
/**
* 窗口回收后回调这个方法
*/
public void recycle() {
mEasyWindow = null;
if (mRootLayout != null) {
mRootLayout.setOnTouchListener(null);
mRootLayout = null;
}
}
@Override
public final boolean onTouch(View view, MotionEvent event) {
if (mEasyWindow == null || mRootLayout == null) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 在按下的时候先更新一下窗口信息和坐标信息,否则点击可能会出现坐标偏移的问题
refreshWindowInfo();
refreshPhysicalScreenSize();
refreshLocationCoordinate();
mConsumeTouchView = null;
View consumeTouchEventView = findNeedConsumeTouchView(mRootLayout, event);
if (consumeTouchEventView != null && dispatchTouchEventToChildView(mRootLayout, consumeTouchEventView, event)) {
mConsumeTouchView = consumeTouchEventView;
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mConsumeTouchView != null) {
try {
return dispatchTouchEventToChildView(mRootLayout, mConsumeTouchView, event);
} finally {
// 释放/置空对象
mConsumeTouchView = null;
}
}
default:
if (mConsumeTouchView != null) {
return dispatchTouchEventToChildView(mRootLayout, mConsumeTouchView, event);
}
break;
}
return onDragWindow(mEasyWindow, mRootLayout, event);
}
/**
* 派发触摸事件给子 View
* * @param event 触摸事件
* @param parentView 父 View
* @param childView 子 View(同时也是被触摸的 View)
*/
public boolean dispatchTouchEventToChildView(@NonNull View parentView, @NonNull View childView, @NonNull MotionEvent event) {
// 派发触摸事件之前,先将 MotionEvent 对象中的位置进行纠偏,否则会导致点击坐标对不上的情况
offsetMotionEventLocation(parentView, childView, event);
return childView.dispatchTouchEvent(event);
}
/**
* 偏移触摸事件坐标,这样子 View 能接受到正确的坐标
*
* @param event 触摸事件
* @param parentView 父 View
* @param childView 子 View(同时也是被触摸的 View)
*/
public void offsetMotionEventLocation(@NonNull View parentView, @NonNull View childView, @NonNull MotionEvent event) {
// 这部分代码参考自 ViewGroup.dispatchTransformedTouchEvent 方法实现
final int offsetX = parentView.getScrollX() - childView.getLeft();
final int offsetY = parentView.getScrollY() - childView.getTop();
event.offsetLocation(offsetX, offsetY);
}
/**
* 窗口拖拽回调方法
*
* @param easyWindow 当前窗口对象
* @param windowRootLayout 当前窗口视图
* @param event 当前触摸事件
* @return 根据返回值决定是否拦截该事件
*/
public abstract boolean onDragWindow(@NonNull EasyWindow<?> easyWindow, @NonNull ViewGroup windowRootLayout, @NonNull MotionEvent event);
@Nullable
public EasyWindow<?> getEasyWindow() {
return mEasyWindow;
}
@Nullable
public ViewGroup getRootLayout() {
return mRootLayout;
}
public void setAllowMoveToScreenNotch(boolean allowMoveToScreenNotch) {
mAllowMoveToScreenNotch = allowMoveToScreenNotch;
}
public boolean isAllowMoveToScreenNotch() {
return mAllowMoveToScreenNotch;
}
/**
* 获取当前 Window 的宽度(已使用真实屏幕宽度)
*/
public int getWindowWidth() {
return mCurrentWindowWidth;
}
/**
* 获取当前 Window 的高度(已使用真实屏幕高度)
*/
public int getWindowHeight() {
return mCurrentWindowHeight;
}
/**
* 获取当前窗口视图的宽度
*/
public int getWindowViewWidth() {
if (mEasyWindow == null) {
return 0;
}
return mEasyWindow.getWindowViewWidth();
}
/**
* 获取当前窗口视图的高度
*/
public int getWindowViewHeight() {
if (mEasyWindow == null) {
return 0;
}
return mEasyWindow.getWindowViewHeight();
}
/**
* 获取窗口不可见的宽度,一般情况下为横屏状态下刘海的高度
*/
public int getWindowInvisibleWidth() {
return mCurrentWindowInvisibleWidth;
}
/**
* 获取窗口不可见的高度,一般情况下为状态栏的高度
*/
public int getWindowInvisibleHeight() {
return mCurrentWindowInvisibleHeight;
}
/**
* 获取 View 在当前屏幕的 X 坐标
*/
public int getViewOnScreenX() {
return mCurrentViewOnScreenX;
}
/**
* 获取 View 在当前屏幕的 Y 坐标
*/
public int getViewOnScreenY() {
return mCurrentViewOnScreenY;
}
/**
* 刷新当前 Window 信息(改为使用 real metrics / real size)
*/
@SuppressWarnings("deprecation")
public void refreshWindowInfo() {
if (mEasyWindow == null) {
return;
}
Context context = mEasyWindow.getContext();
if (context == null) {
return;
}
View decorView = getRootLayout();
if (decorView == null && context instanceof Activity) {
decorView = ((Activity) context).getWindow().getDecorView();
}
if (decorView == null) {
return;
}
WindowManager windowManager = mEasyWindow.getWindowManager();
Display defaultDisplay = windowManager.getDefaultDisplay();
if (defaultDisplay == null) {
return;
}
// 获取真实屏幕尺寸(不受系统栏 / 导航栏裁剪影响)
DisplayMetrics realMetrics = new DisplayMetrics();
try {
defaultDisplay.getRealMetrics(realMetrics);
mCurrentWindowWidth = realMetrics.widthPixels;
mCurrentWindowHeight = realMetrics.heightPixels;
} catch (Exception ignored) {
// fallback to visible display frame if getRealMetrics not available
decorView.getWindowVisibleDisplayFrame(mTempRect);
mCurrentWindowWidth = mTempRect.right - mTempRect.left;
mCurrentWindowHeight = mTempRect.bottom - mTempRect.top;
}
// 同时获取可视区域,计算不可见的宽/高(用于在“不允许进入刘海/系统区”时做限制)
decorView.getWindowVisibleDisplayFrame(mTempRect);
mCurrentWindowInvisibleWidth = Math.max(mTempRect.left, 0);
mCurrentWindowInvisibleHeight = Math.max(mTempRect.top, 0);
}
/**
* 刷新当前设备的物理屏幕尺寸
*/
@SuppressWarnings("deprecation")
public void refreshPhysicalScreenSize() {
if (mEasyWindow == null) {
return;
}
WindowManager windowManager = mEasyWindow.getWindowManager();
Display defaultDisplay = windowManager.getDefaultDisplay();
if (defaultDisplay == null) {
return;
}
DisplayMetrics metrics = new DisplayMetrics();
defaultDisplay.getMetrics(metrics);
float screenWidthInInches;
float screenHeightInInches;
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
Point point = new Point();
defaultDisplay.getRealSize(point);
screenWidthInInches = point.x / metrics.xdpi;
screenHeightInInches = point.y / metrics.ydpi;
} else {
screenWidthInInches = metrics.widthPixels / metrics.xdpi;
screenHeightInInches = metrics.heightPixels / metrics.ydpi;
}
// 勾股定理:直角三角形的两条直角边的平方和等于斜边的平方
mPhysicalScreenSize = Math.sqrt(Math.pow(screenWidthInInches, 2) + Math.pow(screenHeightInInches, 2));
}
/**
* 刷新当前 View 在屏幕的坐标信息
*/
public void refreshLocationCoordinate() {
ViewGroup windowRootLayout = getRootLayout();
if (windowRootLayout == null) {
return;
}
int[] location = new int[2];
windowRootLayout.getLocationOnScreen(location);
mCurrentViewOnScreenX = location[0];
mCurrentViewOnScreenY = location[1];
}
/**
* 屏幕方向发生了改变
*/
public void onScreenOrientationChange() {
ViewGroup windowRootLayout = getRootLayout();
if (windowRootLayout == null) {
return;
}
long refreshDelayMillis = 100;
if (!isFollowScreenRotationChanges()) {
EasyWindow<?> easyWindow = getEasyWindow();
if (easyWindow != null) {
easyWindow.sendTask(() -> {
refreshWindowInfo();
refreshPhysicalScreenSize();
refreshLocationCoordinate();
}, refreshDelayMillis);
}
return;
}
int viewWidth = windowRootLayout.getWidth();
int viewHeight = windowRootLayout.getHeight();
int startX = mCurrentViewOnScreenX - mCurrentWindowInvisibleWidth;
int startY = mCurrentViewOnScreenY - mCurrentWindowInvisibleHeight;
float percentX;
float minTouchDistance = getMinTouchDistance();
if (startX <= minTouchDistance) {
percentX = 0;
} else if (Math.abs(mCurrentWindowWidth - (startX + viewWidth)) < minTouchDistance) {
percentX = 1;
} else {
float centerX = startX + viewWidth / 2f;
percentX = centerX / mCurrentWindowWidth;
}
float percentY;
if (startY <= minTouchDistance) {
percentY = 0;
} else if (Math.abs(mCurrentWindowHeight - (startY + viewHeight)) < minTouchDistance) {
percentY = 1;
} else {
float centerY = startY + viewHeight / 2f;
percentY = centerY / mCurrentWindowHeight;
}
windowRootLayout.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View view, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
view.removeOnLayoutChangeListener(this);
view.postDelayed(() -> {
// 先刷新当前窗口信息
refreshWindowInfo();
// 刷新屏幕物理尺寸
refreshPhysicalScreenSize();
int x = Math.max((int) (mCurrentWindowWidth * percentX - viewWidth / 2f), 0);
int y = Math.max((int) (mCurrentWindowHeight * percentY - viewHeight / 2f), 0);
updateLocation(x, y);
// 需要注意,这里需要延迟执行,否则会有问题
view.post(() -> onScreenRotateInfluenceCoordinateChangeFinish());
}, refreshDelayMillis);
}
});
}
/**
* 屏幕旋转导致悬浮窗坐标发生变化完成方法
*/
protected void onScreenRotateInfluenceCoordinateChangeFinish() {
refreshWindowInfo();
refreshPhysicalScreenSize();
refreshLocationCoordinate();
}
/**
* 悬浮窗是否跟随屏幕方向变化而发生变化
*/
public boolean isFollowScreenRotationChanges() {
return true;
}
public void updateLocation(float x, float y) {
updateLocation(x, y, isAllowMoveToScreenNotch());
}
public void updateLocation(float x, float y, boolean allowMoveToScreenNotch) {
updateLocation((int) x, (int) y, allowMoveToScreenNotch);
}
/**
* 更新悬浮窗的位置
*
* @param x x 坐标(相对与屏幕左上位置)
* @param y y 坐标(相对与屏幕左上位置)
* @param allowMoveToScreenNotch 是否允许移动到挖孔屏的区域
*/
public void updateLocation(int x, int y, boolean allowMoveToScreenNotch) {
// 如果允许进入刘海区域,则直接使用真实屏幕边界约束(允许移动到屏幕任意位置)
if (allowMoveToScreenNotch) {
// 保证 x,y 在真实屏幕范围内
int realW = getRealScreenWidth();
int realH = getRealScreenHeight();
int vw = getWindowViewWidth();
int vh = getWindowViewHeight();
int nx = Math.max(0, Math.min(x, Math.max(0, realW - vw)));
int ny = Math.max(0, Math.min(y, Math.max(0, realH - vh)));
updateWindowCoordinate(nx, ny);
return;
}
// 不允许进入刘海区域的严格限制(使用系统安全区域)
Rect safeInsetRect = getSafeInsetRect();
if (safeInsetRect == null) {
// 没有安全区域信息则退回到可视区域约束
// 使用已刷新到的 mCurrentWindowWidth / mCurrentWindowHeight(real metrics)
int nx = Math.max(0, Math.min(x, Math.max(0, mCurrentWindowWidth - getWindowViewWidth())));
int ny = Math.max(0, Math.min(y, Math.max(0, mCurrentWindowHeight - getWindowViewHeight())));
updateWindowCoordinate(nx, ny);
return;
}
// 如果 safeInsetRect 的四边都有值(说明系统给出完整 inset),则直接使用 real 屏幕宽高与 inset 做限制
if (safeInsetRect.left > 0 && safeInsetRect.right > 0 &&
safeInsetRect.top > 0 && safeInsetRect.bottom > 0) {
int vw = getWindowViewWidth();
int vh = getWindowViewHeight();
int realW = getRealScreenWidth();
int realH = getRealScreenHeight();
if (x < safeInsetRect.left - getWindowInvisibleWidth()) {
x = safeInsetRect.left - getWindowInvisibleWidth();
} else if (x > realW - safeInsetRect.right - vw) {
x = realW - safeInsetRect.right - vw;
}
if (y < safeInsetRect.top - getWindowInvisibleHeight()) {
y = safeInsetRect.top - getWindowInvisibleHeight();
} else if (y > realH - safeInsetRect.bottom - vh) {
y = realH - safeInsetRect.bottom - vh;
}
updateWindowCoordinate(x, y);
return;
}
// 默认回退逻辑(基于 mCurrentWindowWidth/mCurrentWindowHeight)
int viewWidth = getWindowViewWidth();
int viewHeight = getWindowViewHeight();
int windowWidth = getWindowWidth();
int windowHeight = getWindowHeight();
if (x < safeInsetRect.left - getWindowInvisibleWidth()) {
x = safeInsetRect.left - getWindowInvisibleWidth();
} else if (x > windowWidth - safeInsetRect.right - viewWidth) {
x = windowWidth - safeInsetRect.right - viewWidth;
}
if (y < safeInsetRect.top - getWindowInvisibleHeight()) {
y = safeInsetRect.top - getWindowInvisibleHeight();
} else if (y > windowHeight - safeInsetRect.bottom - viewHeight) {
y = windowHeight - safeInsetRect.bottom - viewHeight;
}
updateWindowCoordinate(x, y);
}
public void updateWindowCoordinate(int x, int y) {
if (mEasyWindow == null) {
return;
}
WindowManager.LayoutParams params = mEasyWindow.getWindowParams();
// 屏幕默认的重心(一定要先设置重心位置为左上角)
int screenGravity = Gravity.LEFT | Gravity.TOP;
// 判断本次移动的位置是否跟当前的窗口位置是否一致
if (params.gravity == screenGravity && params.x == x && params.y == y) {
return;
}
params.x = x;
params.y = y;
params.gravity = screenGravity;
// 如果允许移动到刘海区域,尝试打开 layoutInDisplayCutoutMode(API 28+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
try {
if (isAllowMoveToScreenNotch()) {
params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
}
} catch (Exception ignored) {
// 某些 ROM 上可能不支持,忽略即可
}
}
mEasyWindow.update();
refreshLocationCoordinate();
}
/**
* 获取当前屏幕安全区域
*/
@Nullable
public Rect getSafeInsetRect() {
if (mEasyWindow == null) {
return null;
}
Context context = mEasyWindow.getContext();
Window window;
if (!(context instanceof Activity)) {
return null;
}
window = ((Activity) context).getWindow();
if (window == null) {
return null;
}
return getSafeInsetRect(window);
}
/**
* 根据 Window 对象获取屏幕安全区域位置(返回的对象可能为空)
*/
@Nullable
public static Rect getSafeInsetRect(Window window) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
return null;
}
View activityDecorView = null;
if (window != null) {
activityDecorView = window.getDecorView();
}
WindowInsets rootWindowInsets = null;
if (activityDecorView != null) {
rootWindowInsets = activityDecorView.getRootWindowInsets();
}
DisplayCutout displayCutout = null;
if (rootWindowInsets != null) {
displayCutout = rootWindowInsets.getDisplayCutout();
}
if (displayCutout != null) {
// 安全区域距离屏幕左边的距离
int safeInsetLeft = displayCutout.getSafeInsetLeft();
// 安全区域距离屏幕顶部的距离
int safeInsetTop = displayCutout.getSafeInsetTop();
// 安全区域距离屏幕右部的距离
int safeInsetRight = displayCutout.getSafeInsetRight();
// 安全区域距离屏幕底部的距离
int safeInsetBottom = displayCutout.getSafeInsetBottom();
return new Rect(safeInsetLeft, safeInsetTop, safeInsetRight, safeInsetBottom);
}
return null;
}
/**
* 判断用户手指是否移动了,判断标准以下:
* 根据手指按下和抬起时的坐标进行判断,不能根据有没有 move 事件来判断
* 因为在有些机型上面,就算用户没有手指移动,只是简单点击也会产生 move 事件
*
* @param downX 手指按下时的 x 坐标
* @param upX 手指抬起时的 x 坐标
* @param downY 手指按下时的 y 坐标
* @param upY 手指抬起时的 y 坐标
*/
protected boolean isFingerMove(float downX, float upX, float downY, float upY) {
float minTouchSlop = getMinTouchDistance();
return Math.abs(downX - upX) >= minTouchSlop || Math.abs(downY - upY) >= minTouchSlop;
}
/**
* 获取最小触摸距离
*/
protected float getMinTouchDistance() {
double physicalScreenSize = getPhysicalScreenSize();
int dpValue;
if (physicalScreenSize > 0) {
// 市面上的平板最大尺寸不超过 15 英寸
dpValue = (int) Math.ceil(physicalScreenSize / 15);
} else {
dpValue = 1;
}
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue,
Resources.getSystem().getDisplayMetrics());
}
/**
* 寻找需要消费触摸事件的 View(可能为空)
*/
@Nullable
protected View findNeedConsumeTouchView(ViewGroup viewGroup, MotionEvent event) {
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = viewGroup.getChildAt(i);
int[] location = new int[2];
childView.getLocationOnScreen(location);
int left = location[0];
int top = location[1];
int right = left + childView.getWidth();
int bottom = top + childView.getHeight();
float x = event.getRawX();
float y = event.getRawY();
// 判断触摸位置是否在这个 View 内
if (x >= left && x <= right && y >= top && y <= bottom) {
if (isViewNeedConsumeTouchEvent(childView)) {
return childView;
} else if (childView instanceof ViewGroup) {
return findNeedConsumeTouchView((ViewGroup) childView, event);
}
}
}
return null;
}
/**
* 判断 View 是否需要消费当前触摸事件
*/
protected boolean isViewNeedConsumeTouchEvent(@NonNull View view) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && view instanceof ViewGroup && view.isScrollContainer()) {
return canTouchByView(view);
}
if (view instanceof WebView || view instanceof ScrollView || view instanceof ListView || view instanceof SeekBar) {
return canTouchByView(view);
}
// NestedScrollingChild 的子类有:RecyclerView、NestedScrollView、SwipeRefreshLayout 等等
if (view instanceof NestedScrollingChild || view instanceof NestedScrollingParent || view instanceof ViewPager) {
return canTouchByView(view);
}
Class<? extends View> viewClass = view.getClass();
try {
if (viewClass.isAssignableFrom(Class.forName("androidx.viewpager2.widget.ViewPager2"))) {
return canTouchByView(view);
}
} catch (ClassNotFoundException ignored) {
// default implementation ignored
}
return false;
}
/**
* 判断 View 是否能被触摸
*/
protected boolean canTouchByView(@NonNull View view) {
if (view instanceof RecyclerView && !canScrollByRecyclerView(((RecyclerView) view))) {
// 如果这个 RecyclerView 禁止了触摸事件,就不要启动触摸事件
return false;
}
// 这个 View 必须是启用状态,才认为有可能传递触摸事件
return view.isEnabled();
}
/**
* 判断 RecyclerView 是否能被触摸
*/
protected boolean canScrollByRecyclerView(@NonNull RecyclerView recyclerView) {
RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
if (layoutManager == null) {
// 如果没有设置 LayoutManager,则默认不需要触摸事件
return false;
}
// 当前这个 LayoutManager 必须开启垂直滚动或者水平滚动
return layoutManager.canScrollVertically() || layoutManager.canScrollHorizontally();
}
/**
* 获取物理的屏幕尺寸
*/
protected double getPhysicalScreenSize() {
return mPhysicalScreenSize;
}
/**
* 判断当前悬浮窗是否可以移动到屏幕之外的地方
*/
protected boolean isSupportMoveOffScreen() {
if (mEasyWindow == null) {
return false;
}
return mEasyWindow.hasWindowFlags(LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
/**
* 设置拖拽回调
*/
public void setWindowDraggingListener(OnWindowDraggingListener callback) {
mWindowDraggingListener = callback;
}
/**
* 派发开始拖拽事件
*/
protected void dispatchStartDraggingCallback() {
if (mEasyWindow == null) {
return;
}
if (mWindowDraggingListener == null) {
return;
}
mWindowDraggingListener.onWindowDraggingStart(mEasyWindow);
}
/**
* 派发拖拽中事件
*/
protected void dispatchRunningDraggingCallback() {
if (mEasyWindow == null) {
return;
}
if (mWindowDraggingListener == null) {
return;
}
mWindowDraggingListener.onWindowDraggingRunning(mEasyWindow);
}
/**
* 派发停止拖拽事件
*/
protected void dispatchStopDraggingCallback() {
if (mEasyWindow == null) {
return;
}
if (mWindowDraggingListener == null) {
return;
}
mWindowDraggingListener.onWindowDraggingStop(mEasyWindow);
}
public interface OnWindowDraggingListener {
/**
* 开始拖拽
*/
default void onWindowDraggingStart(@NonNull EasyWindow<?> easyWindow) {
// default implementation ignored
}
/**
* 拖拽中
*/
default void onWindowDraggingRunning(@NonNull EasyWindow<?> easyWindow) {
// default implementation ignored
}
/**
* 停止拖拽
*/
default void onWindowDraggingStop(@NonNull EasyWindow<?> easyWindow) {
// default implementation ignored
}
}
/**
* 获取真实屏幕宽度(px)
*/
protected int getRealScreenWidth() {
if (mEasyWindow == null) {
return mCurrentWindowWidth;
}
WindowManager wm = mEasyWindow.getWindowManager();
Display d = wm.getDefaultDisplay();
Point p = new Point();
try {
d.getRealSize(p);
return p.x;
} catch (Exception ignored) {
return mCurrentWindowWidth;
}
}
/**
* 获取真实屏幕高度(px)
*/
protected int getRealScreenHeight() {
if (mEasyWindow == null) {
return mCurrentWindowHeight;
}
WindowManager wm = mEasyWindow.getWindowManager();
Display d = wm.getDefaultDisplay();
Point p = new Point();
try {
d.getRealSize(p);
return p.y;
} catch (Exception ignored) {
return mCurrentWindowHeight;
}
}
}