PopupWindow 在7.0以前使用基本没有问题。但是在7.0之后如果需要从指定View下面弹出,会出现全屏、高度显示不全等问题。
下面等方法能解决此现象:
1.首先定义工具类:
public static int getScreenHeight(Activity activity) {
if (activity == null) {
return 0;
}
Display display = activity.getWindowManager().getDefaultDisplay();
int realHeight = 0;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
final DisplayMetrics metrics = new DisplayMetrics();
display.getRealMetrics(metrics);
realHeight = metrics.heightPixels;
} else {
try {
Method mGetRawH = Display.class.getMethod("getRawHeight");
realHeight = (Integer) mGetRawH.invoke(display);
} catch (Exception e) {
e.printStackTrace();
}
}
return realHeight;
}
public static int getScreenWidth(Activity activity) {
if (activity == null) {
return 0;
}
Display display = activity.getWindowManager().getDefaultDisplay();
int realWidth = 0;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
final DisplayMetrics metrics = new DisplayMetrics();
display.getRealMetrics(metrics);
realWidth = metrics.widthPixels;
} else {
try {
Method mGetRawW = Display.class.getMethod("getRawWidth");
realWidth = (Integer) mGetRawW.invoke(display);
} catch (Exception e) {
e.printStackTrace();
}
}
return realWidth;
}
/**
* 状态栏高度
*/
public static int getStatusBarHeight(Activity activity) {
int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
return activity.getResources().getDimensionPixelSize(resourceId);
}
/**
* 获取虚拟键高度(无论是否隐藏)
*
* @param context
* @return
*/
public static int getNavigationBarHeight(Context context) {
int result = 0;
Resources resources = context.getResources();
int resourceId = resources.getIdentifier(isPortrait(context) ? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
/**
* 手机具有底部导航栏时,底部导航栏是否可见
*
* @param activity
* @return
*/
private static boolean isNavigationBarVisible(Activity activity) {
boolean show = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Display display = activity.getWindow().getWindowManager().getDefaultDisplay();
Point point = new Point();
display.getRealSize(point);
View decorView = activity.getWindow().getDecorView();
Configuration conf = activity.getResources().getConfiguration();
if (Configuration.ORIENTATION_LANDSCAPE == conf.orientation) {
View contentView = decorView.findViewById(android.R.id.content);
show = (point.x != contentView.getWidth());
} else {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
show = (rect.bottom != point.y);
}
}
return show;
}
/**
* 检测是否具有底部导航栏
*
* @param activity
* @return
*/
private static boolean checkDeviceHasNavigationBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
WindowManager windowManager = activity.getWindowManager();
Display display = windowManager.getDefaultDisplay();
DisplayMetrics realDisplayMetrics = new DisplayMetrics();
display.getRealMetrics(realDisplayMetrics);
int realHeight = realDisplayMetrics.heightPixels;
int realWidth = realDisplayMetrics.widthPixels;
DisplayMetrics displayMetrics = new DisplayMetrics();
display.getMetrics(displayMetrics);
int displayHeight = displayMetrics.heightPixels;
int displayWidth = displayMetrics.widthPixels;
return (realWidth - displayWidth) > 0 || (realHeight - displayHeight) > 0;
} else {
boolean hasNavigationBar = false;
Resources resources = activity.getResources();
int id = resources.getIdentifier("config_showNavigationBar", "bool", "android");
if (id > 0) {
hasNavigationBar = resources.getBoolean(id);
}
try {
Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method m = systemPropertiesClass.getMethod("get", String.class);
String navBarOverride = (String) m.invoke(systemPropertiesClass, "qemu.hw.mainkeys");
if ("1".equals(navBarOverride)) {
hasNavigationBar = false;
} else if ("0".equals(navBarOverride)) {
hasNavigationBar = true;
}
} catch (Exception e) {
}
return hasNavigationBar;
}
}
/**
* 获取当前底部导航栏高度(隐藏后高度为0)
*
* @param activity
* @return
*/
public static int getCurrentNavigationBarHeight(Activity activity) {
int navigationBarHeight = 0;
Resources resources = activity.getResources();
int resourceId = resources.getIdentifier(isPortrait(activity) ? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android");
if (resourceId > 0 && checkDeviceHasNavigationBar(activity) && isNavigationBarVisible(activity)) {
navigationBarHeight = resources.getDimensionPixelSize(resourceId);
}
return navigationBarHeight;
}
/**
* 是否为竖屏
*
* @param context
* @return
*/
public static boolean isPortrait(Context context) {
return context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;
}
/**
* 获取可用屏幕高度,排除虚拟键
*
* @param context 上下文
* @return 返回高度
*/
public static int getContentHeight(Activity context) {
int contentHeight = getScreenHeight(context) - getCurrentNavigationBarHeight(context);
return contentHeight;
}
2.顺便定义一个通用的PopupWindow:
public class CustomPopupWindow extends PopupWindow {
private View mContentView;
private View mParentView;
private CustomPopupWindowListener mListener;
private boolean isOutsideTouch;
private boolean isFocus;
private Drawable mBackgroundDrawable;
private int mAnimationStyle;
private boolean isWrap;
private CustomPopupWindow(Builder builder) {
this.mContentView = builder.contentView;
this.mParentView = builder.parentView;
this.mListener = builder.listener;
this.isOutsideTouch = builder.isOutsideTouch;
this.isFocus = builder.isFocus;
this.mBackgroundDrawable = builder.backgroundDrawable;
this.mAnimationStyle = builder.animationStyle;
this.isWrap = builder.isWrap;
initLayout();
}
public static Builder builder() {
return new Builder();
}
private void initLayout() {
mListener.initPopupView(mContentView);
setWidth(isWrap ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT);
setHeight(isWrap ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT);
setFocusable(isFocus);
setOutsideTouchable(isOutsideTouch);
setBackgroundDrawable(mBackgroundDrawable);
//如果设置了动画则使用动画
if (mAnimationStyle != -1) {
setAnimationStyle(mAnimationStyle);
}
setContentView(mContentView);
}
/**
* 获得用于展示popup内容的view
*
* @return
*/
@Override
public View getContentView() {
return mContentView;
}
/**
* 用于填充contentView,必须传ContextThemeWrapper(比如activity)不然popupwindow要报错
*
* @param context
* @param layoutId
* @return
*/
public static View inflateView(Context context, int layoutId) {
return LayoutInflater.from(context)
.inflate(layoutId, null);
}
public void show() {//默认显示到中间
if (mParentView == null) {
showAtLocation(mContentView, Gravity.CENTER , 0, 0);
} else {
showAtLocation(mParentView, Gravity.CENTER , 0, 0);
}
}
public static final class Builder {
private View contentView;
private View parentView;
private CustomPopupWindowListener listener;
/**
* 默认为true
*/
private boolean isOutsideTouch = true;
/**
* 默认为true
*/
private boolean isFocus = true;
/**
* 默认为透明
*/
private Drawable backgroundDrawable = new ColorDrawable(0x00000000);
private int animationStyle = -1;
private boolean isWrap;
private Builder() {
}
public Builder contentView(View contentView) {
this.contentView = contentView;
return this;
}
public Builder parentView(View parentView) {
this.parentView = parentView;
return this;
}
public Builder isWrap(boolean isWrap) {
this.isWrap = isWrap;
return this;
}
public Builder customListener(CustomPopupWindowListener listener) {
this.listener = listener;
return this;
}
public Builder isOutsideTouch(boolean isOutsideTouch) {
this.isOutsideTouch = isOutsideTouch;
return this;
}
public Builder isFocus(boolean isFocus) {
this.isFocus = isFocus;
return this;
}
public Builder backgroundDrawable(Drawable backgroundDrawable) {
this.backgroundDrawable = backgroundDrawable;
return this;
}
public Builder animationStyle(int animationStyle) {
this.animationStyle = animationStyle;
return this;
}
public CustomPopupWindow build() {
if (contentView == null) {
throw new IllegalStateException("ContentView is required");
}
if (listener == null) {
throw new IllegalStateException("CustomPopupWindowListener is required");
}
return new CustomPopupWindow(this);
}
}
public interface CustomPopupWindowListener {
/**初始化
* @param contentView view
*/
void initPopupView(View contentView);
}
@Override
public void showAsDropDown(View anchor) {
showAsDropDown(anchor, 0, 0);
}
@Override
public void showAsDropDown(View anchor, int xoff, int yoff) {
showAsDropDown(anchor, xoff, yoff, Gravity.TOP | Gravity.START);
}
@Override
public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
//7.0一下正常
if (Build.VERSION.SDK_INT < 24 || getHeight() == ViewGroup.LayoutParams.WRAP_CONTENT) {
super.showAsDropDown(anchor, xoff, yoff, gravity);
} else {
if (getContentView().getContext() instanceof Activity) {
Activity activity = (Activity) getContentView().getContext();
int screenHeight;
// 获取屏幕可用高度:真实高度-虚拟按键的高度
screenHeight = DeviceUtil.getContentHeight(activity);
int[] location = new int[2];
// 获取控件在屏幕的位置
anchor.getLocationOnScreen(location);
// pop最大高度
int maxHeight = screenHeight - location[1] - anchor.getHeight();
// pop 有具体的高度值,但是小于anchor下边缘与屏幕底部的距离, 正常显示
if (getHeight() > 0 && getHeight() < maxHeight) {
super.showAsDropDown(anchor, xoff, yoff, gravity);
} else {
//设置为最大可用高度
setHeight(maxHeight);
super.showAsDropDown(anchor, xoff, yoff, gravity);
}
}
}
}
}
3.然后调用即可:
CustomPopupWindow customPopupWindow = CustomPopupWindow.builder()
.contentView(CustomPopupWindow.inflateView(mActivity, R.layout.layout_card_choose))
.isWrap(false)
.isOutsideTouch(false)
.customListener(new CustomPopupWindow.CustomPopupWindowListener() {
@Override
public void initPopupView(View contentView) {
}
})
.build();
customPopupWindow.showAsDropDown(view);