资讯专栏INFORMATION COLUMN

封装一个通用的PopupWindow

ixlei / 1384人阅读

摘要:下面是对外提供的显示的方法,根据不同的枚举类型将显示在不同的位置,具体如下获取目标的坐标获取的宽高使用封装后的下面是使用封装后的,只需四行代码就可以显示一个默认的了,具体如下由于默认背景是透明的,建议测试时设置背景。

上篇文章是关于建造者设计模式的,今天顺便封装一个通用的 PopupWindow 来实践一下, 同时也方便以后使用 PopupWindow,本文将从下面几个方面来介绍 PopupWindow 及其封装,具体如下:

概述

常用方法

基本使用

封装 PopupWindow

使用封装后的PopupWindow

显示效果

概述

PopupWindow 表示一个弹窗,类似于 AlertDialog,相较 AlertDialog 来说 PopupWindow 使用起来更灵活,可有任意指定要显示的位置,当然能够灵活的使用必然在某一层面有所牺牲,如 PopupWindow 相较 AlertDialog 没有默认的布局,每次都得专门创建弹窗的布局,这一点来说 AlertDialog 就比较方便了,所以在开发中没有最好的解决方案,要根据具体的需求选择最合适的解决方案。

常用设置

PopupWindow 的创建,具体如下:

 //构造方法
 public PopupWindow (Context context)  
 public PopupWindow(View contentView)  
 public PopupWindow(View contentView, int width, int height)  
 public PopupWindow(View contentView, int width, int height, boolean focusable) 

PopupWindow 的常用属性设置,具体如下:

 //设置View(必须)
 window.setContentView(contentView);
 //设置宽(必须)
 window.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
 //设置高(必须)
 window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
 //设置背景
 window.setBackgroundDrawable(new ColorDrawable(Color.GRAY));
 //设置PopupWindow之外的触摸事件
 window.setOutsideTouchable(true);
 //设置PopupWindow消失的监听器
 window.setOnDismissListener(this);
 //设置PopupWindow上的触摸事件
 window.setTouchable(true);
 //设置PopupWindow弹出动画
 window.setAnimationStyle(R.style.PopupWindowTranslateTheme);

PopupWindow 的显示有两种设置方式,一种是基于坐标,另一种是基于某个 View ,具体如下:

//基于坐标,参数(当前窗口的某个 View,位置,起始坐标x, 起始坐标y)
void showAtLocation (View parent, int gravity, int x, int y)
//基于某个View,参数(附着的View,x 方向的偏移量,y 方向的偏移量)
void showAsDropDown (View anchor, int xoff, int yoff, int gravity) 
void showAsDropDown (View anchor, int xoff, int yoff)
void showAsDropDown (View anchor) 
基本使用

PopupWindow 的主要内容基本如上,下面使用原生的 PopupWindow 实现一个弹窗,下面是关键代码,具体如下:

//创建PopupWindow
PopupWindow window = new PopupWindow(this);
//设置显示View
window.setContentView(contentView);
//设置宽高
window.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
window.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
//设置背景
window.setBackgroundDrawable(new ColorDrawable(Color.GRAY));
//设置PopupWindow之外的触摸事件
window.setOutsideTouchable(true);
//设置PopupWindow消失的监听器
window.setOnDismissListener(new PopupWindow.OnDismissListener() {
    @Override
    public void onDismiss() {
        //监听PopupWindow的消失
    }
});
//设置PopupWindow上的触摸事件
window.setTouchable(true);
//设置PopupWindow弹出动画
window.setAnimationStyle(R.style.PopupWindowTranslateTheme);
window.showAtLocation(btnTarget, Gravity.BOTTOM | Gravity.CENTER, 0, 0);

下面是显示效果,具体如下:

封装 PopupWindow

这里对 PopupWindow 的封装主要是对 PopupWindow 常用摆放位置做进一步封装,使 PopupWindow 的调用更加灵活、简洁。

在封装过程中遇到的问题是不能正确获取到 PopupWindow 的宽高,正确获取宽高的方法是先对 PopupWindow 进行测量,然后再获取其宽高,具体如下:

//获取PopupWindow的宽高
mPopupWindow.getContentView().measure(
    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
int popupWidth = mPopupWindow.getContentView().getMeasuredWidth();
int popupHeight = mPopupWindow.getContentView().getMeasuredHeight();

对 PopupWindow 的封装使用了建造者设计模式,下面看一下 PopupWindow 的默认配置,具体如下:

public Builder(Context context) {
    this.context = context;
    this.popupWindow = new PopupWindow(context);
    //默认PopupWindow响应触摸事件
    this.outsideTouchable = true;
    //默认响应触摸事件
    this.touchable = true;
    //默认背景透明
    this.backgroundDrawable = new ColorDrawable(Color.TRANSPARENT);
    //默认宽高为WRAP_CONTENT
    this.width  = WindowManager.LayoutParams.WRAP_CONTENT;
    this.height = WindowManager.LayoutParams.WRAP_CONTENT;
    //默认Gravity为Gravity.CENTER
    this.gravity = Gravity.CENTER;
    this.layoutId = -1;
    //默认偏移量为0
    this.offsetX = 0;
    this.offsetY = 0;
    //...
}

由于宽高、背景、是否可点击等相关属性已经设置了默认值,使用时根据自己的需求设置相关属性,如 PopupWindow 的动画等,所以这些设置肯定是非必须的,那么那些事创建时必须的呢。

下面是对 PopupWindow 封装类 MPopupWindow 的初始化,具体如下:

private void setPopupWindowConfig(MPopupWindow window) {
        if (contentView != null && layoutId != -1){
            throw new MException("setContentView and setLayoutId can"t be used together.", "0");
        }else if (contentView == null && layoutId == -1){
            throw new MException("contentView or layoutId can"t be null.", "1");
        }

        if (context == null) {
            throw new MException("context can"t be null.", "2");
        } else {
            window.mContext = this.context;
        }

        window.mWidth  = this.width;
        window.mHeight = this.height;
        window.mView = this.contentView;
        window.mLayoutId = layoutId;
        window.mPopupWindow = this.popupWindow;
        window.mOutsideTouchable   = this.outsideTouchable;
        window.mBackgroundDrawable = this.backgroundDrawable;
        window.mOnDismissListener  = this.onDismissListener;
        window.mAnimationStyle = this.animationStyle;
        window.mTouchable = this.touchable;
        window.mOffsetX = this.offsetX;
        window.mOffsetY = this.offsetY;
        window.mGravity = this.gravity;
    }
}

显然,这里可以看出 context 和 contentView 或 layoutId 是必须需要设置的,如果没有设置相应的会有错误提示,当然在封装中也对 contentView 和 layoutId 不能同时使用做了限制和如果使用了两者的错误提示。

下面是对外提供的显示 PopupWindow 的方法,根据不同的枚举类型将 PopupWindow 显示在不同的位置,具体如下:

public void showPopupWindow(View v, LocationType type) {
    if (mView!=null){
        mPopupWindow.setContentView(mView);
    }else if (mLayoutId != -1){
        View contentView = LayoutInflater.from(mContext).inflate(mLayoutId, null);
        mPopupWindow.setContentView(contentView);
    }
    mPopupWindow.setWidth(mWidth);
    mPopupWindow.setHeight(mHeight);
    mPopupWindow.setBackgroundDrawable(mBackgroundDrawable);
    mPopupWindow.setOutsideTouchable(mOutsideTouchable);
    mPopupWindow.setOnDismissListener(mOnDismissListener);
    mPopupWindow.setAnimationStyle(mAnimationStyle);
    mPopupWindow.setTouchable(mTouchable);
    //获取目标View的坐标
    int[] locations = new int[2];
    v.getLocationOnScreen(locations);
    int left = locations[0];
    int top  =  locations[1];
    //获取PopupWindow的宽高
    mPopupWindow.getContentView().measure(
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
            View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
    int popupWidth = mPopupWindow.getContentView().getMeasuredWidth();
    int popupHeight = mPopupWindow.getContentView().getMeasuredHeight();

    switch (type) {
        case TOP_LEFT:
            mPopupWindow.showAtLocation(v,Gravity.NO_GRAVITY,left - popupWidth + mOffsetX,top - popupHeight + mOffsetY);
            break;
        case TOP_CENTER:
            int offsetX = (v.getWidth() - popupWidth) / 2;
            mPopupWindow.showAtLocation(v,Gravity.NO_GRAVITY,left + offsetX + mOffsetX,top - popupHeight + mOffsetY);
            break;
        case TOP_RIGHT:
            mPopupWindow.showAtLocation(v,Gravity.NO_GRAVITY,left + v.getWidth() + mOffsetX,top - popupHeight + mOffsetY);
            break;

        case BOTTOM_LEFT:
            mPopupWindow.showAsDropDown(v, -popupWidth + mOffsetX,mOffsetY);
            break;
        case BOTTOM_CENTER:
            int offsetX1 = (v.getWidth() - popupWidth) / 2;
            mPopupWindow.showAsDropDown(v,offsetX1 + mOffsetX,mOffsetY);
            break;
        case BOTTOM_RIGHT:
            mPopupWindow.showAsDropDown(v, v.getWidth() + mOffsetX,mOffsetY);
            break;

        case LEFT_TOP:
            mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY, left - popupWidth + mOffsetX, top - popupHeight + mOffsetY);
            break;
        case LEFT_BOTTOM:
            mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY, left - popupWidth + mOffsetX, top + v.getHeight() + mOffsetY);
            break;
        case LEFT_CENTER:
            int offsetY = (v.getHeight() - popupHeight) / 2;
            mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY,left - popupWidth + mOffsetX,top + offsetY + mOffsetY);
            break;

        case RIGHT_TOP:
            mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY, left + v.getWidth() + mOffsetX,top - popupHeight + mOffsetY);
            break;
        case RIGHT_BOTTOM:
            mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY, left + v.getWidth() + mOffsetX,top + v.getHeight() + mOffsetY);
            break;
        case RIGHT_CENTER:
            int offsetY1 = (v.getHeight() - popupHeight) / 2;
            mPopupWindow.showAtLocation(v, Gravity.NO_GRAVITY,left + v.getWidth() + mOffsetX,top + offsetY1 + mOffsetY);
            break;
        case FROM_BOTTOM:
            mPopupWindow.showAtLocation(v,mGravity,mOffsetX,mOffsetY);
            break;
    }
}
使用封装后的PopupWindow

下面是使用封装后的 PopupWindow,只需四行代码就可以显示一个默认的 PopupWindow 了,具体如下:

private void showPopupWindow(MPopupWindow.LocationType type) {
    MPopupWindow popupWindow = new MPopupWindow
            .Builder(this)
            .setLayoutId(R.layout.popup_window_layout)
            .build();
    popupWindow.showPopupWindow(btnTarget, type);
}

由于默认 PopupWindow 背景是透明的,建议测试时设置背景。

显示效果:

下面是 PopupWindow 在各个位置的显示,具体如下:

可以选择关注微信公众号:jzman-blog 获取最新更新,一起交流学习,公众号回复 MPopupWindow 获取源码链接。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/14990.html

相关文章

  • 布局 - 收藏集 - 掘金

    摘要:如可伸缩布局支持集成掘金前言前几天看到官方的博客介绍了开源的一个强大的布局,看见第一眼我心里的想法是,卧槽,居然有这么一个强大的布局。 Android AutoLayout 全新的适配方式 堪称适配终结者 - Android - 掘金转载请标明出处: http://blog.csdn.net/lmj62356...; 本文出自:【张鸿洋的博客】 一、概述 相信Android的开发者对...

    ziwenxie 评论0 收藏0
  • Android组件 - 收藏集 - 掘金

    摘要:手机使用提供侧滑操作的控件掘金这是一款提供侧滑功能的,可以设置它的滑动方向左右上下。预览的属性图解掘金是中最常用的控件之一,而在使用时,必不可少的会使用到它的属性。 收藏安卓开发中非常实用优秀的库! 有图有真相! - Android - 掘金本来是打算收藏工具类的,但转念一想,已经有这么多优秀的库了,就没必要再去重复造轮子了,便归纳工作中比较实用的库吧,不用在花精力去找了 收藏工作中常...

    zero 评论0 收藏0
  • 界面工具 - 收藏集 - 掘金

    摘要:上列表左右滑动开源组件掘金是一款用于为上的排布提供左滑右滑操作的库。这里我贴出最终的兼容方案教你用两层嵌套实现三级展示界面掘金最近项目中使用一个三级展示列表,要求第一级和第二季都可以折叠,并有不同的图标变换,第三层展示数据,可点击。 八年 Android 开发,看我如何简化 Android 的 UI 开发! - Android - 掘金作者 : Super Mary 校对者: Zhao...

    wuaiqiu 评论0 收藏0
  • 代码 - 收藏集 - 掘金

    摘要:行代码的人脸识别实践产品掘金前言很多人都认为人脸识别是一项非常难以实现的工作,看到名字就害怕,然后心怀忐忑到网上一搜,看到网上页的教程立马就放弃了。掘金最新最全的开源项目合集掘金是由整理并维护的安卓相关开源项目库集合。 安卓第三方登录之微信登录(图文详解) - Android - 掘金一、创建应用并成为开发者 1、创建应用 1)到微信开放平台管理中心,点击创建移动应用 ... 三步实现...

    neuSnail 评论0 收藏0
  • Android view - 收藏集 - 掘金

    摘要:掘金一个可实现长按扫描上的二维码的工具库。说到刷新加载,我们第一个想到啥,对了从头开始学四类添加和多种的支持掘金来张图前言敲酷炫的切换效果和弹性指示器。掘金效果图使用方法依赖注意使用时需要在父布局中加入仿讨论组头像掘金仿讨论组头像基 仿美团详情滑动界面,并兼容 NestedScroll 嵌套 - Android - 掘金不论什么APP应该都会有个app产品的详情界面, 详情界面往往也比...

    IntMain 评论0 收藏0

发表评论

0条评论

ixlei

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<