自定義PopupWindow- MvpCommonPopView

1. 使用方式:

  • 自己寫一個子類繼承 在子類內(nèi)部去調(diào)用setContentView(view)以及對view 的初始化
  • 直接new 一個 MvpCommonPopView 實例,調(diào)用 setContentView(view)猜憎;。
  • 在調(diào)用者里面對 view 進(jìn)行初始, 然后調(diào)用 showCenter(view)方法 進(jìn)行顯示,把pop 顯示在屏幕中間,view 參數(shù)可以是任意一個 view .

2. 該pop 可以顯示 一下幾個功能:<p/>

>- 可以設(shè)置pop 外部區(qū)域點(diǎn)擊時是否關(guān)閉pop :{@link #setTouchOutsideDismiss(boolean)}<p/> // 默認(rèn)不關(guān)閉
> - 可以設(shè)置按返回鍵是否可以關(guān)閉pop : {@link #setOnBackKeyDismiss(boolean)}<p/> // 默認(rèn)是按返回鍵關(guān)閉
> - 可是設(shè)置點(diǎn)擊pop 外部時事件是否傳遞到 pop 的后面 : {@link #setOutsideCanTouch(boolean)}<p/> // 默認(rèn)不傳遞

在調(diào)用者里面對 view 進(jìn)行初始, 然后調(diào)用 showCenter(view)方法 進(jìn)行顯示慎王,

package com.example.jiandao;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.view.Gravity;
import android.widget.Toast;

import com.example.guidepageslib.guidepages.GuidePage;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        GuidePage guidePage = new GuidePage(this);
        guidePage.setGuidePage(new GuidePage.IGuidePage() {
            @Override
            public void onAgree() {
                Toast.makeText(MainActivity.this, "同意", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onRefuse() {
                //關(guān)閉應(yīng)用
                finish();
            }
        });

        //獲取頂級視圖    無法添加窗口-令牌null無效;您的活動正在進(jìn)行嗎?所以使用mHandler
        getWindow().getDecorView().post(new Runnable() {
            @Override
            public void run() {
                //顯示pop
                guidePage.showCenter(getWindow().getDecorView());
            }
        });
    }
}

自己寫一個子類繼MvpCommonPopView

package com.example.guidepageslib.guidepages;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;

import com.example.guidepageslib.R;
import com.example.guidepageslib.util.MvpCommonPopView;


public class GuidePage extends MvpCommonPopView {

    private TextView agree;
    private TextView refuse;
    private IGuidePage GuidePage;

    public GuidePage(Context context) {
        super(context);
        initView(context);
    }

    //調(diào)用此方法 可以實現(xiàn) 同意可拒絕的2個方法
    public void setGuidePage(IGuidePage guidePage) {
        GuidePage = guidePage;
    }

    private void initView(Context context) {
        //創(chuàng)建布局
        View view = LayoutInflater.from(context).inflate(R.layout.layout_splash_pop, null);
        //同意的控件
        agree = (TextView)view.findViewById(R.id.splash_pop_btn_agree);
        //拒絕的控件
        refuse = (TextView)view.findViewById(R.id.splash_pop_btn_stop);

        //同意的監(jiān)聽
        agree.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //實現(xiàn)同意的方法
                GuidePage.onAgree();
            }
        });
        //拒絕的監(jiān)聽
        refuse.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //實現(xiàn)拒絕的方法
                GuidePage.onRefuse();
            }
        });

        //在非觸摸屏設(shè)備中接收事件和處理響應(yīng)的控件是具有焦點(diǎn)(Focused)的控件张抄。
        // 一個窗口中一個時間內(nèi)只能有一個具有焦點(diǎn)的控件
        refuse.setFocusable(false);
        agree.setFocusable(false);

        setContentView(view);
        setOnBackKeyDismiss(false);

    }

    /**
     * public 此接口必須公開。無法從外部包訪問
     *      onAgree() 同意
     *      onRefuse() 拒絕
     */
     public interface IGuidePage{
        void onAgree();
        void onRefuse();
    }
}

PopupWindow工具類 - MvpCommonPopView

package com.example.guidepageslib.util;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.PopupWindow;

import com.example.mvplib.utils.MvpUtils;


/**
 * popupwindow 底層實際上是通過  windowmanager 添加 一個 叫 DecorView(FrameLayout)的方式實現(xiàn)的洼怔。
 * <p>
 * popupwindow 一般設(shè)計到三個 view,{1.mDecorView(FrameLayout),2.mBackgroundView(FrameLayout),3.mContentView(我們要顯示的layout)}
 * <p>
 * 1 mDecorView: popupWindow 的根view署惯,它的大小就是通過 設(shè)置popouWindow 的 寬高來決定的(setHeight,setWidth)
 * <p>
 * 2 mBackgroundView:它不一定會創(chuàng)建镣隶,取決于 是否調(diào)用了 setBackgroundDrawable 給  popupWindow 設(shè)置背景极谊,如果設(shè)置了那么會創(chuàng)建一個 mBackgroundView
 * 然后添加到mDecorView 上,如下:
 * <p>
 * if (mBackground != null) { // 是否設(shè)置里背景
 * mBackgroundView = createBackgroundView(mContentView);
 * mBackgroundView.setBackground(mBackground);
 * } else {
 * mBackgroundView = mContentView;
 * }
 * <p>
 * //把我們的 mContentView 的傳進(jìn)來 創(chuàng)建 mBackgroundView
 * <p>
 * private PopupBackgroundView createBackgroundView(View contentView) {
 * final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
 * final int height;
 * if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
 * height = WRAP_CONTENT;
 * } else {
 * height = MATCH_PARENT;
 * }
 * <p>
 * final PopupBackgroundView backgroundView = new PopupBackgroundView(mContext);
 * final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(
 * MATCH_PARENT, height);
 * <p>
 * // 把我們的 mContentView 添加到 mBackgroundView上面,
 * // mContentView的寬度是 MATCH_PARENT(和mBackgroundView的寬度一致)安岂。
 * // mContentView的高度 取決于  mContentView.getLayoutParams() 轻猖,要么是MATCH_PARENT,要么是 WRAP_CONTENT
 * backgroundView.addView(contentView, listParams);
 * <p>
 * return backgroundView;
 * }
 * <p>
 * mDecorView = createDecorView(mBackgroundView);
 * <p>
 * // 通過把mBackgroundView 傳進(jìn)來 創(chuàng)建 mDecorView域那,
 * private PopupDecorView createDecorView(View contentView) {
 * final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
 * final int height;
 * <p>
 * // 如果 mContentView 的 layoutParams 咙边!= null && layoutParams.height == WRAP_CONTENT 時,高度為 wrap_content,否則為 match_parent
 * if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
 * height = WRAP_CONTENT;
 * } else {
 * height = MATCH_PARENT;
 * }
 * <p>
 * final PopupDecorView decorView = new PopupDecorView(mContext);
 * // 把 mBackgroundView添加到了 mDecorView上
 * // mBackgroundView 的寬度是 MATCH_PARENT(和 mDecorView 一樣寬次员,也就是說等于 setWidth的值)
 * <p>
 * //mBackgroundView  的高度 取決于mBackgroundView.getLayoutParams(),要么是MATCH_PARENT败许,要么是 WRAP_CONTENT
 * <p>
 * decorView.addView(contentView, MATCH_PARENT, height);
 * decorView.setClipChildren(false);
 * decorView.setClipToPadding(false);
 * <p>
 * return decorView;
 * }
 * <p>
 * <p>
 * <p>
 * <p>
 * 3 mContentView,這就是我們調(diào)用popupwindow 的 setContentView 方法傳進(jìn)去我們要顯示的內(nèi)容view
 * <p>
 * <p>
 * 如果設(shè)置了pop 的backgrounddrawable 那么創(chuàng)建順序是
 * <p>
 * 根據(jù) contentview 創(chuàng)建 backgroundview,再根據(jù)backgroundview 創(chuàng)建 decorview淑蔚,最后 windowmanager add  decorview
 * <p>
 * <p>
 * 如果沒有設(shè)置pop 的backgrounddrawable的背景檐束,那么創(chuàng)建順序是
 * <p>
 * 直接把 contentview 賦值給 backgroundview。 更具 backgroundview 創(chuàng)建 decorview 束倍,最后 windowmanager add  decorview
 * <p>
 * <p>
 * <p>
 * decorview 的寬高是由 setHeight 和 setWidth 決定的被丧。
 * <p>
 * backgroundview 和 contentview 的 寬度都是不能修改的,都為 match_parent,高度要么是 wrap_content ,要么是 match_parent绪妹,只有當(dāng)
 * <p>
 * contentview 的 layoutparams 不為 null甥桂, 并且其 layoutparams.height == wrap_content 時 ,高度為wrap_content 否則其他情況都為match_parent
 */

/**
 *
 * 使用方式:
 * 1. 自己寫一個子類繼承  在子類內(nèi)部去調(diào)用setContentView(view)以及對view 的初始化
 * 2. 直接new 一個 MvpCommonPopView 實例邮旷,調(diào)用 setContentView(view)黄选;。在調(diào)用者里面對 view  進(jìn)行初始化
 *
 * 然后調(diào)用 showCenter(view)方法 進(jìn)行顯示,把pop 顯示在屏幕中間办陷,view 參數(shù)可以是任意一個 view .
 *
 * 該pop 可以顯示 一下幾個功能:<p/>
 *  *
 * 1. 可以設(shè)置pop 外部區(qū)域點(diǎn)擊時是否關(guān)閉pop :{@link #setTouchOutsideDismiss(boolean)}<p/> // 默認(rèn)不關(guān)閉
 * 2. 可以設(shè)置按返回鍵是否可以關(guān)閉pop : {@link #setOnBackKeyDismiss(boolean)}<p/> // 默認(rèn)是按返回鍵關(guān)閉
 * 3. 可是設(shè)置點(diǎn)擊pop 外部時事件是否傳遞到 pop 的后面 : {@link #setOutsideCanTouch(boolean)}<p/> // 默認(rèn)不傳遞
 *
 */

public class MvpCommonPopView extends PopupWindow {


    private boolean isOutsideTouchDismiss; // 點(diǎn)擊pop 外部是否消失
    private boolean isBackKeyDismiss = true;// 按返回鍵是否消失

    private  Activity context;

    public MvpCommonPopView(Context context, int width, int height) {
        super(context);

        setHeight(height);
        setWidth(width);
        this.context = (Activity) context;
        init(context);
    }

    // 這個構(gòu)造方法貌夕,pop 的高度是WRAP_CONTENT,寬度是 MATCH_PARENT
    public MvpCommonPopView(Context context) {
        this(context,WindowManager.LayoutParams.MATCH_PARENT,WindowManager.LayoutParams.WRAP_CONTENT);
    }

    protected void init(Context context) {
        // 在比較老的版本上,不設(shè)置背景就不能響應(yīng)返回鍵和點(diǎn)擊外部消失的民镜,但是新版本google api 已經(jīng)修改了啡专,為了保險起見,設(shè)個背景最好
        setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

        backgroundAlpha( 0.5f);
        /**
         * 設(shè)置widow 是否獲取焦點(diǎn)制圈,默認(rèn)false,如果為false,那么點(diǎn)擊pop以外的區(qū)域们童,事件會傳遞到popu后面的(behind)界面上.
         * 點(diǎn)擊pop 以外的區(qū)域pop 不會消失,注意如果pop 里面有輸入框鲸鹦,那么輸入框不能彈出鍵盤
         * 如果設(shè)置為true,pop 會有焦點(diǎn)慧库,點(diǎn)擊外部時 pop 會消失。
         */

        setFocusable(true);
        // 這個方法是控制是否當(dāng)pop 以外的區(qū)域點(diǎn)擊時是否發(fā)送 MotionEvent.ACTION_OUTSIDE 通知 pop,true 通知馋嗜,false 不通知齐板,但是 這個方法必須要 setFocusable = false ,setTouchable = true 時才有效果
        setOutsideTouchable(true);

        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) // 當(dāng) setFocusable為 true 點(diǎn)擊外部會收到這個事件
                        && ((x < 0) || (x >= v.getWidth()) || (y < 0) || (y >= v.getHeight()))) {
                    if (isOutsideTouchDismiss) {
                        dismiss();

                    }
                    return true;

                }

               /* if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { // ACTION_OUTSIDE 事件是 當(dāng) setOutsideTouchable(true) 方法 設(shè)置為true 時,才會被接受葛菇。否則不會接收這個事件
                    if (isOutsideTouchDismiss) {
                        CommonPopView.super.dismiss();
                        return true;
                    }
                }*/


                /**
                 * 返回 true 不攔截甘磨,走默認(rèn)行為
                 *
                 *  @Override
                 *         public boolean onTouchEvent(MotionEvent event) {
                 *             final int x = (int) event.getX();
                 *             final int y = (int) event.getY();
                 *
                 *             if ((event.getAction() == MotionEvent.ACTION_DOWN)
                 *                     && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
                 *                 dismiss();
                 *                 return true;
                 *             } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                 *                 dismiss();
                 *                 return true;
                 *             } else {
                 *                 return super.onTouchEvent(event);
                 *             }
                 *         }
                 */
                return false;
            }
        });


    }


    /**
     * 設(shè)置點(diǎn)擊pop 外部是否消失,這個方法只有在給pop 設(shè)置的
     *  寬高不是Match_parent 才有效,因為設(shè)置為了Match_parent這個都是pop,那就沒有點(diǎn)擊外部只說
     * @param dismiss
     */
    //
    public void setTouchOutsideDismiss(boolean dismiss) {
        isOutsideTouchDismiss = dismiss;


    }

    // view: 任意一個已經(jīng)顯示在界面上的view熟呛,注意如果這個view 沒有添加的 window 上時宽档,這個方法會報錯尉姨。
    // 因為 pop 在顯示的時候需要通過 view.getWidowToken.
    public void showCenter(View view){
        showAtLocation(view, Gravity.CENTER,0,0);
    }



    public void setOnBackKeyDismiss(boolean dismiss) {
        isBackKeyDismiss = dismiss;
    }

    @Override
    public void dismiss() {

        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        StackTraceElement traceElement = stackTrace[3];
        String fileName = traceElement.getFileName(); // 拿到調(diào)用 dismiss 方法的類名
        fileName = fileName.substring(0, fileName.lastIndexOf("."));
        if (!fileName.equals(PopupWindow.class.getSimpleName())) { // 如果調(diào)用者不是從系統(tǒng)來的庵朝,那么就是我們認(rèn)為的
            destroy();
            super.dismiss();
        } else {
            if (!isBackKeyDismiss) {
                return;
            } else {
                destroy();
                super.dismiss();
            }
        }


    }

    private void destroy(){
        backgroundAlpha(1);
        this.context = null;
    }


    /**
     * 設(shè)置pop 外部是否可點(diǎn)擊,也就是點(diǎn)擊pop 的外部 能把事件傳遞到pop 的 behind
     * <p>
     * 注意:如果 設(shè)置為true,那么再設(shè)置點(diǎn)擊外部關(guān)閉 pop 將會無效又厉,因此在為true 想關(guān)閉 pop ,必須手動關(guān)閉,調(diào)用close
     *
     * @param touch
     */
    public void setOutsideCanTouch(boolean touch) {

        // 設(shè)置為false九府, 點(diǎn)擊pop 外部,事件會傳遞到 pop 的behind,設(shè)置為true ,不會覆致。雖然設(shè)置為false ,但是 pop 里面如果有 輸入框任然可以獲取焦點(diǎn)侄旬,彈出鍵盤,這和 setFocusable = false 不一樣煌妈。
        if(MvpUtils.hasQ()){
            setTouchModal(!touch);
        }

    }

    /**
     * 設(shè)置添加屏幕的背景透明度
     *
     * @param bgAlpha
     */
    public void backgroundAlpha( float bgAlpha) {
        WindowManager.LayoutParams lp = context.getWindow().getAttributes();
        lp.alpha = bgAlpha;
        context.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
        context.getWindow().setAttributes(lp);
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末儡羔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子璧诵,更是在濱河造成了極大的恐慌汰蜘,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件之宿,死亡現(xiàn)場離奇詭異族操,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)比被,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門色难,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泼舱,“玉大人,你說我怎么就攤上這事枷莉〗筷迹” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵依沮,是天一觀的道長涯贞。 經(jīng)常有香客問我,道長危喉,這世上最難降的妖魔是什么宋渔? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮辜限,結(jié)果婚禮上皇拣,老公的妹妹穿的比我還像新娘。我一直安慰自己薄嫡,他們只是感情好氧急,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著毫深,像睡著了一般吩坝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哑蔫,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天钉寝,我揣著相機(jī)與錄音,去河邊找鬼闸迷。 笑死嵌纲,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的腥沽。 我是一名探鬼主播逮走,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼今阳!你這毒婦竟也來了师溅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤盾舌,失蹤者是張志新(化名)和其女友劉穎墓臭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矿筝,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡起便,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榆综。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡妙痹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鼻疮,到底是詐尸還是另有隱情怯伊,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布判沟,位于F島的核電站耿芹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏挪哄。R本人自食惡果不足惜吧秕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望迹炼。 院中可真熱鬧砸彬,春花似錦、人聲如沸斯入。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽刻两。三九已至增蹭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間磅摹,已是汗流浹背滋迈。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留偏瓤,地道東北人杀怠。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓椰憋,卻偏偏與公主長得像厅克,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子橙依,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354