专栏首页Android开发与分享【Android】在任意位置弹出PopupWindow

【Android】在任意位置弹出PopupWindow

前言

在日常的开发中,经常会有弹框的操作。实现弹框有两种选,PopupWindow或者Dialog,这里就先忽略Dialog。弹框可能会在各种位置出现,在指定View的上、下、左、右、左对齐、右对齐等... 而PopupWindow似乎就提供了showAsDropDown方法(请忽略showAtLocation,这边说的是相对于View显示),这~~就有点尴尬了。

平时我们可能是这样用PopupWindow的:

  • 创建一个布局,再创建一个类继承PopupWindow
public class TestPopupWindow extends PopupWindow {

    public TestPopupWindow(Context context) {
        super(context);
        setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
        setOutsideTouchable(true);
        setFocusable(true);
        setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        View contentView = LayoutInflater.from(context).inflate(R.layout.popup_test, 
              null, false);
        setContentView(contentView);
    }
}
  • 然后直接使用它
TestPopupWindow mWindow = new TestPopupWindow(this);
//根据指定View定位
PopupWindowCompat.showAsDropDown(mWindow, mButtom, 0, 0, Gravity.START);
//或者
mWindow.showAsDropDown(...);
//又或者使用showAtLocation根据屏幕来定位
mWindow.showAtLocation(...);

Gravity.LEFT(Gravity.START):相对于View左对齐; Gravity.RIGHT(Gravity.END):相对于View靠右显示。

Gravity.CENTER:在showAsDropDown()中是跟 Gravity.LEFT一样,在showAtLocation()中Gravity.CENTER才有效果

  • 得到效果

left.gif

查了下showAsDropDown(),发现只能在指定控件的下面弹出,总感觉少了点什么~~ 有时候我想弹在View的上面、左边、右边?怎么解?

可能有机智的boy已经想到了showAsDropDown()中的另外两个参数,xoffyoff

要利用这两个参数,不过不建议在代码中直接写。为什么? 如果你的PopupWindow宽高不确定,这两个参数你也不知道该写多少。 什么!你的PopupWindow宽高都写死了?骚年,你还是太年轻了。当你宽高确定的时候,如果PopupWindow中有文本,用户把字体改得超大,你的字就没掉一块了 什么!你还是嘴硬非要把宽高写死?好吧,你赢了。

各种位置的弹窗

下面就来利用xoffyoff在你想要的任何位置弹框。

准备工作

弹框前,需要得到PopupWindow的大小(也就是PopupWindowcontentView的大小)。 由于contentView还未绘制,这时候的widthheight都是0。因此需要通过measure测量出contentView的大小,才能进行计算。需要如下方法:

    @SuppressWarnings("ResourceType")
    private static int makeDropDownMeasureSpec(int measureSpec) {
        int mode;
        if (measureSpec == ViewGroup.LayoutParams.WRAP_CONTENT) {
            mode = View.MeasureSpec.UNSPECIFIED;
        } else {
            mode = View.MeasureSpec.EXACTLY;
        }
        return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec), mode);
    }

测量contentView的大小

TestPopupWindow window = new TestPopupWindow(this);
View contentView = window.getContentView();
//需要先测量,PopupWindow还未弹出时,宽高为0
contentView.measure(makeDropDownMeasureSpec(window.getWidth()), 
      makeDropDownMeasureSpec(window.getHeight()));

弹框

测量好PopupWindow大小后,就在任意位置弹窗了 弹框的位置无非就是根据PopupWindow以及指定View的大小,计算水平、竖直方向偏移。

水平:居左;竖直:居下

计算偏移:

水平:居左;竖直:居下

代码、效果:

int offsetX = -window.getContentView().getMeasuredWidth();
int offsetY = 0;
PopupWindowCompat.showAsDropDown(window, mButton, offsetX, offsetY, Gravity.START);

效果(水平:居左;竖直:居下)

水平:居中;竖直:居下

计算偏移:

水平:居中;竖直:居下

代码、效果:

offsetX = Math.abs(mWindow.getContentView().getMeasuredWidth()-mButton.getWidth()) / 2;
offsetY = 0;
PopupWindowCompat.showAsDropDown(window, mButton, offsetX, offsetY, Gravity.START);

效果(水平:居中;竖直:居下)

水平:右对齐;竖直:居下

计算偏移:

水平:右对齐;竖直:居下

代码、效果:

offsetX = mButton.getWidth() - mWindow.getContentView().getMeasuredWidth();
offsetY = 0;
PopupWindowCompat.showAsDropDown(window, mButton, offsetX, offsetY, Gravity.START);

效果(水平:右对齐;竖直:居下)

水平:居中;竖直:居上

计算偏移:

水平:居中;竖直:居上

代码、效果:

offsetX = Math.abs(mWindow.getContentView().getMeasuredWidth()-mButton.getWidth()) / 2;
offsetY = -(mWindow.getContentView().getMeasuredHeight()+mButton.getHeight());
PopupWindowCompat.showAsDropDown(window, mButton, offsetX, offsetY, Gravity.START);

效果(水平:居中;竖直:居上)

水平:居左;竖直:居中

计算偏移:

水平:居左;竖直:居中

代码、效果:

offsetX = -mWindow.getContentView().getMeasuredWidth();
offsetY = -(mWindow.getContentView().getMeasuredHeight() + mButton.getHeight()) / 2;
PopupWindowCompat.showAsDropDown(window, mButton, offsetX, offsetY, Gravity.START);

效果(水平:居左;竖直:居中)

水平:居右;竖直:居中

计算偏移:

水平:居右;竖直:居中

代码、效果:

offsetX = 0;
offsetY = -(mWindow.getContentView().getMeasuredHeight() + mButton.getHeight()) / 2;
PopupWindowCompat.showAsDropDown(window, mButton, offsetX, offsetY, Gravity.END);

center-right.gif

画这些图比敲代码还累~~~ 基本上完成了所有位置的弹框。还有一些位置上面没提到,不过通过上面那些水平、竖直的偏移也能拼凑出来。

背景变暗

说完位置方案,顺便提下背景色的变化方案。 通改变Window的透明度来实现背景变暗是常用的一种做法。 在PopupWindow中,先写个修改Window透明度的方法(注意,这边的mContext必须是Activity

    /**
     * 控制窗口背景的不透明度
     */
    private void setWindowBackgroundAlpha(float alpha) {
        if (mContext == null) return;
        if (mContext instanceof Activity) {
            Window window = ((Activity) mContext).getWindow();
            WindowManager.LayoutParams layoutParams = window.getAttributes();
            layoutParams.alpha = alpha;
            window.setAttributes(layoutParams);
        }
    }

然后定义透明度

private float mAlpha = 1f; //背景灰度  0-1  1表示全透明

最后在PopupWindow show的时候调用以下方法。

    /**
     * 窗口显示,窗口背景透明度渐变动画
     */
    private void showBackgroundAnimator() {
        if (mAlpha >= 1f) return;
        ValueAnimator animator = ValueAnimator.ofFloat(1.0f, mAlpha);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float alpha = (float) animation.getAnimatedValue();
                setWindowBackgroundAlpha(alpha);
            }
        });
        animator.setDuration(360);
        animator.start();
    }

通过动画来改变Window达到渐变的效果。

已有的库

这么麻烦的弹框,当然有人已经为我们封装好了

  • RelativePopupWindow:代码简洁,支持各种位置的弹框。还能超出屏幕(感觉用不上)。 用起来是这样的:
popup.showOnAnchor(anchor, VerticalPosition.ABOVE, HorizontalPosition.CENTER, false);
  • EasyPopup:一个功能比较全的库,支持背景变暗,背景不可点击(6.0以上通用)等...而且可以链式调用哦。不过有个缺点:背景变暗效果只支持 4.2 以上的版本。 用起来是这样的:
private EasyPopup mCirclePop;
circlePop = new EasyPopup(this)
        .setContentView(R.layout.layout_circle_comment)
        .setAnimationStyle(R.style.CirclePopAnim)
    //是否允许点击PopupWindow之外的地方消失
        .setFocusAndOutsideEnable(true)
        .createPopup();
//显示
circlePop.showAtAnchorView(view, VerticalGravity.CENTER, HorizontalGravity.LEFT, 0, 0);

这两个库的跟我上面的思路基本一样,然后通过水平、竖直参数来使用。 用的话,如果最小版本大于等于18的话,直接用- EasyPopup就可以了。(毕竟是国人写的,有中文文档~~)

自己写个工具类

介于EasyPopup只适配4.2 以上的版本,而项目要适配到4.1。只好自己写一个了~~ 结合上面的提到的两个库,以及背景变暗的方案。改造一个自己的库。具体的实现代码就不贴出来了,用法也借鉴了上面的两个库。

 SmartPopupWindow popupWindow= SmartPopupWindow.Builder
     .build(Activity.this, view)
     .setAlpha(0.4f)                   //背景灰度     默认全透明
     .setOutsideTouchDismiss(false)    //点击外部消失  默认true(消失)
     .createPopupWindow();             //创建PopupWindow
 popupWindow.showAtAnchorView(view, VerticalPosition.ABOVE, HorizontalPosition.CENTER);

水平方向参数HorizontalPositionLEFT 、 RIGHT 、 ALIGN_LEFT 、 ALIGN_RIGHT、 CENTER 竖直方向参数VerticalPositionABOVE 、 BELOW、 ALIGN_TOP 、 ALIGN_BOTTOM、 CENTER

项目地址SmartPopupWindow

功能很简单,由于是通过别人的库改造的,就不上传JCenter了。 里面就三个文件,想用的话去拷下来就可以了。

参考

RelativePopupWindow EasyPopup Android弹窗_PopupWindow详解 (挺详细的)

以上有错误之处,感谢指出

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • PopupWindow自定义位置显示的实现代码

    在Android中弹出式菜单(以下称弹窗)是使用十分广泛的一种菜单呈现方式,弹窗为用户交互提供了便利。关于弹窗的实现大致有以下两种方式AlertDialog和P...

    砸漏
  • PopupWindow

    A popup window that can be used to display an arbitrary view. The popup window i...

    小小工匠
  • Android弹窗二则: PopupWindow和AlertDialog

    android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app....

    sean_yang
  • Android开发笔记(六十五)多样的菜单

    Android的菜单分为两类:选项菜单和上下文菜单,默认使用选项菜单。菜单的布局文件存放在res/menu目录下,使用ADT新建一个Android工程,首页...

    用户4464237
  • Android PopupWindow怎么合理控制弹出位置(showAtLocation)

    说到PopupWindow,应该都会有种熟悉的感觉,使用起来也很简单 // 一个自定义的布局,作为显示的内容 Context context = null;  ...

    用户1155943
  • Android PopupMenu and PopupWindow

    本文通过一个实例简单介绍下PopupMenu和PopupWindow的区别和各自使用方式。

    宅男潇涧
  • Android编程实现popupwindow定时消失的方法

    本文实例讲述了Android编程实现popupwindow定时消失的方法。分享给大家供大家参考,具体如下:

    砸漏
  • Android从屏幕底部弹出popupWindow

    先看一下效果,看看是不是你想要的效果,免得浪费大家的时间,有一点说明,由于我录制的 gif 是用的模拟器,所以没有屏幕变暗的效果和加速的弹起的效果,实际效果以真...

    Vance大飞
  • Android 7.0 PopupWindow 又引入新的问题,Google工程师也不够仔细么

    Android7.0 PopupWindow的兼容问题 Android7.0 中对 PopupWindow 这个常用的控件又做了一些改动,修复了以前遗留的一些...

    用户1155943
  • 详解Android PopupWindow怎么合理控制弹出位置(showAtLocation)

    如果创建PopupWindow的时候没有指定高宽,那么showAsDropDown默认只会向下弹出显示,这种情况有个最明显的缺点就是:弹窗口可能被屏幕截断,显示...

    砸漏
  • Android开发之PopupWindow创建弹窗、对话框的方法详解

    本文实例讲述了Android开发之PopupWindow创建弹窗、对话框的方法。分享给大家供大家参考,具体如下:

    砸漏
  • 4-VIV-Android之PopupWindow

    张风捷特烈
  • 高仿微信聊天界面长按弹框样式

    为了说明该PopupWindow适应各个位置,所以效果图里面有三个按钮,分别位于左下角,中间,右上角。

    AndroidTraveler
  • 详解Android中PopupWindow在7.0后适配的解决

    本文介绍了详解Android中PopupWindow在7.0后适配的解决,分享给大家,具体如下:

    砸漏
  • Android 开发使用PopupWindow实现弹出警告框的复用类示例

    本文实例讲述了Android 开发使用PopupWindow实现弹出警告框的复用类。分享给大家供大家参考,具体如下:

    砸漏
  • Dialog和PopupWindow的区别

    它们都可以实现弹窗功能,但是他们之间有一些差别,下面我们先对比Dialog和PopuWindow区别。

    剑行者
  • Android Popupwindow弹出窗口的简单使用方法

    本文实例为大家分享了Android Popupwindow弹出窗口的具体代码,供大家参考,具体内容如下

    砸漏
  • Android沉浸式状态栏与Dialog/PopupWindow兼容

    在使用某APP的过程中发现,该APP对PopupWindow的沉浸式支持并不好,在使用PopupWindow的时候,状态栏会变成纯白色,看不到时间信号等等。如下...

    None_Ling
  • 让你一目了然!—PopupWindow

    我们的基础控件之路马上就要共同完成啦,相信跟着我们坚持学习的你一定收获颇丰,那么我们本篇继续介绍 Android的基础控件 PopupWindow这个控件,花粉...

    下码看花

扫码关注云+社区

领取腾讯云代金券