作者: 夏至,歡迎轉載,但請保留這段申明,謝謝
juejin.im/post/5961e0…
為了避免重複造輪子,我們一般都會封裝一個通用的控制元件,比如這次,專案中需要用到比較多的 popupwindow ,如果需要一個個寫,那麼依舊會累死人,而且還是無用功,無意義,所以,封裝一個通用的,除了讓同事看了直刷666之外,自己還省了很多事情。
先上效果圖:
1、如何使用
那麼,一般我們配置一個 PopupWindow 正常步驟需要多少程式碼呢?如下:
PopupWindow popupWindow = new PopupWindow(this);
View contentview = LayoutInflater.from(this).inflate(R.layout.popup_calendar,null);
popupWindow =
new PopupWindow(contentview,
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
true);
//設定取消
popupWindow.setOutsideTouchable(true);
popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
//設定位置
View rootview = LayoutInflater.from(this).inflate(R.layout.activity_main,null);
popupWindow.showAtLocation(rootview,Gravity.CENTER,0,0);複製程式碼
一般我們需要實現上面的基本程式碼,PopupWindow 才能跑起來,然後我們還需要新增動畫,監聽back鍵等等,然後,另外一個需要用到的時候,又得重複寫,真的讓人很絕望,這個時候,封裝的思想就從腦袋冒出來了,那麼,封裝之後,怎麼樣的呢?如下:
CustomPopupWindow popupWindow = new CustomPopupWindow.Builder()
.setContext(this) //設定 context
.setContentView(R.layout.popup_calendar) //設定佈局檔案
.setwidth(LinearLayout.LayoutParams.WRAP_CONTENT) //設定寬度,由於我已經在佈局寫好,這裡就用 wrap_content就好了
.setheight(LinearLayout.LayoutParams.WRAP_CONTENT) //設定高度
.setFouse(true) //設定popupwindow 是否可以獲取焦點
.setOutSideCancel(true) //設定點選外部取消
.setAnimationStyle(R.style.popup_anim_style) //設定popupwindow動畫
// .setBackGroudAlpha(mActivity,0.7f) //是否設定背景色,原理為調節 alpha
.builder() //
.showAtLocation(R.layout.activity_calendar, Gravity.CENTER,0,0); //設定popupwindow居中顯示複製程式碼
注意上面的 showAtLocation 是在 builder 之後的,表示顯示正中間;如果想讓它顯示在某個 view 的相應位置,也可以使用 showAsLocation() 來實現。
至於為什麼在 builder() 的後面呢?因為不太確定在用的時候,是顯示在父佈局的位置,還是顯示在某個控制元件的相應位置,所以,我把程式碼封裝成下面這樣:
/**
* 根據父佈局,顯示位置
* @param rootviewid
* @param gravity
* @param x
* @param y
* @return
*/
public CustomPopupWindow showAtLocation(int rootviewid,int gravity,int x,int y){
if (mPopupWindow != null){
View rootview = LayoutInflater.from(mContext).inflate(rootviewid,null);
mPopupWindow.showAtLocation(rootview,gravity,x,y);
}
return this;
}複製程式碼
當然,你要把它抽出來也可以的;
還有一種常見的情況,我們常用 popupwindow 作用 dialog,那麼裡面有 button 處理相應的邏輯。那如何想獲取 PopupWindow 裡面的控制元件怎麼辦?為了方便呼叫,這裡我也採用用 id 的形式,所以,呼叫只要這樣即可:
mMonthPicker = (PickerView) popupWindow.getItemView(R.id.picker_month);複製程式碼
然後就可以用 mMonthPicker 這個 view 搞事情了。
這樣就把 contentview 中的控制元件取出來使用了,只要知道 id 就可以了,是不是方便了很多,都挺簡單的,大家自己封裝一邊就ok全明白了。
封裝思路
相比封裝 listview 和 recyclerview ,這個算是比較簡單的,就是觀察最原始的程式碼,提取最核心不變的;無非就是 PopupWindow 的最要佈局
- cnotentview ,為了避免每次都來個 layoutinflate ,我們封裝成一個 id
- 大小,我們都知道 PopupWindow 沒有自己的佈局,上面在給了 contentview 之後,大小也要給
- 顯示位置,顯示就兩個函式 ,showAtLocation 和 showAsLocation ,為了方便,我們也寫成 id 的方式,當然也可以傳入 view
基本就可以了,至於其他附加項,比如動畫,點選外部取消,監聽back鍵,或者簡單 contentview 控制元件的事件,都是變動的,所以,用 Builder 的模式構建比較舒服一些。具體就這些了。如果你對 Builder 這中模式不熟悉,可以看我以前文章:
3、CustomPopupWindow 完成程式碼
以下是我現在用的程式碼,大家可以參考一下,根據自己的需求新增或者刪除。
(編輯器竟然無法更新程式碼,我這邊用圖片來吧,坑爹、、、、)
動畫部分:
menushow :
menudiss :
佈局就不貼出來,由於用到自定義控制元件,貼出來反而不好,大家根據自己的需求,編寫即可。我找個時間把這個上下滾動自定義的 pickerview 也寫一下好了。。。。。
CustomPopupWindow 完整程式碼:
public class CustomPopupWindow implements PopupWindow.OnDismissListener {
private static final String TAG = "CustomPopupWindow";
private PopupWindow mPopupWindow;
private View contentview;
private Context mContext;
private Activity mActivity;
public CustomPopupWindow(Builder builder) {
mContext = builder.context;
contentview = LayoutInflater.from(mContext).inflate(builder.contentviewid,null);
if (builder.width ==0 || builder.height == 0) {
builder.width = ViewGroup.LayoutParams.WRAP_CONTENT;
builder.height = ViewGroup.LayoutParams.WRAP_CONTENT;
}
mPopupWindow =
new PopupWindow(contentview, builder.width, builder.height, builder.fouse);
//需要跟 setBackGroundDrawable 結合
mPopupWindow.setOutsideTouchable(builder.outsidecancel);
mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mPopupWindow.setAnimationStyle(builder.animstyle);
//設定背景色
if (builder.alpha > 0 && builder.alpha < 1){
mActivity = builder.activity;
WindowManager.LayoutParams params = builder.activity.getWindow().getAttributes();
params.alpha = builder.alpha;
builder.activity.getWindow().setAttributes(params);
}
mPopupWindow.setOnDismissListener(this);
}
/**
* popup 消失
*/
public void dismiss(){
if (mPopupWindow != null){
mPopupWindow.dismiss();
if (mActivity != null){
WindowManager.LayoutParams params = mActivity.getWindow().getAttributes();
params.alpha = 1.0f;
mActivity.getWindow().setAttributes(params); //回覆背景色
}
}
}
/**
* 根據id獲取view
* @param viewid
* @return
*/
public View getItemView(int viewid){
if (mPopupWindow != null){
return this.contentview.findViewById(viewid);
}
return null;
}
/**
* 根據父佈局,顯示位置
* @param rootviewid
* @param gravity
* @param x
* @param y
* @return
*/
public CustomPopupWindow showAtLocation(int rootviewid,int gravity,int x,int y){
if (mPopupWindow != null){
View rootview = LayoutInflater.from(mContext).inflate(rootviewid,null);
mPopupWindow.showAtLocation(rootview,gravity,x,y);
}
return this;
}
/**
* 根據id獲取view ,並顯示在該view的位置
* @param targetviewId
* @param gravity
* @param offx
* @param offy
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public CustomPopupWindow showAsLaction(int targetviewId, int gravity, int offx, int offy){
if (mPopupWindow != null){
View targetview = LayoutInflater.from(mContext).inflate(targetviewId,null);
mPopupWindow.showAsDropDown(targetview,offx,offy,gravity);
}
return this;
}
/**
* 顯示在 targetview 的不同位置
* @param targetview
* @param gravity
* @param offx
* @param offy
* @return
*/
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public CustomPopupWindow showAsLaction(View targetview, int gravity, int offx, int offy){
if (mPopupWindow != null){
mPopupWindow.showAsDropDown(targetview,offx,offy,gravity);
}
return this;
}
/**
* 根據id設定焦點監聽
* @param viewid
* @param listener
*/
public void setOnFocusListener(int viewid,View.OnFocusChangeListener listener){
View view = getItemView(viewid);
view.setOnFocusChangeListener(listener);
}
/**
* 根據id設定點選事件監聽
* @param viewid
* @param listener
*/
public void setOnClickListener(int viewid, View.OnClickListener listener){
getItemView(viewid).setOnClickListener(listener);
}
/**
* 監聽 dismiss,還原背景色
*/
@Override
public void onDismiss() {
Log.d(TAG, "onDismiss: ");
if (mActivity != null){
WindowManager.LayoutParams params = mActivity.getWindow().getAttributes();
params.alpha = 1.0f;
mActivity.getWindow().setAttributes(params);
}
}
/**
* builder 類
*/
public static class Builder{
private int contentviewid;
private int width;
private int height;
private boolean fouse;
private boolean outsidecancel;
private int animstyle;
private Context context;
private Activity activity;
private float alpha;
public Builder setContext(Context context){
this.context = context;
return this;
}
public Builder setContentView(int contentviewid){
this.contentviewid = contentviewid;
return this;
}
public Builder setwidth(int width){
this.width = width;
return this;
}
public Builder setheight(int height){
this.height = height;
return this;
}
public Builder setFouse(boolean fouse){
this.fouse = fouse;
return this;
}
public Builder setOutSideCancel(boolean outsidecancel){
this.outsidecancel = outsidecancel;
return this;
}
public Builder setAnimationStyle(int animstyle){
this.animstyle = animstyle;
return this;
}
public Builder setBackGroudAlpha(Activity activity,float alpha){
this.activity = activity;
this.alpha = alpha;
return this;
}
public CustomPopupWindow builder(){
return new CustomPopupWindow(this);
}
}
}複製程式碼