1. 程式人生 > >安卓專案實戰之通用的PopupWindow封裝和使用

安卓專案實戰之通用的PopupWindow封裝和使用

如何使用

1:背景變暗配置示例:

//建立並顯示popWindow
     mCustomPopWindow= new CustomPopWindow.PopupWindowBuilder(this)
                    .setView(contentView)
                    .enableBackgroundDark(true) //彈出popWindow時,背景是否變暗
                    .setBgDarkAlpha(0.7f) // 控制亮度
                    .create()
                    .
showAsDropDown(mButton5,0,20);

2:顯示消失動畫配置:

CustomPopWindow popWindow = new CustomPopWindow.PopupWindowBuilder(this)
                    .setView(R.layout.pop_layout1)
                    .setFocusable(true)
                    .setOutsideTouchable(true)
                    .setAnimationStyle(R.style.
CustomPopWindowStyle) // 新增自定義顯示和消失動畫 .create() .showAsDropDown(mButton1,0,10);

3:點選PopupWindow以外區域不讓關閉(預設DisMiss):

mPopWindow = new CustomPopWindow.PopupWindowBuilder(this)
                    .setView(view)
                    .enableOutsideTouchableDissmiss(false
)// 設定點選PopupWindow之外的地方,popWindow不關閉,如果不設定這個屬性或者為true,則關閉 .create(); mPopWindow.showAsDropDown(mButton7,0,10);

1,簡便寫法

    CustomPopWindow popWindow = new CustomPopWindow.PopupWindowBuilder(this)
                    .setView(R.layout.pop_layout1)//顯示的佈局,還可以通過設定一個View
               //     .size(600,400) //設定顯示的大小,不設定就預設包裹內容
                    .setFocusable(true)//是否獲取焦點,預設為ture
                    .setOutsideTouchable(true)//是否PopupWindow 以外觸控dissmiss
                    .create()//建立PopupWindow
                    .showAsDropDown(mButton1,0,10);//顯示PopupWindow

以上就是彈出一個簡單的PopupWindow,是不是看起來很優雅和簡單,還可以簡單一點:

    CustomPopWindow popWindow = new CustomPopWindow.PopupWindowBuilder(this)
                    .setView(R.layout.pop_layout1)//顯示的佈局
                    .create()//建立PopupWindow
                    .showAsDropDown(mButton1,0,10);//顯示PopupWindow

如果是一個簡單的只展示文案的彈窗,就可以只設置一個View,就可以了,很簡單吧!!!

2,展示一個PopupWindow 彈窗選單(像手機QQ,微信的頂部選單)

    View contentView = LayoutInflater.from(this).inflate(R.layout.pop_menu,null);
            //處理popWindow 顯示內容
            handleLogic(contentView);
            //建立並顯示popWindow
            mCustomPopWindow= new CustomPopWindow.PopupWindowBuilder(this)
                    .setView(contentView)
                    .create()
                    .showAsDropDown(mButton3,0,20);

如果PopupWindow 展示的內容需要在程式程式碼中設定或者響應點選事件等,可以現獲取到這個View,然後處理一些顯示和點選事件邏輯,再交給CustomPopWindow 建立顯示。比如響應選單點選事件的邏輯處理:

     /**
         * 處理彈出顯示內容、點選事件等邏輯
         * @param contentView
         */
        private void handleLogic(View contentView){
            View.OnClickListener listener = new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(mCustomPopWindow!=null){
                        mCustomPopWindow.dissmiss();
                    }
                    String showContent = "";
                    switch (v.getId()){
                        case R.id.menu1:
                            showContent = "點選 Item選單1";
                            break;
                        case R.id.menu2:
                            showContent = "點選 Item選單2";
                            break;
                        case R.id.menu3:
                            showContent = "點選 Item選單3";
                            break;
                        case R.id.menu4:
                            showContent = "點選 Item選單4";
                            break;
                        case R.id.menu5:
                            showContent = "點選 Item選單5" ;
                            break;
                    }
                    Toast.makeText(MainActivity.this,showContent,Toast.LENGTH_SHORT).show();
                }
            };
            contentView.findViewById(R.id.menu1).setOnClickListener(listener);
            contentView.findViewById(R.id.menu2).setOnClickListener(listener);
            contentView.findViewById(R.id.menu3).setOnClickListener(listener);
            contentView.findViewById(R.id.menu4).setOnClickListener(listener);
            contentView.findViewById(R.id.menu5).setOnClickListener(listener);
        }
    }

3,展示一個ListView,其實跟上面是一樣的,這裡貼一下例項程式碼:

private void showPopListView(){
            View contentView = LayoutInflater.from(this).inflate(R.layout.pop_list,null);
            //處理popWindow 顯示內容
            handleListView(contentView);
            //建立並顯示popWindow
            mListPopWindow= new CustomPopWindow.PopupWindowBuilder(this)
                    .setView(contentView)
                    .size(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)//顯示大小
                    .create()
                    .showAsDropDown(mButton4,0,20);
        }
        
    private void handleListView(View contentView){
        RecyclerView recyclerView = (RecyclerView) contentView.findViewById(R.id.recyclerView);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        manager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(manager);
        MyAdapter adapter = new MyAdapter();
        adapter.setData(mockData());
        recyclerView.setAdapter(adapter);
        adapter.notifyDataSetChanged();
    }

其他api的呼叫對照封裝類自行測試。。。。

封裝通用的CustomPopWindow

/**
 *
 * 自定義PopWindow類,封裝了PopWindow的一些常用屬性,用Builder模式支援鏈式呼叫
 */

public class CustomPopWindow implements PopupWindow.OnDismissListener{
    private static final String TAG = "CustomPopWindow";
    private static final float DEFAULT_ALPHA = 0.5f;
    private Context mContext;
    private int mWidth;
    private int mHeight;
    private boolean mIsFocusable = true;
    private boolean mIsOutside = true;
    private int mResLayoutId = -1;
    private View mContentView;
    private PopupWindow mPopupWindow;
    private int mAnimationStyle = -1;

    private boolean mClippEnable = true;//default is true
    private boolean mIgnoreCheekPress = false;
    private int mInputMode = -1;
    private PopupWindow.OnDismissListener mOnDismissListener;
    private int mSoftInputMode = -1;
    private boolean mTouchable = true;//default is ture
    private View.OnTouchListener mOnTouchListener;

    private Window mWindow;//當前Activity 的視窗
    /**
     * 彈出PopWindow 背景是否變暗,預設不會變暗。
     */
    private boolean mIsBackgroundDark = false;

    private float mBackgroundDrakValue = 0;// 背景變暗的值,0 - 1
    /**
     * 設定是否允許點選 PopupWindow之外的地方,關閉PopupWindow
     */
    private boolean enableOutsideTouchDisMiss = true;// 預設點選pop之外的地方可以關閉

    private CustomPopWindow(Context context){
        mContext = context;
    }

    public int getWidth() {
        return mWidth;
    }

    public int getHeight() {
        return mHeight;
    }

    /**
     *
     * @param anchor
     * @param xOff
     * @param yOff
     * @return
     */
    public CustomPopWindow showAsDropDown(View anchor, int xOff, int yOff){
        if(mPopupWindow!=null){
            mPopupWindow.showAsDropDown(anchor,xOff,yOff);
        }
        return this;
    }

    public CustomPopWindow showAsDropDown(View anchor){
        if(mPopupWindow!=null){
            mPopupWindow.showAsDropDown(anchor);
        }
        return this;
    }

    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    public CustomPopWindow showAsDropDown(View anchor, int xOff, int yOff, int gravity){
        if(mPopupWindow!=null){
            mPopupWindow.showAsDropDown(anchor,xOff,yOff,gravity);
        }
        return this;
    }


    /**
     * 相對於父控制元件的位置(通過設定Gravity.CENTER,下方Gravity.BOTTOM等 ),可以設定具體位置座標
     * @param parent 父控制元件
     * @param gravity
     * @param x the popup's x location offset
     * @param y the popup's y location offset
     * @return
     */
    public CustomPopWindow showAtLocation(View parent, int gravity, int x, int y){
        if(mPopupWindow!=null){
            mPopupWindow.showAtLocation(parent,gravity,x,y);
        }
        return this;
    }

    /**
     * 新增一些屬性設定
     * @param popupWindow
     */
    private void apply(PopupWindow popupWindow){
        popupWindow.setClippingEnabled(mClippEnable);
        if(mIgnoreCheekPress){
            popupWindow.setIgnoreCheekPress();
        }
        if(mInputMode!=-1){
            popupWindow.setInputMethodMode(mInputMode);
        }
        if(mSoftInputMode!=-1){
            popupWindow.setSoftInputMode(mSoftInputMode);
        }
        if(mOnDismissListener!=null){
            popupWindow.setOnDismissListener(mOnDismissListener);
        }
        if(mOnTouchListener!=null){
            popupWindow.setTouchInterceptor(mOnTouchListener);
        }
        popupWindow.setTouchable(mTouchable);



    }

    private PopupWindow build(){

        if(mContentView == null){
            mContentView = LayoutInflater.from(mContext).inflate(mResLayoutId,null);
        }

        // 2017.3.17 add
        // 獲取當前Activity的window
        Activity activity = (Activity) mContentView.getContext();
        if(activity!=null && mIsBackgroundDark){
            //如果設定的值在0 - 1的範圍內,則用設定的值,否則用預設值
            final  float alpha = (mBackgroundDrakValue > 0 && mBackgroundDrakValue < 1) ? mBackgroundDrakValue : DEFAULT_ALPHA;
            mWindow = activity.getWindow();
            WindowManager.LayoutParams params = mWindow.getAttributes();
            params.alpha = alpha;
            mWindow.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            mWindow.setAttributes(params);
        }


        if(mWidth != 0 && mHeight!=0 ){
            mPopupWindow = new PopupWindow(mContentView,mWidth,mHeight);
        }else{
            mPopupWindow = new PopupWindow(mContentView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        if(mAnimationStyle!=-1){
            mPopupWindow.setAnimationStyle(mAnimationStyle);
        }

        apply(mPopupWindow);//設定一些屬性

        if(mWidth == 0 || mHeight == 0){
            mPopupWindow.getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
            //如果外面沒有設定寬高的情況下,計算寬高並賦值
            mWidth = mPopupWindow.getContentView().getMeasuredWidth();
            mHeight = mPopupWindow.getContentView().getMeasuredHeight();
        }

        // 新增dissmiss 監聽
        mPopupWindow.setOnDismissListener(this);

        //2017.6.27 add:fix 設定  setOutsideTouchable(false)點選外部取消的bug.
        // 判斷是否點選PopupWindow之外的地方關閉 popWindow
        if(!enableOutsideTouchDisMiss){
            //注意這三個屬性必須同時設定,不然不能disMiss,以下三行程式碼在Android 4.4 上是可以,然後在Android 6.0以上,下面的三行程式碼就不起作用了,就得用下面的方法
            mPopupWindow.setFocusable(true);
            mPopupWindow.setOutsideTouchable(false);
            mPopupWindow.setBackgroundDrawable(null);
            //注意下面這三個是contentView 不是PopupWindow
            mPopupWindow.getContentView().setFocusable(true);
            mPopupWindow.getContentView().setFocusableInTouchMode(true);
            mPopupWindow.getContentView().setOnKeyListener(new View.OnKeyListener() {
                @Override
                public boolean onKey(View v, int keyCode, KeyEvent event) {
                    if (keyCode == KeyEvent.KEYCODE_BACK) {
                        mPopupWindow.dismiss();

                        return true;
                    }
                    return false;
                }
            });
            //在Android 6.0以上 ,只能通過攔截事件來解決
            mPopupWindow.setTouchInterceptor(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {

                    final int x = (int) event.getX();
                    final int y = (int) event.getY();

                    if ((event.getAction() == MotionEvent.ACTION_DOWN)
                            && ((x < 0) || (x >= mWidth) || (y < 0) || (y >= mHeight))) {
                        Log.e(TAG,"out side ");
                        Log.e(TAG,"width:"+mPopupWindow.getWidth()+"height:"+mPopupWindow.getHeight()+" x:"+x+" y  :"+y);
                        return true;
                    } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                        Log.e(TAG,"out side ...");
                        return true;
                    }
                    return false;
                }
            });
        }else{
            mPopupWindow.setFocusable(mIsFocusable);
            mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            mPopupWindow.setOutsideTouchable(mIsOutside);
        }
        // update
        mPopupWindow.update();

        return mPopupWindow;
    }

    @Override
    public void onDismiss() {
        dissmiss();
    }

    /**
     * 關閉popWindow
     */
    public void dissmiss(){

        if(mOnDismissListener!=null){
            mOnDismissListener.onDismiss();
        }

        //如果設定了背景變暗,那麼在dissmiss的時候需要還原
        if(mWindow!=null){
            WindowManager.LayoutParams params = mWindow.getAttributes();
            params.alpha = 1.0f;
            mWindow.setAttributes(params);
        }
        if(mPopupWindow!=null && mPopupWindow
            
           

相關推薦

專案實戰通用PopupWindow封裝使用

如何使用 1:背景變暗配置示例: //建立並顯示popWindow mCustomPopWindow= new CustomPopWindow.PopupWindowBuilder(this) .setView(

專案實戰好用的SharedPreferences工具類封裝

SpUtils.java程式碼如下: public class SpUtils { /** * 儲存在手機裡面的檔名,在這裡要特別注意,因為在Android中已經確定了SharedPreferences是以xml形式儲存, * 所以,在填寫檔名引數時,不要

專案實戰關於獲取SD卡指定路徑檔案的講解

前言 當我們將手機連線到電腦上時,我們發現在SD卡的根目錄下生成了成百上千的由各種應用程式建立的資料夾,導致我們也不知道哪個資料夾是用來幹嘛的,這正因為這樣安卓開發人員經常受到使用者的吐槽。 的確作為一個安卓開發者,我們的確不應該將我們應用的資料直接存到SD卡的根目錄下,這樣當手機安

專案實戰Activity啟動過程中動態獲取元件寬高的3種方式

前言 有時候我們需要在Activity啟動的時候獲取某一元件的寬或者是高用於動態的更改UI佈局,但是這時候我們直接通過getWidth和getHeight方法獲取是有問題的,如下: 我們在Activity的onCreate方法中呼叫如下的方法來獲取元件的寬高: /** * 在onC

專案實戰設定Activity跳轉動畫的5種實現方式

前言 在介紹activity的切換動畫之前我們先來說明一下實現切換activity的兩種方式: 1,呼叫startActivity方法啟動一個新的Activity並跳轉其頁面 2,呼叫finish方法銷燬當前的Activity返回上一個Activity介面 當呼叫startActiv

專案實戰如何debug執行具有release簽名的apk

需求分析 眾所周知,Android的安裝包有測試包(debug版本)和正式包(release版本)之分,一般我們測試時安裝的debug版本預設採用的簽名都是系統幫我們提供的debug.keystore簽名檔案,該檔案位於C:\Users\Administrator.android目錄

專案實戰強大的網路請求框架okGo使用詳解(六):擴充套件專案okServer,更強大的下載上傳功能,支援斷點多工管理

OkGo與OkDownload的區別就是,OkGo只是簡單的做一個下載功能,不具備斷點下載,暫停等操作,但是這在很多時候已經能滿足需要了。 而有些app需要有一個下載列表的功能,就像迅雷下載一樣,每個下載任務可以暫停,可以繼續,可以重新下載,可以有下載優先順序,這時候OkDownload就有

專案實戰強大的網路請求框架okGo使用詳解(五):擴充套件專案okRx,完美結合RxJava

前言 在第一篇講解okGo框架新增依賴支援時,還記得我們額外新增的兩個依賴嗎,一個okRx和一個okServer,這兩個均是基於okGo框架的擴充套件專案,其中okRx可以使請求結合RxJava一起使用,而okServer則提供了強大的下載上傳功能,如斷點支援,多工管理等,本篇我們主要講

專案實戰強大的網路請求框架okGo使用詳解(四):Cookie的管理

Cookie概念相關 具體來說cookie機制採用的是在客戶端保持狀態的方案,而session機制採用的是在伺服器端保持狀態的方案。同時我們也看到,由於採用伺服器端保持狀態的方案在客戶端也需要儲存一個標識,所以session機制是需要藉助於cookie機制來達到儲存標識的目的,所謂ses

專案實戰強大的網路請求框架okGo使用詳解(三):快取的使用

相關實體類必須實現序列化介面 使用快取前,必須讓涉及到快取javaBean物件實現Serializable介面,否者會報NotSerializableException。因為快取的原理是將物件序列化後直接寫入資料庫中,如果不實現Serializable介面,會導致物件無法序列化,進而無法

專案實戰強大的網路請求框架okGo使用詳解(二):深入理解Callback自定義JsonCallback

前言 JSON是一種取代XML的資料結構,和xml相比,它更小巧但描述能力卻不差,由於它的小巧所以網路傳輸資料將減少更多流量從而加快了傳輸速度,目前客戶端伺服器返回的資料大多都是基於這種格式的,相應的我們瞭解的關於json的解析工具主要有兩個:Gson(Google官方出的)和fas

專案實戰強大的網路請求框架okGo使用詳解(一):實現get,post基本網路請求,下載上傳進度監聽以及對Callback自定義的深入理解

1.新增依賴 //必須使用 compile 'com.lzy.net:okgo:3.0.4' //以下三個選擇新增,okrx和okrx2不能同時使用,一般選擇新增最新的rx2支援即可 compile 'com.lzy.net:okrx:1.0.2' compile 'com.lzy

專案實戰APP版本升級更新,適配7.0

前言 APP的版本升級主要分為兩種方式: 1.應用市場升級 2.應用內升級 而應用內升級的方式是目前大多數APP採用的升級更新方式。 應用內升級的模式 按照不同的業務需求又可以分為兩種: 1,強制性更新 如果APP有更新,那麼則彈出更新提示對話方塊,並且

專案實戰CoordinatorLayout實現頁面特效(一)

效果圖如下: material design控制元件簡介: 轉載自:https://blog.csdn.net/gitzzp/article/details/52573068 CoordinatorLayout CoordinatorLayout:協調者佈局。它是support

專案實戰Glide 3高手養成(三):Glide的回撥與監聽

前言 通過前面兩篇的講解,我們對於Glide的一些使用都有了基本瞭解,知道了使用Glide載入圖片只需要一行程式碼即可: Glide.with(this).load(url).into(imageView); 而在這一行程式碼的背後,Glide幫我們執行了成千上萬行的邏輯。

專案實戰Glide 3高手養成(二):Glide強大的圖片變換功能

使用Glide時普遍會遇到的一個問題,如何解決? 首先我們嘗試使用Glide來載入一張圖片,圖片URL地址是:https://www.baidu.com/img/bd_logo1.png 這是百度首頁logo的一張圖片,圖片尺寸是540*258畫素。 接下來我們編寫一個非常簡單的佈局檔案

專案實戰Glide 3高手養成(一):Glide的基本使用

前言 現在Android上的圖片載入框架非常成熟,從最早的老牌圖片載入框架UniversalImageLoader,到後來Google推出的Volley,再到後來的新興軍Glide和Picasso,當然還有Facebook的Fresco。每一個都非常穩定,功能也都十分強大。但是它們的使用

專案實戰記憶體洩漏檢測神器LeakCanary

為什麼會產生記憶體洩漏? Java記憶體洩漏指的是程序中某些物件(垃圾物件)已經沒有使用價值了,但有另外一個正在使用的物件持有它的引用,從而導致它不能回收停留在堆記憶體中,這就產生了記憶體洩漏。無用的物件佔據著記憶體空間,使得實際可使用記憶體變小,形象地說法就是記憶體洩漏了。 記憶體

專案實戰:最實用的載入中、無網路、無資料、出錯四種情況切換庫的使用總結

效果圖 錄出來的效果不是很好,真機上展示效果還算完美。 如圖所示該效果的實現依賴三個庫: 1,頂部Tab導航:比TabLayout功能更強大的FlycoTabLayout,具體使用檢視本人之前部落格:安卓專案實戰之:FlycoTabLayout和FlycoRoundView的介紹及

專案實戰:ToolBar的使用介紹

ToolBar簡介 Toolbar是谷歌在2014年Google IO 大會上推出的一套全新的設計規範Material Design中的控制元件之一,主要是用來在android 5.0之後代替Android傳統的標題欄ActionBar的,引入在android-support-v7相