一辱士、原理
1、添加獨立的View
我們在APP中想不依賴Activity中的布局添加View時残炮,可以通過WindowManager.addView()的方式蜈缤,創(chuàng)建一個window,并顯示添加的View呐萨。
2杀饵、Window可分為三類
應(yīng)用window:一般位于最底層,對應(yīng)一個Activity谬擦;
子window:不能單獨存在切距,需要附屬在父window上赞季,如Dialog忘伞;
系統(tǒng)window:一般位于最頂層,不會被其他window遮住疲酌,如Toast北秽。
二葡幸、懸浮窗
1、檢查權(quán)限
// 檢查懸浮窗
Settings.canDrawOverlays(mContext)
// 跳轉(zhuǎn)到懸浮窗設(shè)置頁面
activity.startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + activity.getPackageName())), requestCode);
2贺氓、添加懸浮窗
此處需要注意的是flag對于懸浮窗的影響:
FLAG_NOT_TOUCH_MODAL: 懸浮窗外部可響應(yīng)事件
FLAG_NOT_FOCUSABLE : 不處理系統(tǒng)按鈕事件
FLAG_LAYOUT_NO_LIMITS: 懸浮窗可以延伸到屏幕外
if (Settings.canDrawOverlays(mContext)) {
// 獲取WindowManager服務(wù)
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mLayoutParams = new WindowManager.LayoutParams();
// 設(shè)置LayoutParam
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
// 懸浮窗外部可響應(yīng)事件
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// 不處理系統(tǒng)按鈕事件
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蔚叨、響應(yīng)點擊事件
// 可以給view直接設(shè)置
mRootView.setOnClickListener(onClickListener);
4、拖動
首先辙培, 在ACTION_DOWN中記錄當(dāng)前的x蔑水,y位置;
然后扬蕊, 在ACTION_MOVE中判斷滑動距離搀别,如果觸發(fā)了滑動,算出滑動后的x尾抑,y位置歇父,通過updateViewLayout進行更新;
最后蛮穿, 在ACTION_UP中處理貼邊庶骄,點擊等邏輯。
// 拖動可以給view設(shè)置時間監(jiān)聽
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;
// 如果沒有觸發(fā)滑動践磅,則進行滑動判斷
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);
// 貼邊終點只有左、右兩種狀態(tài)
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);
}
// 處理移動后還會響應(yīng)點擊事件的情況
if (isMoved) {
isMoved = false;
return true;
}
break;
default:
break;
}
return false;
}
}
5灸异、橫豎屏切換適配
監(jiān)聽到屏幕變化后府适,由于屏幕的長寬發(fā)生了變化羔飞,重新計算當(dāng)前的x,y檐春,并進行更新逻淌。
// 添加屏幕變化監(jiān)聽
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) {
// 監(jiān)聽到屏幕發(fā)生變化
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還沒有觸發(fā)測量卡儒,主動測量一次
getRootView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
}
int viewWidth = getRootView().getMeasuredWidth();
int screenWidth = ScreenUtils.getScreenWidth(mContext);
int screenHeight = ScreenUtils.getScreenHeight(mContext);
// 部分手機,如:vivo R9s俐巴,橫豎屏切換骨望,獲取的值不會發(fā)生變化,此處做一個適配
screenWidth = isHorizontalScreen(mContext) ? Math.max(screenWidth, screenHeight) : Math.min(screenWidth, screenHeight);
// 貼邊初始只有左和右欣舵,沒有中間態(tài)
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);
}
// 當(dāng)前位置已經(jīng)超過允許的右邊距,返回最右邊距
return screenWidth - viewWidth - mXEdgeSize;
} else if (xPosition < mXEdgeSize) {
if (mOnPositionListener != null) {
mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_LEFT);
}
// 當(dāng)前位置小于允許的左邊距糟把,返回最左邊距
return mXEdgeSize;
} else {
if (mOnPositionListener != null) {
mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_MIDDLE);
}
// 由于不吸邊绢涡,所以展示真實位置
return xPosition;
}
}
}
private int getRealY(int yPosition, boolean isInit) {
// 此時view還沒有觸發(fā)測量,主動測量一次
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垂寥,橫豎屏切換,獲取的值不會發(fā)生變化另锋,此處做一個適配
screenHeight = isHorizontalScreen(mContext) ? Math.min(screenWidth, screenHeight) : Math.max(screenWidth, screenHeight);
if (yPosition > (screenHeight - viewHeight - mYBottomEdgeSize)) {
// 當(dāng)前位置已經(jīng)超過允許的下邊距滞项,返回最下邊距
return screenHeight - viewHeight - mYBottomEdgeSize;
} else if (yPosition < mYTopEdgeSize) {
// 當(dāng)前位置小于允許的上邊距,返回最上邊距
return mYTopEdgeSize;
} else {
// 展示真實位置
return yPosition;
}
}
private boolean isHorizontalScreen(Context context) {
int angle = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
//屏幕旋轉(zhuǎn)90°或270°夭坪,表示橫屏
return angle == Surface.ROTATION_90 || angle == Surface.ROTATION_270;
}
三文判、注意
部分場景需要點擊回到當(dāng)前的Activity,可以參考如下代碼:
// 方法一:跳轉(zhuǎn)到app展示的上一個頁面
public void onClick(View v) {
Intent intent = BaseApplication.getGlobalContext().getPackageManager().getLaunchIntentForPackage(BaseApplication.getGlobalContext().getPackageName());
BaseApplication.getGlobalContext().startActivity(intent);
}
// 方法二:本地緩存頁面室梅,判斷登錄狀態(tài)跳轉(zhuǎn)不同的頁面
public void onClick(View v) {
// 注意戏仓,這里的跳轉(zhuǎn)沒有參數(shù)傳遞,在一些activity的onNewIntent中會對intent進行再次解析
// 因此亡鼠,需要判斷是否是從懸浮窗跳過去的赏殃,進行攔截,防止出現(xiàn)異常
// 也可以將正確的參數(shù)傳遞過去间涵,成本較高仁热,用作備選項
// FLAG_ACTIVITY_SINGLE_TOP,如果當(dāng)前activity已經(jīng)在棧頂勾哩,不再創(chuàng)建抗蠢,直接跳轉(zhuǎn)
// FLAG_ACTIVITY_REORDER_TO_FRONT举哟,如果activity已經(jīng)創(chuàng)建了,如:ABCD,跳轉(zhuǎn)到B,不會重新創(chuàng)建,且變?yōu)锳CDB
if (ACCOUNT_CONTROLLER.hasLogin()) {
if (mCurrentActivity != null) {
// 正常情況都不是null迅矛,除非長時間被系統(tǒng)回收了
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 {
// 跳轉(zhuǎn)首頁妨猩,然后通過恢復(fù)接口進行再次跳轉(zhuǎn)
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;
// 添加屏幕變化監(jiān)聽
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) {
// 監(jiān)聽到屏幕發(fā)生變化
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服務(wù)
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
if (canDrag) {
mRootView.setOnTouchListener(new EFWindowOnTouchListener());
}
mRootView.setOnClickListener(onClickListener);
mLayoutParams = new WindowManager.LayoutParams();
// 設(shè)置LayoutParam
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
// 懸浮窗外部可響應(yīng)事件
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
// 不處理系統(tǒng)按鈕事件
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還沒有觸發(fā)測量壶硅,主動測量一次
getRootView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
}
int viewWidth = getRootView().getMeasuredWidth();
int screenWidth = ScreenUtils.getScreenWidth(mContext);
int screenHeight = ScreenUtils.getScreenHeight(mContext);
// 部分手機,如:vivo R9s销斟,橫豎屏切換庐椒,獲取的值不會發(fā)生變化,此處做一個適配
screenWidth = isHorizontalScreen(mContext) ? Math.max(screenWidth, screenHeight) : Math.min(screenWidth, screenHeight);
// 貼邊初始只有左和右票堵,沒有中間態(tài)
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);
}
// 當(dāng)前位置已經(jīng)超過允許的右邊距,返回最右邊距
return screenWidth - viewWidth - mXEdgeSize;
} else if (xPosition < mXEdgeSize) {
if (mOnPositionListener != null) {
mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_LEFT);
}
// 當(dāng)前位置小于允許的左邊距特纤,返回最左邊距
return mXEdgeSize;
} else {
if (mOnPositionListener != null) {
mOnPositionListener.onPositionChanged(OnPositionListener.POSITION_MIDDLE);
}
// 由于不吸邊军俊,所以展示真實位置
return xPosition;
}
}
}
private int getRealY(int yPosition, boolean isInit) {
// 此時view還沒有觸發(fā)測量,主動測量一次
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粪躬,橫豎屏切換,獲取的值不會發(fā)生變化昔穴,此處做一個適配
screenHeight = isHorizontalScreen(mContext) ? Math.min(screenWidth, screenHeight) : Math.max(screenWidth, screenHeight);
if (yPosition > (screenHeight - viewHeight - mYBottomEdgeSize)) {
// 當(dāng)前位置已經(jīng)超過允許的下邊距镰官,返回最下邊距
return screenHeight - viewHeight - mYBottomEdgeSize;
} else if (yPosition < mYTopEdgeSize) {
// 當(dāng)前位置小于允許的上邊距,返回最上邊距
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;
// 如果沒有觸發(fā)滑動吗货,則進行滑動判斷
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);
// 貼邊終點只有左泳唠、右兩種狀態(tài)
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);
}
// 處理移動后還會響應(yīng)點擊事件的情況
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:請先設(shè)置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();
//屏幕旋轉(zhuǎn)90°或270°,表示橫屏
return angle == Surface.ROTATION_90 || angle == Surface.ROTATION_270;
}
}