1. 程式人生 > >拒絕無用功,封裝一個通用的 PopupWindow

拒絕無用功,封裝一個通用的 PopupWindow

為了避免重複造輪子,我們一般都會封裝一個通用的控制元件,比如這次,專案中需要用到比較多的 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(this)
                        .setContentView(R.layout.popup_calendar)
                        .setwidth(LinearLayout.LayoutParams.WRAP_CONTENT)
                        .setheight(LinearLayout.LayoutParams.WRAP_CONTENT)
                        .setFouse(true)
                        .setOutSideCancel(true)
                        .setAnimationStyle(R.style.popup_anim_style)
                        .builder()
                        .showAtLocation(R.layout.activity_calendar, Gravity.CENTER,0,0);

注意上面的 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 完成程式碼

以下是我現在用的程式碼,大家可以參考一下,根據自己的需求新增或者刪除。

public  class CustomPopupWindow {
    private PopupWindow mPopupWindow;
    private View contentview;
    private static Context mContext;
    public CustomPopupWindow(Builder builder) {
        contentview = LayoutInflater.from(mContext).inflate(builder.contentviewid,null);
        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);
    }
    /**
     * popup 消失
     */
    public void dismiss(){
        if (mPopupWindow != null){
            mPopupWindow.dismiss();
        }
    }
    /**
     * 根據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
     */
    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,gravity,offx,offy);
        }
        return this;
    }
    /**
     * 顯示在 targetview 的不同位置
     * @param targetview
     * @param gravity
     * @param offx
     * @param offy
     * @return
     */
    public CustomPopupWindow showAsLaction(View targetview,int gravity,int offx,int offy){
        if (mPopupWindow != null){
            mPopupWindow.showAsDropDown(targetview,gravity,offx,offy);
        }
        return this;
    }
    /**
     * 根據id設定焦點監聽
     * @param viewid
     * @param listener
     */
    public void setOnFocusListener(int viewid,View.OnFocusChangeListener listener){
        View view = getItemView(viewid);
        view.setOnFocusChangeListener(listener);
    }
    /**
     * builder 類
     */
    public static class Builder{
        private int contentviewid;
        private int width;
        private int height;
        private boolean fouse;
        private boolean outsidecancel;
        private int animstyle;
        public Builder(Context context){
            mContext = context;
        }
        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 CustomPopupWindow builder(){
           return new CustomPopupWindow(this);
        }
    }
}