封裝一個通用的PopupWindow
是關於建造者設計模式的,今天順便封裝一個通用的 PopupWindow 來實踐一下, 同時也方便以後使用 PopupWindow,本文將從下面幾個方面來介紹 PopupWindow 及其封裝,具體如下:
- 概述
- 常用方法
- 基本使用
- 封裝 PopupWindow
- 使用封裝後的PopupWindow
- 顯示效果
概述
PopupWindow 表示一個彈窗,類似於 AlertDialog,相較 AlertDialog 來說 PopupWindow 使用起來更靈活,可有任意指定要顯示的位置,當然能夠靈活的使用必然在某一層面有所犧牲,如 PopupWindow 相較 AlertDialog 沒有預設的佈局,每次都得專門建立彈窗的佈局,這一點來說 AlertDialog 就比較方便了,所以在開發中沒有最好的解決方案,要根據具體的需求選擇最合適的解決方案。
常用設定
PopupWindow 的建立,具體如下:
//構造方法
public PopupWindow (Context context)
public PopupWindow(View contentView)
public PopupWindow(View contentView, int width, int height)
public PopupWindow(View contentView, int width, int height, boolean focusable)
PopupWindow 的常用屬性設定,具體如下:
//設定View(必須)
window.setContentView(contentView);
//設定寬(必須)
window.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
//設定高(必須)
window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
//設定背景
window.setBackgroundDrawable(new ColorDrawable(Color.GRAY));
//設定PopupWindow之外的觸控事件
window.setOutsideTouchable(true);
//設定PopupWindow消失的監聽器
window.setOnDismissListener(this);
//設定PopupWindow上的觸控事件
window.setTouchable(true);
//設定PopupWindow彈出動畫
window.setAnimationStyle(R.style.PopupWindowTranslateTheme);
PopupWindow 的顯示有兩種設定方式,一種是基於座標,另一種是基於某個 View ,具體如下:
//基於座標,引數(當前視窗的某個 View,位置,起始座標x, 起始座標y)
void showAtLocation (View parent, int gravity, int x, int y)
//基於某個View,引數(附著的View,x 方向的偏移量,y 方向的偏移量)
void showAsDropDown (View anchor, int xoff, int yoff, int gravity)
void showAsDropDown (View anchor, int xoff, int yoff)
void showAsDropDown (View anchor)
基本使用
PopupWindow 的主要內容基本如上,下面使用原生的 PopupWindow 實現一個彈窗,下面是關鍵程式碼,具體如下:
//建立PopupWindow
PopupWindow window = new PopupWindow(this);
//設定顯示View
window.setContentView(contentView);
//設定寬高
window.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
//設定背景
window.setBackgroundDrawable(new ColorDrawable(Color.GRAY));
//設定PopupWindow之外的觸控事件
window.setOutsideTouchable(true);
//設定PopupWindow消失的監聽器
window.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
//監聽PopupWindow的消失
}
});
//設定PopupWindow上的觸控事件
window.setTouchable(true);
//設定PopupWindow彈出動畫
window.setAnimationStyle(R.style.PopupWindowTranslateTheme);
window.showAtLocation(btnTarget, Gravity.BOTTOM | Gravity.CENTER, 0, 0);
下面是顯示效果,具體如下:
封裝 PopupWindow
這裡對 PopupWindow 的封裝主要是對 PopupWindow 常用擺放位置做進一步封裝,使 PopupWindow 的呼叫更加靈活、簡潔。
在封裝過程中遇到的問題是不能正確獲取到 PopupWindow 的寬高,正確獲取寬高的方法是先對 PopupWindow 進行測量,然後再獲取其寬高,具體如下:
//獲取PopupWindow的寬高
mPopupWindow.getContentView().measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
int popupWidth = mPopupWindow.getContentView().getMeasuredWidth();
int popupHeight = mPopupWindow.getContentView().getMeasuredHeight();
對 PopupWindow 的封裝使用了建造者設計模式,下面看一下 PopupWindow 的預設配置,具體如下:
public Builder(Context context) {
this.context = context;
this.popupWindow = new PopupWindow(context);
//預設PopupWindow響應觸控事件
this.outsideTouchable = true;
//預設響應觸控事件
this.touchable = true;
//預設背景透明
this.backgroundDrawable = new ColorDrawable(Color.TRANSPARENT);
//預設寬高為WRAP_CONTENT
this.width = WindowManager.LayoutParams.WRAP_CONTENT;
this.height = WindowManager.LayoutParams.WRAP_CONTENT;
//預設Gravity為Gravity.CENTER
this.gravity = Gravity.CENTER;
this.layoutId = -1;
//預設偏移量為0
this.offsetX = 0;
this.offsetY = 0;
//...
}
由於寬高、背景、是否可點選等相關屬性已經設定了預設值,使用時根據自己的需求設定相關屬性,如 PopupWindow 的動畫等,所以這些設定肯定是非必須的,那麼那些事建立時必須的呢。
下面是對 PopupWindow 封裝類 MPopupWindow 的初始化,具體如下:
private void setPopupWindowConfig(MPopupWindow window) {
if (contentView != null && layoutId != -1){
throw new MException("setContentView and setLayoutId can't be used together.", "0");
}else if (contentView == null && layoutId == -1){
throw new MException("contentView or layoutId can't be null.", "1");
}
if (context == null) {
throw new MException("context can't be null.", "2");
} else {
window.mContext = this.context;
}
window.mWidth = this.width;
window.mHeight = this.height;
window.mView = this.contentView;
window.mLayoutId = layoutId;
window.mPopupWindow = this.popupWindow;
window.mOutsideTouchable = this.outsideTouchable;
window.mBackgroundDrawable = this.backgroundDrawable;
window.mOnDismissListener = this.onDismissListener;
window.mAnimationStyle = this.animationStyle;
window.mTouchable = this.touchable;
window.mOffsetX = this.offsetX;
window.mOffsetY = this.offsetY;
window.mGravity = this.gravity;
}
}
顯然,這裡可以看出 context 和 contentView 或 layoutId 是必須需要設定的,如果沒有設定相應的會有錯誤提示,當然在封裝中也對 contentView 和 layoutId 不能同時使用做了限制和如果使用了兩者的錯誤提示。
下面是對外提供的顯示 PopupWindow 的方法,根據不同的列舉型別將 PopupWindow 顯示在不同的位置,具體如下:
public void showPopupWindow(View v, LocationType type) {
if (mView!=null){
mPopupWindow.setContentView(mView);
}else if (mLayoutId != -1){
View contentView = LayoutInflater.from(mContext).inflate(mLayoutId, null);
mPopupWindow.setContentView(contentView);
}
mPopupWindow.setWidth(mWidth);
mPopupWindow.setHeight(mHeight);
mPopupWindow.setBackgroundDrawable(mBackgroundDrawable);
mPopupWindow.setOutsideTouchable(mOutsideTouchable);
mPopupWindow.setOnDismissListener(mOnDismissListener);
mPopupWindow.setAnimationStyle(mAnimationStyle);
mPopupWindow.setTouchable(mTouchable);
//獲取目標View的座標
int[] locations = new int[2];
v.getLocationOnScreen(locations);
int left = locations[0];
int top = locations[1];
//獲取PopupWindow的寬高
mPopupWindow.getContentView().measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
int popupWidth = mPopupWindow.getContentView().getMeasuredWidth();
int popupHeight = mPopupWindow.getContentView().getMeasuredHeight();
switch (type) {
case TOP_LEFT:
mPopupWindow.showAtLocation(v,Gravity.NO_GRAVITY,left - popupWidth + mOffsetX,top - popupHeight + mOffsetY);
break;
case TOP_CENTER:
int offsetX = (v.getWidth() - popupWidth) / 2;
mPopupWindow.showAtLocation(v,Gravity.NO_GRAVITY,left + offsetX + mOffsetX,top - popupHeight + mOffsetY);
break;
case TOP_RIGHT:
mPopupWindow.showAtLocation(v,Gravity.NO_GRAVITY,left + v.getWidth() + mOffsetX,top - popupHeight + mOffsetY);
break;
case BOTTOM_LEFT:
mPopupWindow.showAsDropDown(v, -popupWidth + mOffsetX,mOffsetY);
break;
case BOTTOM_CENTER:
int offsetX1 = (v.getWidth() - popupWidth) / 2;
mPopupWindow.showAsDropDown(v,offsetX1 + mOffsetX,mOffsetY);
break;
case BOTTOM_RIGHT:
mPopupWindow.showAsDropDown(v, v.getWidth() + mOffsetX,mOffsetY);
break;
case LEFT_TOP:
mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY, left - popupWidth + mOffsetX, top - popupHeight + mOffsetY);
break;
case LEFT_BOTTOM:
mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY, left - popupWidth + mOffsetX, top + v.getHeight() + mOffsetY);
break;
case LEFT_CENTER:
int offsetY = (v.getHeight() - popupHeight) / 2;
mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY,left - popupWidth + mOffsetX,top + offsetY + mOffsetY);
break;
case RIGHT_TOP:
mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY, left + v.getWidth() + mOffsetX,top - popupHeight + mOffsetY);
break;
case RIGHT_BOTTOM:
mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY, left + v.getWidth() + mOffsetX,top + v.getHeight() + mOffsetY);
break;
case RIGHT_CENTER:
int offsetY1 = (v.getHeight() - popupHeight) / 2;
mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY,left + v.getWidth() + mOffsetX,top + offsetY1 + mOffsetY);
break;
case FROM_BOTTOM:
mPopupWindow.showAtLocation(v,mGravity,mOffsetX,mOffsetY);
break;
}
}
使用封裝後的PopupWindow
下面是使用封裝後的 PopupWindow,只需四行程式碼就可以顯示一個預設的 PopupWindow 了,具體如下:
private void showPopupWindow(MPopupWindow.LocationType type) {
MPopupWindow popupWindow = new MPopupWindow
.Builder(this)
.setLayoutId(R.layout.popup_window_layout)
.build();
popupWindow.showPopupWindow(btnTarget, type);
}
由於預設 PopupWindow 背景是透明的,建議測試時設定背景。
顯示效果:
下面是 PopupWindow 在各個位置的顯示,具體如下:
可以選擇關注微信公眾號:jzman-blog 獲取最新更新,一起交流學習,公眾號回覆 MPopupWindow 獲取原始碼連結。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4606/viewspace-2825807/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 通用 PopupWindow,幾行程式碼搞定 PopupWindow 彈窗(續)行程
- React — 通用hooks封裝ReactHook封裝
- 【vue3 + ts + echarts】封裝一個通用echarts元件-2.0版VueEcharts封裝元件
- JSONP 通用函式封裝JSON函式封裝
- BaseViewController(一個通用的帶導航欄的基礎控制器+封裝庫/類)ViewController封裝
- ECharts圖表——封裝通用配置Echarts封裝
- 通用、封裝、簡化 webpack 配置封裝Web
- 封裝一個自己的js庫封裝JS
- 一個簡單的 Amqp 封裝MQ封裝
- 使用 Repository 設計模式封裝通用的 CURD設計模式封裝
- Vue Hook 封裝通用型表格VueHook封裝
- 使用ts封裝一個ajax封裝
- 用promise封裝一個ajaxPromise封裝
- 封裝一個簡單的日曆元件封裝元件
- 8.7 一個模組的封裝過程封裝
- 【JavaScript框架封裝】實現一個類似於JQuery的動畫框架的封裝JavaScript框架封裝jQuery動畫
- 封裝了一個? URL地址解析封裝
- 基於DotNetCoreNPOI封裝特性通用匯出excelNetCore封裝Excel
- 訊息中介軟體通用化封裝封裝
- 【JavaScript框架封裝】實現一個類似於JQuery的CSS樣式框架的封裝JavaScript框架封裝jQueryCSS
- 如何封裝一個flutter的多語言plugin封裝FlutterPlugin
- 封裝一個簡單的動畫函式封裝動畫函式
- 封裝一個優雅的element ui表格元件封裝UI元件
- 封裝一個簡易的上傳附件方法封裝
- 如何優雅的封裝一個DOM事件庫封裝事件
- Flutter 封裝一個 Banner 輪播圖Flutter封裝
- 如何基於 React 封裝一個元件React封裝元件
- 基於FutureBuilder通用網路請求介面封裝Rebuild封裝
- [記錄] 通用封裝函式——四則運算封裝函式
- 一、類的封裝性封裝
- WPF如何封裝一個可擴充套件的Window封裝套件
- 封裝一個的toast彈出框(vue專案)封裝ASTVue
- 封裝一個在react上更易用的redux框架封裝ReactRedux框架
- 封裝一個簡易版的ajax操作物件封裝物件
- AVFoundation | 封裝一個好用的視訊播放器封裝播放器
- Python 之requests封裝通用http協議介面請求Python封裝HTTP協議
- Flutter 如何封裝一個 Banner 輪播圖?Flutter封裝
- Node教程——封裝一個token驗證器封裝