1、控件介绍
我们有时会有这样的需求,点击界面上的某个界面元素,会在该元素的旁边弹出一个浮层,这个浮层用于显示对该元素的消息描述。
2、控件使用
2.3、浮层展示
要展示浮层我们需要用到PopupWindow控件,这个控件在Android API level 1 就引入了,所以可以直接使用。
一个简单的使用示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public static PopupWindow showPopupWindow(final View anchorView, View contentView) { contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); // 创建PopupWindow时候指定高宽时showAsDropDown能够自适应 // 如果设置为wrap_content,showAsDropDown会认为下面空间一直很充足(我以认为这个Google的bug) // 备注如果PopupWindow里面有ListView,ScrollView时,一定要动态设置PopupWindow的大小 final PopupWindow popupWindow = new PopupWindow(contentView, contentView.getMeasuredWidth(), contentView.getMeasuredHeight(), false); // 如果不设置PopupWindow的背景,有些版本就会出现一个问题:无论是点击外部区域还是Back键都无法dismiss弹框 popupWindow.setBackgroundDrawable(new ColorDrawable()); // setOutsideTouchable设置生效的前提是setTouchable(true)和setFocusable(false) popupWindow.setOutsideTouchable(true); // 设置为true之后,PopupWindow内容区域 才可以响应点击事件 popupWindow.setTouchable(true); // true时,点击返回键先消失 PopupWindow // 但是设置为true时setOutsideTouchable,setTouchable方法就失效了(点击外部不消失,内容区域也不响应事件) // false时PopupWindow不处理返回键 popupWindow.setFocusable(false); popupWindow.setTouchInterceptor(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; // 这里面拦截不到返回键 } }); // 如果希望showAsDropDown方法能够在下面空间不足时自动在anchorView的上面弹出 // 必须在创建PopupWindow的时候指定高度,不能用wrap_content int[] windowPos = calculatePopWindowPos(anchorView, contentView); popupWindow.showAtLocation(anchorView, Gravity.TOP | Gravity.START, windowPos[0], windowPos[1]); return popupWindow; } |
2.2、控件事件
如果想要实现这样的交互,例如触摸某个界面元素弹出浮层,松开时隐藏浮层。我们可以监听该元素的onTouch事件,并拦截ACTION_DOWN和ACTION_MOVE,这样该元素可以监听ACTION_UP事件,然后在该事件中实现浮层的dismiss,代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@Override public boolean onTouch(View view, MotionEvent motionEvent) { switch(motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: mPopupWindow = showPopupWindow(anchorView, popupContentView); return true; case MotionEvent.ACTION_MOVE: return true; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if(mPopupWindow != null) { mPopupWindow.dismiss(); } return false; } return false; } |
2.1、控件展示位置
想要控制浮层的展示位置,我们需要使用到showAtLocation方法,该方法包含四个参数:
其中parent非常重要,它所在的Window的坐标就是浮层的起始坐标(0,0)。如果你是在Activity里面显示浮层,那么这个浮层起始坐标就是屏幕的(0,0)位置;如果parent是在Dialog对话框中,那么浮层的其实位置是dialog所在window的左上角(0,0)位置。也就是说showAtLocation的参数x和y是相对偏移量,是有可能为负值的。
2.1.1、X,Y 坐标
下面有两张图来描述浮层的位置计算方法:
1、Activity里面元素(即parent在Activity里面)的浮层
2、Dialog或Window里面元素(即parent在Dialog或window里面)的浮层
注意因为parent元素在对话框里面,所以此时的(0,0)坐标在对话框的左上角,而非屏幕位置的左上角,
获取x,y偏移量的代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
private static int[] calculatePopWindowPos(final View anchorView, final View contentView) { final int windowPos[] = new int[2]; final int anchorLocOnScreen[] = new int[2]; final int anchorLocInWindow[] = new int[2]; // 获取锚点View在屏幕上的左上角坐标位置 anchorView.getLocationOnScreen(anchorLocOnScreen); anchorView.getLocationInWindow(anchorLocInWindow); final int anchorHeight = anchorView.getHeight(); final int anchorWidth = anchorView.getWidth(); // 获取window的高宽 contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); // 计算contentView的高宽 final int contentHeight = contentView.getMeasuredHeight(); final int contentWidth = contentView.getMeasuredWidth(); // 判断需要向上弹出还是向下弹出显示 final boolean isNeedShowUp = anchorLocOnScreen[1] > contentHeight; int halfAnchorWidth = anchorWidth >> 1; int halfWindowWidth = contentWidth >> 1; windowPos[0] = Math.max(anchorLocInWindow[0] + halfAnchorWidth - halfWindowWidth, 0); if (isNeedShowUp) { windowPos[1] = anchorLocInWindow[1] - contentHeight; } else { windowPos[1] = anchorLocInWindow[1] + anchorHeight; } return windowPos; } |
2.1.2、Gravity使用
如果你不需要精确的计算X, Y 位置,你可以用gravity来实现居中,靠左等效果。你也可以结合X,Y 坐标一起使用。下面以X,Y 坐标为(0,0)为例来看Gravity对popupwindow位置的影响:
代码示例:
1 |
popupWindow.showAtLocation(anyView, Gravity.CENTER, 0, 0); |
2.1.2.1、居中( Gravity.CENTER )
x,y坐标设置为(0,0), Gravity设置为Center
2.1.2.2、靠边展示
Gravity.TOP
Gravity.BOTTOM
Gravity.LEFT
Gravity.RIGHT
x,y 设置为(0,0),Gravity分别设置为上面列出的4个值,展示效果如下:
Top和Bottom 默认是水平居中的,如果你想要让popwindow在左上方,可以用多个gravity组合来实现,具体如下:
2.1.2.3、Gravity组合展示
Gravity.TOP | Gravity.LEFT
Gravity.TOP | Gravity.RIGHT
Gravity.BOTTOM | Gravity.LEFT
Gravity.BOTTOM | Gravity.RIGHT
x,y 设置为(0,0),Gravity分别设置为上面列出的4个组合值,展示效果如下:
2.1.2.4、x,y坐标和Gravity结合
你可以在调用showAtLocation方法时传入x,y坐标来设置偏移量,代码示例:
1 |
popupWindow.showAtLocation(anyView, gravity, x, y); |
结合Gravity的使用示例如下:
1 |
popupWindow.showAtLocation(anyView, Gravity.BOTTOM, 0, 300); |
我们可以看出这个popupwindow在屏幕下方,并举例屏幕底部300px,同时它是水平居中的。我们知道gravity设置为Bottom时,默认是在屏幕下方且水平居中的,此时设置y的值为300px,那么这个y的值是相对屏幕左下角往上方向偏移了300px。所以x,y偏移的方向是和gravity相关联的。