Android的PopupWindow的使用,根据点击位置显示弹窗
在使用PopupWindow弹窗时遇见的一个问题,我这边列表中长按Item时需要显示一个弹窗,但是因为item的高度太大,导致弹窗出现的位置可能距离用户点击的坐标较大,所以需要对PopupWindow弹窗的位置做一些偏移,类似微信的聊天列表长按时出现的弹窗。解决方法来自于: http://www.cnblogs.com/popfisher/p/5608436.html.
基本使用
首先是初始化PopupWindow设置一些参数:
/**
* 创建popupWindow弹窗
*
* @param context
* @param anchor 用于弹出PopupWindow的View
* @param x 点击坐标到屏幕左边的距离
* @param y 点击坐标到屏幕上边的距离
* @param listener popupWindow中的点击事件接口
*/
public PopupWindow createPopupWindow(Context context, View anchor, int x, int y, OnPopupClickListener listener) {
// 自定义的布局View
View view = LayoutInflater.from(context)
.inflate(R.layout.popup_item_active, null, false);
view.findViewById(R.id.tv_update).setOnClickListener(v -> listener.onPopupClick(v.getId()));
view.findViewById(R.id.tv_delete).setOnClickListener(v -> listener.onPopupClick(v.getId()));
PopupWindow popupWindow = new PopupWindow();
popupWindow.setContentView(view);
popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setWidth(dip2px(context, 160));
popupWindow.setBackgroundDrawable(new ColorDrawable()); // 需要设置一个背景setOutsideTouchable(true)才会生效
popupWindow.setFocusable(true); // 防止点击事件穿透
popupWindow.setOutsideTouchable(true); // 设置点击外部时取消
int windowPos[] = PopupUtils.calculatePopWindowPos(anchor, view, x, y);
int[] location = new int[2];
view.getLocationOnScreen(location);
popupWindow.showAtLocation(anchor, Gravity.TOP | Gravity.START, windowPos[0], windowPos[1]);
return popupWindow;
}
int x;
int y;
/**
* 获取点击坐标的方法
*
* @param view 点击的View
*/
public void clickXY(View view) {
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
x = (int) event.getRawX();
y = (int) event.getRawY();
return false;
}
});
}
上面的参数可以根据自己的需求添加,需要注意的是setFocusable(true)可以用来获取焦点,防止点击事件传递给后面的控件,这样在点击外部时则会取消弹窗,而不是响应后面按钮的点击事件。下面的方法是用来获取点击坐标的xy,在初始化点击View的地方添加即可。
设置PopupWindow弹出位置
显示Popup有两种方式:
1.依附于指定的View:showAsDropDown( )
2.相对父控件的做偏差显示:showAtLocation( )
这次使用的showAtLocation()方法。
/**
* 计算popupWindow在长按view 的什么位置显示
*
* @param anchorView 长按锚点的view
* @param contentView 弹出框的布局View
* @param touchX 锚点距离屏幕左边的距离
* @param touchY 锚点距离屏幕上方的距离
* @return popupWindow在长按view中的xy轴的偏移量
*/
public static int[] calculatePopWindowPos(final View anchorView, final View contentView,
int touchX, int touchY) {
final int windowLoc[] = new int[2];
int offset = 144;
// 获取屏幕的高宽
final int screenHeight = getScreenHeight(anchorView.getContext());
final int screenWidth = getScreenWidth(anchorView.getContext());
// 测量弹出框View的宽高
contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
final int popHeight = contentView.getMeasuredHeight();
final int popWidth = contentView.getMeasuredWidth();
// 判断需要向上弹出还是向下弹出显示
// 屏幕高度-触点距离左上角的高度 < popupWindow的高度
// 如果小于弹出框的高度那么说明下方空间不够显示 popupWindow,需要放在触点的上方显示
final boolean isNeedShowTop = (popHeight + touchY > screenHeight);
// 判断需要向右边弹出还是向左边弹出显示
//判断触点右边的剩余空间是否够显示popupWindow 大于就说明够显示
final boolean isNeedShowRight = (touchX < (screenWidth / 2));
if (isNeedShowTop) {
//如果在上方显示 则用 触点的距离上方的距离 - 弹框的高度
windowLoc[1] = touchY - popHeight;
} else {
//如果在下方显示 则用 触点的距离上方的距离
windowLoc[1] = touchY;
}
if (isNeedShowRight) {
windowLoc[0] = touchX;
} else {
//显示在左边的话 那么弹出框的位置在触点左边出现,则是触点距离左边距离 - 弹出框的宽度
windowLoc[0] = touchX - popWidth - offset;
}
return windowLoc;
}
因为弹窗设置的是默认在左上方显示,所以计算偏移也是按照这个计算的。
注意:offset是我根据弹窗出现的位置出现偏差增加的偏差值,这个可以根据自己的偏差进行调整,这里出现的偏差值也不知道是什么原因造成的。
用到的工具类中的方法:
/**
* dp转px
*/
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}
/**
* 获取屏幕高度(px)
*/
public static int getScreenHeight(Context context) {
return context.getResources().getDisplayMetrics().heightPixels;
}
/**
* 获取屏幕宽度(px)
*/
public static int getScreenWidth(Context context) {
return context.getResources().getDisplayMetrics().widthPixels;
}
当时查找了很多资料才找到了解决方法,所以在此记录一下。