Android7.0 PopupWindow的兼容问题

  Android7.0 中对 PopupWindow 这个常用的控件又做了一些改动,修复了以前遗留的一些问题的同时貌似又引入了一些问题,本文通过在7.0设备上实测并且结合源码分析,带你了解关于 PopupWindow 的相关改动。

  Android7.0 中下面两个问题解决了,这里强调一下,不是说从 Android7.0 开始才解决这两个问题的,因为具体版本细节没去深究。可能在其他的某些版本下面的问题也是被解决了的。

  1. PopupWindow 不响应点击外部消失和返回键消失的解决方法,博文地址:
    http://www.cnblogs.com/popfisher/p/5608717.html
  2. 不得不吐槽的 Android PopupWindow 的几个痛点(实现带箭头的上下文菜单遇到的坑),博文地址:
    http://www.cnblogs.com/popfisher/p/5944054.html

Android7.0 中又引入了新的问题(这就非常的尴尬了)

  1. 调用 update 方法,PopupWindowGravity 会改变

从源码看7.0怎么解决遗留问题的

  解决 PopupWindow 不响应点击外部消失和返回键消失的问题,我们是通过自己设置一个背景。Android7.0 中不设置背景也是可以的,那么它的代码肯定做了处理。从 api24 的源码中找到 PopupWindow.java 文件,我找到里面的 preparePopup 方法如下:

private void preparePopup(WindowManager.LayoutParams p) {
if (mContentView == null || mContext == null || mWindowManager == null) {
throw new IllegalStateException("You must specify a valid content view by "
+ "calling setContentView() before attempting to show the popup.");
} // The old decor view may be transitioning out. Make sure it finishes
// and cleans up before we try to create another one.
if (mDecorView != null) {
mDecorView.cancelTransitions();
} // When a background is available, we embed the content view within
// another view that owns the background drawable.
if (mBackground != null) {
mBackgroundView = createBackgroundView(mContentView);
mBackgroundView.setBackground(mBackground);
} else {
mBackgroundView = mContentView;
} mDecorView = createDecorView(mBackgroundView); // The background owner should be elevated so that it casts a shadow.
mBackgroundView.setElevation(mElevation); // We may wrap that in another view, so we'll need to manually specify
// the surface insets.
p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/); mPopupViewInitialLayoutDirectionInherited =
(mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
}

  重点只需要看 mDecorView = createDecorView(mBackgroundView); 可以看到不管 mBackground 变量是否为空,最终都执行了这句代码,这句代码会多加一层 ViewGroupmBackgroundView 包进去了,里面应该包含了对返回键的处理逻辑,我们再看看 createDecorView 方法源码:

private PopupDecorView createDecorView(View contentView) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
final int height;
if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
height = WRAP_CONTENT;
} else {
height = MATCH_PARENT;
} final PopupDecorView decorView = new PopupDecorView(mContext);
decorView.addView(contentView, MATCH_PARENT, height);
decorView.setClipChildren(false);
decorView.setClipToPadding(false); return decorView;
}

  createDecorView 里面还是没有直接看出对事件的处理,但是里面有个 PopupDecorView 类,应该在里面了吧,继续看:

    private class PopupDecorView extends FrameLayout {
//......有代码被省略 @Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
if (getKeyDispatcherState() == null) {
return super.dispatchKeyEvent(event);
} if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
state.startTracking(event, this);
}
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
final KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null && state.isTracking(event) && !event.isCanceled()) {
dismiss();
return true;
}
}
return super.dispatchKeyEvent(event);
} else {
return super.dispatchKeyEvent(event);
}
} //......有代码被省略 @Override
public boolean onTouchEvent(MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY(); if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
dismiss();
return true;
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
dismiss();
return true;
} else {
return super.onTouchEvent(event);
}
} //......有代码被省略
}

  从上面的代码中我们看到了KeyEvent.KEYCODE_BACKMotionEvent.ACTION_OUTSIDE,没错这里有对返回键和其他事件的处理。

  至于怎么解决 showAsDropDown 方法弹出位置不对的问题,也就是上文中描述的第二个问题,本文就不贴源码了,感兴趣的可以下载源码去看看,本文只是提供一种解决问题的思路,希望大家能从源码中找到解决问题的办法,这才是作者希望达到的效果。 文章末尾会给出 Android7.0 PopupWindow.java 的 java 文件。

Android7.0引入的新问题

  调用 update 方法时,PopupWindowGravity 会改变,导致位置发生了改变,具体看下图:

showAtLocation传入Gravity.Bottom:从屏幕底部对齐弹出

调用update方法更新第5点中弹出PopupWindow,发现PopupWindow的Gravity发生了改变

关于这个问题还有篇文章可以参考, http://www.jianshu.com/p/0df10893bf5b

Android7.0 PopupWindow其他改动点,与Android5.1的对比

主界面

1. PopupWindow高宽都设置为match_parent:7.0(左边)从屏幕左上角弹出,5.1(右边)从anchorView下方弹出

2. 宽度wrap_content-高度match_parent:7.0(左边)从屏幕左上角弹出,5.1(右边)从anchorView下方弹出

3. 宽度match_parent-高度wrap_content:都从anchorView下方弹出

4. 宽度wrap_content-高度大于anchorView到屏幕底部的距离:7.0与5.1都从anchorView上方弹出,与anchorView左对齐

源码地址

Github工程地址,收录了 PopupWindow 相关使用问题:
https://github.com/PopFisher/SmartPopupWindow

Android 7.0 PopupWindow.java 文件:
https://github.com/PopFisher/SmartPopupWindow/blob/master/sourcecode/PopupWindow(7.0).java

总结

  Android PopupWindow 这个控件 Google 一直没有优化好,使用时需要参考我之前的几篇文章。本文是希望读者善于从源码的角度去分析和解决问题,加深自己对源码的理解,对问题的理解,这样印象要深刻一些。

  本来2017年回来还没有时间写写文章,这篇文章也是巧合,同事在 Android7.0 中发现 PopupWindow 使用上有 bug,所以我就借此机会研究一下,虽然知识点简单,但是也花费了几个小时的时间整理出这样一篇文章。如果读者觉得有用,别忘记点击推荐哦,总之也算是开了一个好头吧,以后还是会坚持每个月写些文章出来分享。

Android 7.0 PopupWindow 又引入新的问题,Google工程师也不够仔细么的更多相关文章

  1. Android 7.0 PopupWindow 的兼容问题

    Android7.0 PopupWindow的兼容问题    Android7.0 中对 PopupWindow 这个常用的控件又做了一些改动,修复了以前遗留的一些问题的同时貌似又引入了一些问题,本文 ...

  2. Android 7.0(牛轧糖)新特性

    Android 7.0(牛轧糖)新特性 谷歌正式在I/O大会现场详细介绍了有关Android 7.0的大量信息.目前,我们已经知道,新一代Android操作系统将支持无缝升级,能够通过Vulkan A ...

  3. Android 5.0(棒棒糖))十大新特性

    Android 5.0(棒棒糖))十大新特性 1. 全新Material Design设计风格 Android Lollipop全新的设计语言是受到了多种因素影响,是一种大胆的平面化创新.换句话说,谷 ...

  4. Android 6.0(棉花糖)新特性

    1.支持4K显示 Android 6.0本身已经支持4K显示,会通过一定优化形式使4K内容更加清晰. 2. 启动验证 (更完整的应用权限管理) Android 6.0在开机时会自动运行验证代码,检测设 ...

  5. 关于android studio3.0版本后引入资源问题error: failed linking file resources

    最近更新阿里短视频的SDK,导入demo项目时,发现两个问题: 1.频繁报错AAPT2 error: file not found.以为直接Sync重新编译就可以,但是仍旧频繁提醒引入资源错误. 查找 ...

  6. 在android 6.0(API 23)中,Google已经移除了移除了Apache HttpClient相关的类

    推荐使用HttpUrlConnection,如果要继续使用需要Apache HttpClient,需要在eclipse下libs里添加org.apache.http.legacy.jar,androi ...

  7. Android 8.0 功能和 API

    Android 8.0 为用户和开发者引入多种新功能.本文重点介绍面向开发者的新功能. 用户体验 通知 在 Android 8.0 中,我们已重新设计通知,以便为管理通知行为和设置提供更轻松和更统一的 ...

  8. Android Studio3.0 新特性 ~ New Features in Android Studio Preview (译文)

    原文地址:https://developer.android.google.cn/studio/preview/features/index.html 最新Android Studio版本是Andro ...

  9. Android 5.0新控件——TextInputLayout

    Android 5.0(M)新控件--TextInputLayout 介绍之前,先直观的看一下效果 TextInputLayout其实是一个容器,他继承自LinearLayout,该容器是作用于Tex ...

随机推荐

  1. python - getattr 与 getattribute 机制

    #__getattribute__ class Foo(): def __init__(self,name): self.name = name def __getattr__(self, item) ...

  2. ubuntu 14.04界面美化

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABVYAAAMACAIAAABAXKuVAAAAA3NCSVQICAjb4U/gAAAgAElEQVR4Xu

  3. passwd: Have exhausted maximum number of retries for service【转】

    使用命令passwd修改密码时,遇到如下问题: # echo 'utf8'|passwd zhangsan --stdin Changing password for user zhangsan. p ...

  4. Innodb ,MyISAM

    1. InnoDB不支持FULLTEXT类型的索引. 2. InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算 ...

  5. @RequestBody,@ResponseBody

    @RequestBody 作用: i) 该注解用于读取Request请求的body部分数据,使用系统默认配置的HttpMessageConverter进行解析,然后把相应的数据绑定到要返回的对象上: ...

  6. oracle 回退表空间清理

    1.查看已有表空间,找到回退表空间 SELECT * FROM DBA_TABLESPACES WHERE CONTENTS='UNDO' 2.创建新的回退表空间 create undo tables ...

  7. 一步步实现windows版ijkplayer系列文章之四——windows下编译ijkplyer版ffmpeg

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  8. Log4Net 无法写入到SqlServer

    直接进入正题: 今天在测试使用Log4Net写入到数据库的时候,发现一直无法写入到数据库中,而且程式也没有报任何错误. 配置信息如下: <appender name="AdoNetAp ...

  9. 关于ftp上传changeWorkingDirectory()方法的路径切换问题

    在上传时 FTPClient提供了upload方法,对于upload(file,path)的第二个参数path ,上传到哪里的这个路径, ftp是利用changeWorkingDirectory()方 ...

  10. poj2155二维树状数组区间更新

    垃圾poj又交不上题了,也不知道自己写的对不对 /* 给定一个矩阵,初始化为0:两种操作 第一种把一块子矩阵里的值翻转:0->1,1->0 第二种询问某个单元的值 直接累计单元格被覆盖的次 ...