关于写这篇文章的目的是因为项目中要写一个PopupWindow的弹出框动画,结果碰到各种坑,各种百度,但是说的要么支离破碎,要么根本就是错的,所以再次整合了一下,并力所能及的解决了一些问题。
大部分人都碰到了PopupWindow点击外部不会消失或者点击没反应等等的情况,然而百度上解决的方法就那么几种可能你写完之后发现还是没什么用,其实并不是因为他们说错了,而是你创建pop时的设置和他们根本不同导致的问题,下面我详细的说明下各种创建方法会导致的问题已经解决方法。
其实大部分能不能关闭,能不能点击都是setFocusable是true还是false导致的,那么我将对这2种状态进行分析。
在mPopupWindow.setFocusable(true)时的情况
第一种情况:
new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
如果这里的高度是WRAP_CONTENT
(xml中的布局也到不满屏的地步)
1.1那么点击PopupWindow外部 PopupWindow是会自动dismiss的并且activity的onTouchEvent和dispatchTouchEvent都不会触发(他会优先触发dismiss) 换句话说你想要在PopupWindow显示的情况下点击外部的一些控件就很难了,(解决方法下面再说)
1.2点击返回键PopupWindow也是会自动dismiss的 并且activity中的onKeyDown(int keyCode, KeyEvent event)也无法触发 换句话说你想要在PopupWindow显示的情况下控制返回键也已经不可能了
1.3点击PopupWindow中的EditText可以弹出键盘
1.4在安卓7.0以上的手机上这个显示也是正确的(会从指定的控件下方弹出)
第二种情况:
new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
如果这里的高度是MATCH_PARENT
2.1那么PopupWindow自然是不存在什么外部空闲区域了 1.1的情况也就不存在了其他都一样 想让它dismiss 只能按返回键或者自己调用他的dismiss方法。
PS:在这里声明一下 网上说的那些
1.PopupWindow.showAtLocation(mThreeTest, Gravity.TOP, 0, 500) MATCH_PARENT 这里的500并没有什么用 顶部的500依然算内部
2.PopupWindow.showAsDropDown(mThreeTest, 0, 0) 这里说明下 如果用了这个那mThreeTest的控件已经算外部了
3.PopupWindow.setBackgroundDrawable(new BitmapDrawable());
4.contentView.setFocusable(true); contentView.setFocusableInTouchMode(true);
这都亲自在各种情况下一一试过 反正得到的结果都是一样的 只要setFocusable(true)都是以上写的几个结论。
在mPopupWindow.setFocusable(false)时的情况
第一种情况:
new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
1.1那么点击PopupWindow外部 PopupWindow并不会dismiss activity的onTouchEvent和dispatchTouchEvent也都能接受 总之PopupWindow以外的控件点击都是有效的
1.2点击返回键的话和没有PopupWindow是一样的 原本是什么逻辑就是什么逻辑 而且activity中的onKeyDown(int keyCode, KeyEvent event)也是能监听到的
1.3点击PopupWindow中的EditText不可以弹出键盘 (点击textView等控件是有反应的只是EditText的键盘不弹而已并不是内部控件点击无效)
1.4在安卓7.0版本以上显示的位置不正确,总会在屏幕最上方显示(解决方法下面说明)
第二种情况:
new PopupWindow(contentView, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
如果这里的高度是MATCH_PARENT
2.1PopupWindow也不存在什么外部空闲区域了 点击类似于外部的控件也是无任何反应的
这个的2.1我在说一遍 如果PopupWindow.showAsDropDown(mThreeTest, 0, 0) 那么即使是MATCH_PARENT mThreeTest依然算外部 既然是外部当然是有反应的
以上就是PopupWindow在各种设置下会出现的情况 那么上面还留了几个问题 在回答这些问题前我们先总结下
如果想PopupWindow 实现点击外部就消失 就把setFocusable(true)就可以了 缺点:当然在显示时 返回键也就必须是消失它了
如果想PopupWindow 显示的情况下还能点击外部的控件 那么setFocusable(fasle)就可以了 缺点:点击PopupWindow中的editView 键盘不会弹出了 并且在7.0以上的系统显示位置会错误
先来说说在安卓7.0以上setFocusable(fasle)位置显示不正确的问题。
第一种解决方案:将setFocusable(true) (相当于说了废话)
第二种解决方案:将showAsDropDown重写(调用自己重写的showAsDropDown, 将PopupWindow 当参数传进去
public void showAsDropDown(PopupWindow pw, View anchor, int xoff, int yoff) {
if (Build.VERSION.SDK_INT >= 24) {
Rect rect = new Rect();
anchor.getGlobalVisibleRect(rect);
int h = anchor.getResources().getDisplayMetrics().heightPixels - rect.bottom;
pw.setHeight(h);
pw.showAsDropDown(anchor, xoff, yoff);
} else {
pw.showAsDropDown(anchor, xoff, yoff);
}
}
还有一个问题 我
既想要点击外外部能让外部控件有反应 又要让内部的editView弹框呢(其实就是上面遗留的问题)
先说说在 setFocusable(fasle)的情况下 在这种情况下高度写成 WRAP_CONTENT外部自然是不需要处理的 那么就剩下如何让键盘弹出了
对pop内部的editView实现监听事件 使他强制弹框
popEdit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//强制弹出软键盘
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
}
});
然而这样还不够 键盘是弹出来了 可是被pop挡住了 需要在加上
mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
mPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
但是还是有个问题 就是第一次点击editView并不会弹出键盘 第二次才会弹出(如果你activity的xml布局层级很深 可能需要点击很多次才会弹出 即使拿最外层的id去让它弹出 依然需要2次) 至于这个问题 我并没有什么好的方法可以解决 如果知道怎么解决的大佬请务必告诉我
由于setFocusable(fasle)的请求下并不能很好的解决 那么只剩下setFocusable(true)的情况了
setFocusable(true)时 高度写成 WRAP_CONTENT 那么内部的editView本身就是可以点击弹软件盘的 那么只剩下解决 点击外部控件不消失pop并且能响应对应控件的问题了
利用pop的触摸监听来拦截
mPopupWindow.setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int height = v.getHeight();
float touchY = event.getY();
if (touchY > 0 && touchY < height) {
return false; //点击pop内部时 不进行拦截
}else {
MorePopOutSideTouchable(v, event); //自己写方法进行处理
return true; //点击pop外部是 进行拦截 不让事件传递下去
}
}
});
这里没什么特别好的方法 虽然解决了外部点击pop会消失 已经其他控件不响应的问题 但是在外部的区域 所有的控件 想要响应对应的点击事件
只能利用点击的X和Y的坐标来和控件的X Y坐标进行对比来实现对应控件的点击事件
private void MorePopOutSideTouchable(View v, MotionEvent event) {
float x = event.getX();
float y = event.getY();
}
最后我发现有些安卓的全面屏手机(系统在7.0以上)布局的需求是下方有阴影(参考某宝的 “我的”-->“账单”-->“筛选”或者“分类”的弹框)如果用setFocusable(fasle) 然后重写showAsDropDown使其弹框位置正确 也会出现最下方不会被阴影覆盖的情况(某宝暂时也没解决) 那么这个时候用setFocusable(true) 就可以解决了至于外部点击无效什么的 上面也说了解决方法。
到此关于PopupWindow的说明就到此为止了,写文不易,如需转发请标注出处,谢谢大家了!!