美文网首页Android潜修者android开发Android知识库
通用PopupWindow,几行代码搞定PopupWindow弹

通用PopupWindow,几行代码搞定PopupWindow弹

作者: 依然范特稀西 | 来源:发表于2016-11-30 22:39 被阅读20607次

    我们都知道,Android 中的弹窗基本有两种,一种是AlertDialog,另一种是PopupWindow,AlertDialog的显示位置是固定的,PopWindow 的显示位置是我们可以设置和调整的,因此,像项目中的一些场景如:某个功能的提示说明、点击按钮在按钮上方或者下方弹出菜单、新功能弹窗引导等。由于这些弹窗的位置不固定,因此都可以用PopupWindow来做。最近项目中也用到了PopupWindow弹窗功能,在写的过程中,发现虽然API比较简单,但是写一个PopupWindow,还是有点繁琐,很多重复代码,决定简单封装一下,以后添加一个弹窗就不用那么麻烦了,几行代码就搞定了。本篇文章是对项目中使用 PopupWindow做一个笔记和总结。

    一、首先先看一下效果图

    效果图:

    popWindow.gif

    以上示例展示了4中PopupWindow 使用场景

    Github地址:CustomPopWindow

    二、PopupWindow 简单介绍

    首先看一下Google 官方文档对PopupWindow的介绍:

    This class represents a popup window that can be used to display an arbitrary view. The popup window is a floating container that appears on top of the current activity.

    大致意思就是:popup window 是一个出现在当前Activity顶层的悬浮容器,可以用来展示任意的View。

    因此只要是个View,都可以用PopupWindow 来展示。这里我就不过多介绍PopupWindow 的API,挑几个重要并且常用的说一下:

    1,构造函数,这个不用多说,多个重载函数,穿不同的参数。

    public PopupWindow(int width, int height)
    
    public PopupWindow(View contentView, int width, int height) 
    
    public PopupWindow(View contentView, int width, int height, boolean focusable) 
    

    2,设置显示的View:

    public void setContentView(View contentView)
    

    3,设置展示的宽、高,构造函数传了宽高就不用重新设置

    // 设置宽,其实构造函数也是调用的这个方法
    public void setWidth(int width)
    //设置高
    public void setHeight(int height)
    

    4,设置是否获取焦点

    public void setFocusable(boolean focusable)
    

    5,设置点击PopupWindow 以外区域是否可以隐藏PopupWindow

    public void setOutsideTouchable(boolean touchable)
    

    注意:这里要注意一下,有时侯我们希望触摸PopupWindow 以外区域就隐藏PopupWindow,理论上我们只需要调用 setOutsideTouchable(ture)设置为ture就可以了,但是实际上只设置这个属性是不行的,必须设置背景,也就是说要和setBackgroundDrawable(Drawable background)同时使用才有效,不然,点击PopupWindow以外区域是不能隐藏掉的。

    6,隐藏PopupWindow

    public void dismiss()
    

    7,设置dissmiss 回调监听

    public void setOnDismissListener(OnDismissListener onDismissListener)
    

    8,显示PopupWindow

    //直接显示在参照View 的左下方
    public void showAsDropDown(View anchor)
    // 显示在参照View的左下方,可以通过xoff,yOff,来调节x,y方向的偏移
    public void showAsDropDown(View anchor, int xoff, int off)
    
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity)
    //显示在指定位置,相对于整个屏幕的window而言,通过gravity调解显示在左、上、右、下、中. x,y调整两个方向的偏移
    public void showAtLocation(View parent, int gravity, int x, int y) 
    

    以上就是PopupWindow 重要的并且常用的API。

    三、封装通用PopupWindow,CustomPopWindow,使用链式的方式配置并显示

    由于每次写PopupWindow都要写很多重复代码,因此简单的封装了一个CustomPopWindow.封装了PopupWindow 的一些常用API,使用Builder模式,就像写AlertDialog 一样,链式配置。

    使用方法:
    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();
    
        }
    

    以上就是对于 PopupWindow的封装和使用示例。封装了PopupWindow 常用的API,没有展示完全,还有像添加显示和隐藏动画setAnimationStyle(int animationStyle),隐藏回调监听等等。需要的朋友自己试试。

    最后,贴出CustomPopWindow的源码:

    /**
     *
     * 自定义PopWindow类,封装了PopWindow的一些常用属性,用Builder模式支持链式调用
     * Created by zhouwei on 16/11/28.
     */
    
    public class CustomPopWindow {
        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 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);
            }
    
            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);//设置一些属性
    
            mPopupWindow.setFocusable(mIsFocusable);
            mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            mPopupWindow.setOutsideTouchable(mIsOutside);
    
            if(mWidth == 0 || mHeight == 0){
                mPopupWindow.getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
                //如果外面没有设置宽高的情况下,计算宽高并赋值
                mWidth = mPopupWindow.getContentView().getMeasuredWidth();
                mHeight = mPopupWindow.getContentView().getMeasuredHeight();
            }
    
            mPopupWindow.update();
    
            return mPopupWindow;
        }
    
        /**
         * 关闭popWindow
         */
        public void dissmiss(){
            if(mPopupWindow!=null){
                mPopupWindow.dismiss();
            }
        }
    
    
        public static class PopupWindowBuilder{
            private CustomPopWindow mCustomPopWindow;
    
            public PopupWindowBuilder(Context context){
                mCustomPopWindow = new CustomPopWindow(context);
            }
            public PopupWindowBuilder size(int width,int height){
                mCustomPopWindow.mWidth = width;
                mCustomPopWindow.mHeight = height;
                return this;
            }
    
    
            public PopupWindowBuilder setFocusable(boolean focusable){
                mCustomPopWindow.mIsFocusable = focusable;
                return this;
            }
    
    
    
            public PopupWindowBuilder setView(int resLayoutId){
                mCustomPopWindow.mResLayoutId = resLayoutId;
                mCustomPopWindow.mContentView = null;
                return this;
            }
    
            public PopupWindowBuilder setView(View view){
                mCustomPopWindow.mContentView = view;
                mCustomPopWindow.mResLayoutId = -1;
                return this;
            }
    
            public PopupWindowBuilder setOutsideTouchable(boolean outsideTouchable){
                mCustomPopWindow.mIsOutside = outsideTouchable;
                return this;
            }
    
            /**
             * 设置弹窗动画
             * @param animationStyle
             * @return
             */
            public PopupWindowBuilder setAnimationStyle(int animationStyle){
                mCustomPopWindow.mAnimationStyle = animationStyle;
                return this;
            }
    
    
            public PopupWindowBuilder setClippingEnable(boolean enable){
                mCustomPopWindow.mClippEnable =enable;
                return this;
            }
    
    
            public PopupWindowBuilder setIgnoreCheekPress(boolean ignoreCheekPress){
                mCustomPopWindow.mIgnoreCheekPress = ignoreCheekPress;
                return this;
            }
    
            public PopupWindowBuilder setInputMethodMode(int mode){
                mCustomPopWindow.mInputMode = mode;
                return this;
            }
    
            public PopupWindowBuilder setOnDissmissListener(PopupWindow.OnDismissListener onDissmissListener){
                mCustomPopWindow.mOnDismissListener = onDissmissListener;
                return this;
            }
    
    
            public PopupWindowBuilder setSoftInputMode(int softInputMode){
                mCustomPopWindow.mSoftInputMode = softInputMode;
                return this;
            }
    
    
            public PopupWindowBuilder setTouchable(boolean touchable){
                mCustomPopWindow.mTouchable = touchable;
                return this;
            }
    
            public PopupWindowBuilder setTouchIntercepter(View.OnTouchListener touchIntercepter){
                mCustomPopWindow.mOnTouchListener = touchIntercepter;
                return this;
            }
    
    
            public CustomPopWindow create(){
                //构建PopWindow
                mCustomPopWindow.build();
                return mCustomPopWindow;
            }
    
        }
    
    }
    

    好了,本篇文章到此结束,以上就是PopupWindow的一些介绍和一个简单的封装。要源码的同学,请看 Github地址:CustomWindow,如果觉得对你有用的话,欢迎start。

    相关文章

      网友评论

      • Holly_dd20:setOutsideTouchable(false) 点击外部还是会消失
        依然范特稀西:@Holly_dd20 这个方法:enableOutsideTouchableDissmiss
      • 骑小猪看流星:外部类调用内部类,链式调用的核心
      • 明朗__:如果需要透明背景效果 这个好像办不到
      • 5c5113a4a475:看的出卤煮是一个很“懒”的人,对一些重复性代码都是会想到去封装,这种代码洁癖:+1: :+1:
      • 83f379da4db4:楼主,棒棒哒!!!:+1: :+1: :+1:
        我想问一下,登录页面,点击EditText弹出软键盘,为了不让软键盘挡住EditText,我让整体布局往上移,取消软键盘的时候,整体布局再恢复。然后问题就出来了,点击下拉的时候弹出账号选择的popWindow,取消软键盘,但是popWindow依然在原来的位置,请问怎么解决。
        83f379da4db4:在代码里面改变布局的位置。让布局整体上移,然后这样就不会挡住登录按钮了,类似于手机QQ的登录。但是软键盘隐藏之后,popwindow依旧在位置改变之后的EditText下面,可是这个时候EditText已经恢复到原来的位置了。
        依然范特稀西:@Trumenxing 这个我还真没遇到过,你是咋个让布局上移的呢
      • StupidBirdDo:链式调用优点是啥 是保持单例吗?
      • 汪简书:关闭popWindow dissmiss() 会调用2次,改成这样就可以了
        ```java
        if (mOnDismissListener != null) {
        mOnDismissListener.onDismiss();
        mOnDismissListener = null;
        }
        ```
        依然范特稀西:@然而喵星人早已看穿了一切 收到
      • Bakon:popupwindo里面添加fragment吗
      • 0841905d3f0e:想问下,这个popwindow 怎么设置透明?
        依然范特稀西:@深夜诗人_fcb9 设置透明有讲哦,请看这篇文章http://www.jianshu.com/p/46d13fe78099
      • 662fefb8182e:感谢作者,用起来很方便,不过我有个问题一直没解决,就是我希望点击弹窗外面任何位置,也要求弹窗不消失,但是设置背景后再设置setOutsideTouchable()参数true或者false时,点击弹窗外侧,弹窗都会消失。。。弹窗不设置背景的话,点击外侧是不会消失,不过点击back键,也无效了,好纠结。。。。
        A_si: PopWindow popWindow = new PopWindow.PopupWindowBuilder(MainActivity.this)
        .size(DrawerLayout.LayoutParams.MATCH_PARENT, DrawerLayout.LayoutParams.WRAP_CONTENT)
        .setOutsideTouchable(false)
        .setView(R.layout.pop).create();这样设置,点击外部,依然会消失。为什么
        662fefb8182e:@依然范特稀西 恩 可以实现 多谢指点 感谢:blush:
        依然范特稀西:对的,文章中说了这个问题的,点击popwindow 之外消失,必须要设置背景才有效,你试一下不设置然后监听返回按钮点击事件,手动调用dissmiss 看行不啊
      • null人:shouc
      • 6f5eccb2caf4:谢谢分享,学习了!
      • ysoyiyi:好棒
      • code小生:不错
      • 67d4f071ea5f:弹出时背景没有变黑
      • hfk:链式调用的方式不错,我喜欢,哈哈
        依然范特稀西:@hfk 谢谢支持,:stuck_out_tongue_winking_eye::stuck_out_tongue_winking_eye:

      本文标题:通用PopupWindow,几行代码搞定PopupWindow弹

      本文链接:https://www.haomeiwen.com/subject/hibtmttx.html