自定义悬浮窗

一、原理

1、添加独立的View

我们在APP中想不依赖Activity中的布局添加View时,可以通过WindowManager.addView()的方式,创建一个window,并显示添加的View。

2、Window可分为三类

应用window: 一般位于最底层,对应一个Activity;
子window: 不能单独存在,需要附属在父window上,如Dialog;
系统window: 一般位于最顶层,不会被其他window遮住,如Toast。

二、悬浮窗

1、检查权限
// 检查悬浮窗
Settings.canDrawOverlays(mContext)

// 跳转到悬浮窗设置页面
activity.startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + activity.getPackageName())), requestCode);

2、添加悬浮窗

此处需要注意的是flag对于悬浮窗的影响:
FLAG_NOT_TOUCH_MODAL: 悬浮窗外部可响应事件
FLAG_NOT_FOCUSABLE : 不处理系统按钮事件
FLAG_LAYOUT_NO_LIMITS: 悬浮窗可以延伸到屏幕外

if (Settings.canDrawOverlays(mContext)) {
    // 获取WindowManager服务
    mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    mLayoutParams = new WindowManager.LayoutParams();
    // 设置LayoutParam
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
    } else {
        mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
    }
    // 悬浮窗外部可响应事件
    mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
            // 不处理系统按钮事件
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
            // 悬浮窗可以延伸到屏幕外
            WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
    mLayoutParams.format = PixelFormat.RGBA_8888;
    mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
    mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    mLayoutParams.gravity = Gravity.TOP | Gravity.START;
    mLayoutParams.x = 0;
    mLayoutParams.y = 300;
    // 将悬浮窗控件添加到WindowManager
    mWindowManager.addView(mRootView, mLayoutParams);
}


// 移除悬浮窗
try {
    mWindowManager.removeView(mRootView);
} catch (Exception e) {
    //nothing
}
3、响应点击事件
// 可以给view直接设置
mRootView.setOnClickListener(onClickListener);
4、拖动

首先, 在ACTION_DOWN中记录当前的x,y位置;
然后, 在ACTION_MOVE中判断滑动距离,如果触发了滑动,算出滑动后的x,y位置,通过updateViewLayout进行更新;
最后, 在ACTION_UP中处理贴边,点击等逻辑。

// 拖动可以给view设置时间监听
if (canDrag) {
    mRootView.setOnTouchListener(new EFWindowOnTouchListener());
}

private class EFWindowOnTouchListener implements View.OnTouchListener {
        private boolean isMoved;
        private int oX;
        private int oY;
        private int x;
        private int y;

        @Override
        public boolean onTouch(View view, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // 初始化滑动的标记
                    isMoved = false;
                    oX = (int) event.getRawX();
                    oY = (int) event.getRawY();
                    x = (int) event.getRawX();
                    y = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int nowX = (int) event.getRawX();
                    int nowY = (int) event.getRawY();
                    int movedX = nowX - x;
                    int movedY = nowY - y;
                    x = nowX;
                    y = nowY;

                    // 如果没有触发滑动,则进行滑动判断
                    if (!isMoved && (Math.abs(oX - x) > mMinScrollDistance || Math.abs(oY - y) > mMinScrollDistance)) {
                        isMoved = true;
                    }

                    // 拖动中不能用贴边计算
                    mLayoutParams.x = getRealX(mLayoutParams.x + movedX, false, false);
                    mLayoutParams.y = getRealY(mLayoutParams.y + movedY, false);
                    mWindowManager.updateViewLayout(view, mLayoutParams);
                    break;
                case MotionEvent.ACTION_UP:
                    if (mIsSticky) {
                        int viewWidth = view.getMeasuredWidth();
                        int screenWidth = ScreenUtils.getScreenWidth(mContext);
                        // 贴边终点只有左、右两种状态
                        if ((mLayoutParams.x + viewWidth / 2) > screenWidth / 2) {
                            if (mOnPositionListener != null) {
                                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_RIGHT);
                            }
                            mLayoutParams.x = screenWidth - viewWidth - mXEdgeSize;
                        } else {
                            if (mOnPositionListener != null) {
                                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_LEFT);
                            }
                            mLayoutParams.x = mXEdgeSize;
                        }
                        mWindowManager.updateViewLayout(view, mLayoutParams);
                    }

                    // 处理移动后还会响应点击事件的情况
                    if (isMoved) {
                        isMoved = false;
                        return true;
                    }
                    break;
                default:
                    break;
            }
            return false;
        }
    }
5、横竖屏切换适配

监听到屏幕变化后,由于屏幕的长宽发生了变化,重新计算当前的x,y,并进行更新。

// 添加屏幕变化监听
DisplayManager displayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
displayManager.registerDisplayListener(new DisplayManager.DisplayListener() {
    @Override
    public void onDisplayAdded(int displayId) {
    }
    @Override
    public void onDisplayRemoved(int displayId) {
    }
    @Override
    public void onDisplayChanged(int displayId) {
        // 监听到屏幕发生变化
        if (mLayoutParams != null && mWindowManager != null
                && mRootView != null && mRootView.isAttachedToWindow()) {
            mLayoutParams.x = getRealX(mLayoutParams.x, false, true);
            mLayoutParams.y = getRealY(mLayoutParams.y, false);
            mWindowManager.updateViewLayout(mRootView, mLayoutParams);
        }
    }
}, new Handler(Looper.getMainLooper()));

6、贴边,缩进等处理
private int getRealX(int xPosition, boolean isInit, boolean considerSticky) {
    if (isInit) {
        // 此时view还没有触发测量,主动测量一次
        getRootView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
    }
    int viewWidth = getRootView().getMeasuredWidth();
    int screenWidth = ScreenUtils.getScreenWidth(mContext);
    int screenHeight = ScreenUtils.getScreenHeight(mContext);
    // 部分手机,如:vivo R9s,横竖屏切换,获取的值不会发生变化,此处做一个适配
    screenWidth = isHorizontalScreen(mContext) ? Math.max(screenWidth, screenHeight) : Math.min(screenWidth, screenHeight);
    // 贴边初始只有左和右,没有中间态
    if (considerSticky && mIsSticky) {
        if ((xPosition + viewWidth / 2) > screenWidth / 2) {// 处理缩进
            if (mOnPositionListener != null) {
                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_RIGHT);
            }
            // 贴右边显示,真实坐标为(屏幕宽度 - view的宽度 - x边距)
            return screenWidth - viewWidth - mXEdgeSize;
        } else {
            if (mOnPositionListener != null) {
                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_LEFT);
            }
            // 贴左边显示,真实坐标为(左起点 + x边距),左边的起点为0。
            return mXEdgeSize;
        }
    } else {
        if (xPosition > (screenWidth - viewWidth - mXEdgeSize)) {
            if (mOnPositionListener != null) {
                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_RIGHT);
            }
            // 当前位置已经超过允许的右边距,返回最右边距
            return screenWidth - viewWidth - mXEdgeSize;
        } else if (xPosition < mXEdgeSize) {
            if (mOnPositionListener != null) {
                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_LEFT);
            }
            // 当前位置小于允许的左边距,返回最左边距
            return mXEdgeSize;
        } else {
            if (mOnPositionListener != null) {
                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_MIDDLE);
            }
            // 由于不吸边,所以展示真实位置
            return xPosition;
        }
    }
}


private int getRealY(int yPosition, boolean isInit) {
    // 此时view还没有触发测量,主动测量一次
    if (isInit) {
        getRootView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
    }
    int viewHeight = getRootView().getMeasuredHeight();
    int screenWidth = ScreenUtils.getScreenWidth(mContext);
    int screenHeight = ScreenUtils.getScreenHeight(mContext);
    // 部分手机,如:vivo R9s,横竖屏切换,获取的值不会发生变化,此处做一个适配
    screenHeight = isHorizontalScreen(mContext) ? Math.min(screenWidth, screenHeight) : Math.max(screenWidth, screenHeight);
    if (yPosition > (screenHeight - viewHeight - mYBottomEdgeSize)) {
        // 当前位置已经超过允许的下边距,返回最下边距
        return screenHeight - viewHeight - mYBottomEdgeSize;
    } else if (yPosition < mYTopEdgeSize) {
        // 当前位置小于允许的上边距,返回最上边距
        return mYTopEdgeSize;
    } else {
        // 展示真实位置
        return yPosition;
    }
}

private boolean isHorizontalScreen(Context context) {
    int angle = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
    //屏幕旋转90°或270°,表示横屏
    return angle == Surface.ROTATION_90 || angle == Surface.ROTATION_270;
}

三、注意

部分场景需要点击回到当前的Activity,可以参考如下代码:

// 方法一:跳转到app展示的上一个页面
public void onClick(View v) {
    Intent intent = BaseApplication.getGlobalContext().getPackageManager().getLaunchIntentForPackage(BaseApplication.getGlobalContext().getPackageName());
    BaseApplication.getGlobalContext().startActivity(intent);
}

// 方法二:本地缓存页面,判断登录状态跳转不同的页面
public void onClick(View v) {
    // 注意,这里的跳转没有参数传递,在一些activity的onNewIntent中会对intent进行再次解析
    // 因此,需要判断是否是从悬浮窗跳过去的,进行拦截,防止出现异常
    // 也可以将正确的参数传递过去,成本较高,用作备选项
    // FLAG_ACTIVITY_SINGLE_TOP,如果当前activity已经在栈顶,不再创建,直接跳转
    // FLAG_ACTIVITY_REORDER_TO_FRONT,如果activity已经创建了,如:ABCD,跳转到B,不会重新创建,且变为ACDB
    if (ACCOUNT_CONTROLLER.hasLogin()) {
        if (mCurrentActivity != null) {
            // 正常情况都不是null,除非长时间被系统回收了
            Intent targetIntent = new Intent(BaseApplication.getGlobalContext(), mCurrentActivity.getClass());
            targetIntent.putExtra(SpName.INTENT_FROM, FROM_EFWINDOW);
            targetIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            mCurrentActivity.startActivity(targetIntent);
        } else {
            // 跳转首页,然后通过恢复接口进行再次跳转
            Intent targetIntent = new Intent(BaseApplication.getGlobalContext(), HomeActivity.class);
            targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            targetIntent.putExtra(SpName.INTENT_FROM, FROM_EFWINDOW);
            BaseApplication.getGlobalContext().startActivity(targetIntent);
        }
    } else {
        // 登录信息失效了
        Intent targetIntent = new Intent(BaseApplication.getGlobalContext(), LoginActivity.class);
        targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        targetIntent.putExtra(SpName.INTENT_FROM, FROM_EFWINDOW);
        BaseApplication.getGlobalContext().startActivity(targetIntent);
        ToastUtil.showMessage("登录信息失效,请重新登录");
    }
}

四、悬浮窗代码

//try {
//    if (mEfWindow == null) {
//        View view = LayoutInflater.from(BaseApplication.getGlobalContext()).inflate(R.layout.layout_edj_floating_window, null);
//        mEfWindow = new EFWindow.Builder(BaseApplication.getGlobalContext())
//                .view(view)
//                .yPosition(ScreenUtils.getScreenHeight(BaseApplication.getGlobalContext()) / 3)
//                .isSticky(true)
//                .canDrag(true)
//                .xEdgeSize(-UIUtils.dp2px(BaseApplication.getGlobalContext(), 20))
//                .onClickListener(this)
//                .build();
//    }
//    mEfWindow.show();
//} catch (Exception e) {
//    e.printStackTrace();
//}
public class EFWindow {
    private final int mMinScrollDistance;
    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mLayoutParams;
    private final View mRootView;
    private boolean mIsShow;
    private boolean mIsSticky;
    private int mXEdgeSize;
    private int mYTopEdgeSize;
    private int mYBottomEdgeSize;
    private OnPositionListener mOnPositionListener;
    private final Context mContext;

    @RequiresApi(api = Build.VERSION_CODES.M)
    private EFWindow(Builder builder) {
        // 赋值
        mContext = builder.mContext;
        mMinScrollDistance = ViewConfiguration.get(mContext).getScaledTouchSlop();
        mIsSticky = builder.mIsSticky;
        mXEdgeSize = builder.mXEdgeSize;
        mYTopEdgeSize = builder.mYTopEdgeSize;
        mYBottomEdgeSize = builder.mYBottomEdgeSize;
        mOnPositionListener = builder.mOnPositionListener;
        mRootView = builder.mRootView;
        boolean canDrag = builder.mCanDrag;
        int xPosition = builder.mXPosition;
        int yPosition = builder.mYPosition;
        View.OnClickListener onClickListener = builder.mOnClickListener;

        // 添加屏幕变化监听
        DisplayManager displayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
        displayManager.registerDisplayListener(new DisplayManager.DisplayListener() {
            @Override
            public void onDisplayAdded(int displayId) {

            }

            @Override
            public void onDisplayRemoved(int displayId) {

            }

            @Override
            public void onDisplayChanged(int displayId) {
                // 监听到屏幕发生变化
                if (mLayoutParams != null && mWindowManager != null
                        && mRootView != null && mRootView.isAttachedToWindow()) {
                    mLayoutParams.x = getRealX(mLayoutParams.x, false, true);
                    mLayoutParams.y = getRealY(mLayoutParams.y, false);
                    mWindowManager.updateViewLayout(mRootView, mLayoutParams);
                }
            }
        }, new Handler(Looper.getMainLooper()));

        if (Settings.canDrawOverlays(mContext)) {
            // 获取WindowManager服务
            mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
            if (canDrag) {
                mRootView.setOnTouchListener(new EFWindowOnTouchListener());
            }
            mRootView.setOnClickListener(onClickListener);

            mLayoutParams = new WindowManager.LayoutParams();
            // 设置LayoutParam
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
            } else {
                mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
            }
            // 悬浮窗外部可响应事件
            mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                    // 不处理系统按钮事件
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                    // 悬浮窗可以延伸到屏幕外
                    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
            mLayoutParams.format = PixelFormat.RGBA_8888;
            mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
            mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
            mLayoutParams.gravity = Gravity.TOP | Gravity.START;
            mLayoutParams.x = getRealX(xPosition, true, true);
            mLayoutParams.y = getRealY(yPosition, true);
        }
    }

    private int getRealX(int xPosition, boolean isInit, boolean considerSticky) {
        if (isInit) {
            // 此时view还没有触发测量,主动测量一次
            getRootView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        }
        int viewWidth = getRootView().getMeasuredWidth();
        int screenWidth = ScreenUtils.getScreenWidth(mContext);
        int screenHeight = ScreenUtils.getScreenHeight(mContext);

        // 部分手机,如:vivo R9s,横竖屏切换,获取的值不会发生变化,此处做一个适配
        screenWidth = isHorizontalScreen(mContext) ? Math.max(screenWidth, screenHeight) : Math.min(screenWidth, screenHeight);

        // 贴边初始只有左和右,没有中间态
        if (considerSticky && mIsSticky) {
            if ((xPosition + viewWidth / 2) > screenWidth / 2) {// 处理缩进
                if (mOnPositionListener != null) {
                    mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_RIGHT);
                }
                // 贴右边显示,真实坐标为(屏幕宽度 - view的宽度 - x边距)
                return screenWidth - viewWidth - mXEdgeSize;
            } else {
                if (mOnPositionListener != null) {
                    mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_LEFT);
                }
                // 贴左边显示,真实坐标为(左起点 + x边距),左边的起点为0。
                return mXEdgeSize;
            }
        } else {
            if (xPosition > (screenWidth - viewWidth - mXEdgeSize)) {
                if (mOnPositionListener != null) {
                    mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_RIGHT);
                }
                // 当前位置已经超过允许的右边距,返回最右边距
                return screenWidth - viewWidth - mXEdgeSize;
            } else if (xPosition < mXEdgeSize) {
                if (mOnPositionListener != null) {
                    mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_LEFT);
                }
                // 当前位置小于允许的左边距,返回最左边距
                return mXEdgeSize;
            } else {
                if (mOnPositionListener != null) {
                    mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_MIDDLE);
                }
                // 由于不吸边,所以展示真实位置
                return xPosition;
            }
        }
    }

    private int getRealY(int yPosition, boolean isInit) {
        // 此时view还没有触发测量,主动测量一次
        if (isInit) {
            getRootView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        }
        int viewHeight = getRootView().getMeasuredHeight();
        int screenWidth = ScreenUtils.getScreenWidth(mContext);
        int screenHeight = ScreenUtils.getScreenHeight(mContext);

        // 部分手机,如:vivo R9s,横竖屏切换,获取的值不会发生变化,此处做一个适配
        screenHeight = isHorizontalScreen(mContext) ? Math.min(screenWidth, screenHeight) : Math.max(screenWidth, screenHeight);

        if (yPosition > (screenHeight - viewHeight - mYBottomEdgeSize)) {
            // 当前位置已经超过允许的下边距,返回最下边距
            return screenHeight - viewHeight - mYBottomEdgeSize;
        } else if (yPosition < mYTopEdgeSize) {
            // 当前位置小于允许的上边距,返回最上边距
            return mYTopEdgeSize;
        } else {
            // 展示真实位置
            return yPosition;
        }
    }

    public View getRootView() {
        return mRootView;
    }

    public boolean isShow() {
        return mIsShow;
    }

    public void show() {
        mIsShow = true;

        // 先尝试移除
        try {
            mLayoutParams.x = getRealX(mLayoutParams.x, false, true);
            mLayoutParams.y = getRealY(mLayoutParams.y, false);
            mWindowManager.removeView(mRootView);
        } catch (Exception e) {
            //nothing
        }
        // 将悬浮窗控件添加到WindowManager
        mWindowManager.addView(mRootView, mLayoutParams);
    }

    public void dismiss() {
        // 尝试移除
        try {
            mWindowManager.removeView(mRootView);
        } catch (Exception e) {
            //nothing
        }
        mIsShow = false;
    }


    private class EFWindowOnTouchListener implements View.OnTouchListener {
        private boolean isMoved;
        private int oX;
        private int oY;
        private int x;
        private int y;

        @Override
        public boolean onTouch(View view, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    // 初始化滑动的标记
                    isMoved = false;
                    oX = (int) event.getRawX();
                    oY = (int) event.getRawY();
                    x = (int) event.getRawX();
                    y = (int) event.getRawY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    int nowX = (int) event.getRawX();
                    int nowY = (int) event.getRawY();
                    int movedX = nowX - x;
                    int movedY = nowY - y;
                    x = nowX;
                    y = nowY;

                    // 如果没有触发滑动,则进行滑动判断
                    if (!isMoved && (Math.abs(oX - x) > mMinScrollDistance || Math.abs(oY - y) > mMinScrollDistance)) {
                        isMoved = true;
                    }

                    // 拖动中不能用贴边计算
                    mLayoutParams.x = getRealX(mLayoutParams.x + movedX, false, false);
                    mLayoutParams.y = getRealY(mLayoutParams.y + movedY, false);
                    mWindowManager.updateViewLayout(view, mLayoutParams);
                    break;
                case MotionEvent.ACTION_UP:
                    if (mIsSticky) {
                        int viewWidth = view.getMeasuredWidth();
                        int screenWidth = ScreenUtils.getScreenWidth(mContext);
                        // 贴边终点只有左、右两种状态
                        if ((mLayoutParams.x + viewWidth / 2) > screenWidth / 2) {
                            if (mOnPositionListener != null) {
                                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_RIGHT);
                            }
                            mLayoutParams.x = screenWidth - viewWidth - mXEdgeSize;
                        } else {
                            if (mOnPositionListener != null) {
                                mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_LEFT);
                            }
                            mLayoutParams.x = mXEdgeSize;
                        }
                        mWindowManager.updateViewLayout(view, mLayoutParams);
                    }

                    // 处理移动后还会响应点击事件的情况
                    if (isMoved) {
                        isMoved = false;
                        return true;
                    }
                    break;
                default:
                    break;
            }
            return false;
        }
    }

    public static class Builder {
        private final Context mContext;
        private View mRootView;
        private int mXPosition;
        private int mYPosition;
        private boolean mCanDrag;
        private boolean mIsSticky;
        private int mXEdgeSize;
        private int mYTopEdgeSize;
        private int mYBottomEdgeSize;
        private View.OnClickListener mOnClickListener;
        private OnPositionListener mOnPositionListener;

        public Builder(Context context) {
            mContext = context;
        }

        public Builder onPositionListener(OnPositionListener onPositionListener) {
            mOnPositionListener = onPositionListener;
            return this;
        }

        public Builder onClickListener(View.OnClickListener onClickListener) {
            mOnClickListener = onClickListener;
            return this;
        }

        public Builder xPosition(int xPosition) {
            mXPosition = xPosition;
            return this;
        }

        public Builder yPosition(int yPosition) {
            mYPosition = yPosition;
            return this;
        }

        public Builder view(View view) {
            mRootView = view;
            return this;
        }

        public Builder canDrag(boolean canDrag) {
            mCanDrag = canDrag;
            return this;
        }

        public Builder isSticky(boolean isSticky) {
            mIsSticky = isSticky;
            return this;
        }

        public Builder xEdgeSize(int xEdgeSize) {
            mXEdgeSize = xEdgeSize;
            return this;
        }

        public Builder yTopEdgeSize(int yTopEdgeSize) {
            mYTopEdgeSize = yTopEdgeSize;
            return this;
        }

        public Builder yBottomEdgeSize(int yBottomEdgeSize) {
            mYBottomEdgeSize = yBottomEdgeSize;
            return this;
        }

        public Builder view(int viewRes) throws Exception {
            if (mContext == null) {
                throw new Exception("EFWindow:请传入正确的context.");
            }
            mRootView = LayoutInflater.from(mContext).inflate(viewRes, null);
            return this;
        }

        @RequiresApi(api = Build.VERSION_CODES.M)
        public EFWindow build() throws Exception {
            if (mContext == null) {
                throw new Exception("EFWindow:请传入正确的context.");
            }
            if (mRootView == null) {
                throw new Exception("EFWindow:请先设置Window布局.");
            }
            return new EFWindow(this);
        }

    }

    public interface OnPositionListener {
        int POSITION_LEFT = 0;
        int POSITION_MIDDLE = 1;
        int POSITION_RIGHT = 2;

        void onPositionChanged(int positionStatus);
    }

    private boolean isHorizontalScreen(Context context) {
        int angle = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
        //屏幕旋转90°或270°,表示横屏
        return angle == Surface.ROTATION_90 || angle == Surface.ROTATION_270;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值