美文网首页Android知识Android开发经验谈Android开发
拒绝无用功,封装一个通用的 PopupWindow

拒绝无用功,封装一个通用的 PopupWindow

作者: 菜鸟窝 | 来源:发表于2017-07-14 09:54 被阅读369次

    作者: 夏至,欢迎转载,但请保留这段申明,谢谢
    https://juejin.im/post/5961e03e51882568b13c3308

    为了避免重复造轮子,我们一般都会封装一个通用的控件,比如这次,项目中需要用到比较多的 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 这中模式不熟悉,可以看我以前文章:

    模仿常用框架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);
            }
        }
    }
    
    image.png
                      识别下方二维码关注“菜鸟窝官网”公众号
                        免费领取“140套优秀开源项目源码”
    
    image.png

    相关文章

      网友评论

        本文标题:拒绝无用功,封装一个通用的 PopupWindow

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