弹窗

出现位置飘忽不定的弹窗在Android应用并不少见, 比如这种:

示例1

还有这种:

示例2

这些窗口的特点是位置不固定, 神出鬼没. 这种情况下, 我们可以使用PopupWindow来实现.

示例1

我们可以尝试着使用PopupWindow来实现图1的效果. 整个页面的载体是一个ListView, 每一个条目都有一个分享按钮, 点击分享按钮需要在按钮的上方显示一个悬浮窗. 难点就是悬浮窗显示的位置. 其实使用PopupWindow很容易实现, 毕竟PopupWindow就是为这种情况而设计的, 直接上代码:

布局就不贴了, 就是一个.9的背景图片加上几个TextView. 加上注释应该很容易理解上面的代码. 我们可以通过setAnimationStyle来为PopupWindow添加动画, setAnimationStyle需要一个style id, style中注明显示与隐藏的动画资源id:

使用方法:

PopupWindow最核心的方法就是showAtLocation, showAsDropDown以及他们的各种重载方法:

  • showAtLocation(View parent, int gravity, int x, int y) 显示在屏幕中指定的x,y的位置

  • showAtLocation(IBinder token, int gravity, int x, int y) 显示在屏幕中指定的x,y的位置

  • showAsDropDown(View anchor) 显示在指定view的下方

  • showAsDropDown(View anchor, int xoff, int yoff) 显示在指定view的下方, 并在x轴偏移x, y轴偏移y

  • showAsDropDown(View anchor, int xoff, int yoff, int gravity) 显示在指定view的下方, 并在x轴偏移x, y轴偏移y

示例二

看了示例一再来看示例二会觉得非常简单. 无非就是一个输入框加一个按钮. 然后有n中方法可以让弹窗显示在屏幕底部. 我使用的则是showAtLocation(view, Gravity.BOTTOM, 0, 0), 并且PopupWindow的宽高均是MATCH_PARENT. 布局则是有一个半透明的灰色的MATCH_PARENT的LinearLayout, 输入框和按钮都显示在LinearLayout的底部. 为什么这么做呢? 因为评论框弹出的时候背景要变灰, 所以使用了这种取巧的方式.

但是, ui效果虽然简单, 想要把评论框的用户体验做好, 不是那么简单:

  1. 首先, 想要用户体验好就需要在弹窗弹出的同时, 打开软键盘, 参考微信的朋友圈评论.

  2. 其次, 弹窗在显示状态下, 按返回键, 或者触摸输入框其他空白位置的时候, 不仅要隐藏弹窗, 还需要收回软键盘

  3. 默认情况下, 软键盘会遮盖一部分的输入框, 不同手机可能有不同的效果.

如何解决上面三个问题, 才是示例二的难点所在. 接下来一个个的看:

自动弹出软键盘

这个问题应该是最简单的了, 直接在show方法中使用IMEUtil来打开软键盘即可.

自动隐藏软键盘

首先, 触摸输入框其他空白位置的时候收回软键盘同时隐藏弹窗, 这个问题也不难, 同样借助IMEUtil:

我们只需要在整个布局上设置OnClickListener即可. 但是, 点击返回键的时候需要收回键盘并且dismiss就不那么容易了. 看到这个问题我们第一反应就是监听返回键. 监听返回键只能在Activity里去做, PopupWindow本身不提供此API. 监听返回键之后, 需要判断PopupWindow是否是显示状态, 如果是, 则隐藏, 不是, 则要将事件传递出去. 这就意味这Activity要持有PopupWindow. 如果PopupWindow本身就在Activity中, 这么做没问题. 但是, 如果PopupWindow是在Fragment中, 这样做就不太好了. 虽然有很多方法可以将PopupWindow传递给持有Fragment的Activity, 但是不建议这么做, 类不要持有与其无直接关联的引用.

还有第二种方法, 自定义EditText. EditText中有一个可被覆写的方法dispatchKeyEventPreIme. 看看此方法的注释:

大意就是, 由于有软键盘存在的情况下, 按返回键会默认先收回软键盘, 此方法可以拦截按返回键收回软键盘这种事件. 代码如下:

代码很简单, 首先如果监听到返回键, 会去收回软键盘, 然后借助RxBus发送一个不带数据的事件. 在PopupWindow监听此事件, 一旦接受到, 就dismiss自己. 达到点击返回键的时候需要收回键盘并且dismiss的效果.

软键盘覆盖部分输入框

在某些机型上, 默认情况下软键盘覆盖弹窗的部分输入框. 要解决这个问题首先想到的就是Activity的windowSoftInputMode属性. 但是每种属性都试过了还是有这种情况. 面向百度或谷歌也没有发现是什么问题. 后来无意中发现在PopupWindow中一个方法: setSoftInputMode. 这个方法是不是跟Activity的windowSoftInputMode类似? 试验发现, 如果这么写, 问题就解决了:

至此, 三个问题就全部解决.

Last updated

Was this helpful?