拒绝无用功,封装一个通用的PopupWindow_夏至的稻穗的博客-程序员宅基地

技术标签: android-封装  popupwindw  封装  

作者: 夏至,欢迎转载,但请保留这段申明,谢谢
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()
                        .setContext(this) //设置 context
                        .setContentView(R.layout.popup_calendar) //设置布局文件
                        .setwidth(LinearLayout.LayoutParams.WRAP_CONTENT) //设置宽度,由于我已经在布局写好,这里就用 wrap_content就好了
                        .setheight(LinearLayout.LayoutParams.WRAP_CONTENT) //设置高度
                        .setFouse(true)  //设置popupwindow 是否可以获取焦点
                        .setOutSideCancel(true) //设置点击外部取消
                        .setAnimationStyle(R.style.popup_anim_style) //设置popupwindow动画
                        .builder() //
                        .showAtLocation(R.layout.activity_calendar, Gravity.CENTER,0,0); //设置popupwindow居中显示

注意上面的 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 完成代码

以下是我现在用的代码,大家可以参考一下,根据自己的需求添加或者删除。

* 动画部分:*

 <style name="popup_anim_style">
        <item name="android:windowEnterAnimation">@anim/menushow</item>
        <item name="android:windowExitAnimation">@anim/menudiss</item>
    </style>

menushow :

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fillAfter="true">
    <scale
        android:fromXScale="0"
        android:fromYScale="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="1.0"
        android:toYScale="1.0" />
    <alpha
        android:fromAlpha="0"
        android:toAlpha="1.0" />

</set>

menudiss :

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="300"
    android:fillAfter="true">
    <scale
        android:fromXScale="1"
        android:fromYScale="1"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toXScale="0"
        android:toYScale="0" />
    <alpha
        android:fromAlpha="1.0"
        android:toAlpha="0" />

</set>

布局就不贴出来,由于用到自定义控件,贴出来反而不好,大家根据自己的需求,编写即可。

CustomPopupWindow 完整代码:

public  class CustomPopupWindow {
    
    private PopupWindow mPopupWindow;
    private View contentview;
    private  Context mContext;
    public CustomPopupWindow(Builder builder) {
        mContext = builder.context;
        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;
        private Context context;

       public Builder setContext(Context context){
           this.context = context;
           return this;
       }


        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);
        }
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u011418943/article/details/74908772

智能推荐

一个有趣的时钟flash_恒奇恒毅的博客-程序员宅基地

div>script charset="Shift_JIS" src="http://chabudai.sakura.ne.jp/blogparts/honehoneclock/honehone_clock_wh.js">script>div>

ANFIS学习笔记(二)_anfis-python_SimpleMikasa的博客-程序员宅基地

ANFIS学习笔记(二)matlab自带ANFIS工具箱介绍ANFIS工具箱语法语法说明matlab自带ANFIS工具箱介绍上一章主要介绍了ANFIS的起源、模型以及算法部分,这里介绍一下matlab自带的ANFIS工具箱的使用,详情可参照help center中的介绍(貌似要翻墙???)。ANFIS工具箱语法ANFIS语法目前有以下5种。fis=anfis(trainingData)...

android 拍照 预览图与 照片分辨率(可视区域)不一致_zzwkcc的博客-程序员宅基地

问题来源是来自项目自定义相机模块,问题出现在拍摄下的照片与预览的照片课时范围不一致,在测试手机上出现的具体情况是拍摄的照片可视宽度大于预览的画面。问题出在 该自定义相机采用了全屏预览画面,随着全面屏手机的发展,现在的屏幕可视区域的长宽比越来越放飞自我,很多介于2:1和16:9之间,而手机摄像头的采集分辨率相对固定,常用的16:9 4:3 13:9 2:1 1:1 的分辨率,与手机的屏幕不一致,最终导致全屏预览下,预览画面无法与相机拍摄内容完全一致,简单解决方式类似手机系统相机,将预览画...

给定一个整数数组,找出其中两个数相加等于目标值_整数数组,找出相加为输入16 的元素_tromaker3的博客-程序员宅基地

Example:Given nums = [2, 7, 11, 15], target = 9,Because nums[0] + nums[1] = 2 + 7 = 9,return [0, 1].题目的意思:在无序的数组中找两个数,使得这两个数之和与给定的目标值相等,返回这两个数的下标。大佬们的做法:https://blog.csdn.net/wz2292667460/article...

小程序开发视频教程免费下载_ordinary90的博客-程序员宅基地

01-内容介绍.wmv02-微信小程序开发工具的安装.wmv03-案例体验.wmv04-项目结构1.wmv05-项目结构2.wmv06-编程体验.wmv01-回顾与概要.wmv02-导航条的配置.wmv03-创建页面及配置.wmv04-Swiper使用.wmv05-列表样式.wmv06-页面与页面之间传值.wmv07-加载数据.wmv08-Demo结构介绍.wmv源码及开发工具...

嵌入式中sprintf %f 失效问题解决_Z大侠的博客-程序员宅基地

 在嵌入式c语言编程中,使用sprintf %f时经常出现一些莫名其妙的问题,如:打印结果为0;打印越界;死机.... 在查阅了一些资料后,发现是使用嵌入式操作系统时,堆栈8字节对齐会有影响(具体详细原因我还没搞明白).......= =、为了避免这个问题,徒手敲了一个浮点数转字符串的函数,并做了长度限制记录如下:/*功 能: 浮点数保留小数位数四舍五入*//*输入参数: ...

随便推点

在C++中模拟委托事件的方法(下篇)_using dpex::_幺幺桃的博客-程序员宅基地

转自:http://blog.csdn.net/gogogo/article/details/6999960四、静态函数与类模板结合模拟事件对应的例子工程名DelegateEvent为了解决多个对象接收不同的事件的问题,同时规范化程序的编写,我们这里使用C++模板类的方法来定义一个委托类管理事件1、  具体的实现方法(1)、委托类模板的定义与实现

Magento2 Minicart 的工作过程_weixin_33739541的博客-程序员宅基地

2019独角兽企业重金招聘Python工程师标准&gt;&gt;&gt; ...

endnote无法同步原因_截至2020年7月最完美最详细PC(Win or Mac)+iPad的多平台同步的解决方案..._Ace Wu的博客-程序员宅基地

折腾了好久,终于获得个人觉得最完美的解决方案。先说结论:iPad (pro)+pencil + Zotero + ZotFile + 坚果云 + apple快捷指令快毕业了才折腾出来这个方案。赶紧趁热打铁记录下来。后面会详细介绍,并说明该方案目前为数不多的缺点和注意点。特别感谢 @MiracleXYZ 制作的「ZotExpert」快捷指令!大家可以移步看她的【工具】ZotExpert:实现iPad...

Linux 应用---make及makefile的编写_makefile make的第一个目标_zqixiao_09的博客-程序员宅基地

Make 在我们做linux 开发中是必不可少的一部分,它在我们编写大型项目工程文件中起到非常大的作用。     Make工程管理器也就是个“自动编译管理器”,这里的“自动”是指它能够根据文件时间戳自动发现更新过的文件而减少编译的工作量,同时,它通过读入Makefile文件的内容来执行大量的编译工作。Make将只编译改动的代码文件,而不用完全编译。    而Makefile是Make读入的

HyperLedger-fabric V0.6 for CentOS7.2开发环境搭建_weixin_34007886的博客-程序员宅基地

BlockChain联盟链中的Hyperledger项目中的Fabrica项目是商业联盟应用区块链的基石之作,现在来搭建一个运行环境,进行技术验证。安装组件如下:1.Docker-compose:Docker 容器管理; 2.Go lang SDK:Go 语言开发、编译环境; 3.Git:git 镜像克隆与提交; 4.Rest Client: rest ...

推荐文章

热门文章

相关标签