public class SmartPopupWindow extends PopupWindow {
private int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
private int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
private float mAlpha = 1f; //背景灰度 0-1 1表示全透明
private Context mContext;
private View mContentView;
private boolean isTouchOutsideDismiss = true; //點擊外部消失
private int mAnimationStyle = -1;
//下面的幾個變量只是位置處理外部點擊事件(6.0以上)
//是否只是獲取寬高
//getViewTreeObserver監聽時
private boolean isOnlyGetWH = true;
private View mAnchorView;
@VerticalPosition
private int mVerticalGravity = VerticalPosition.BELOW;
@HorizontalPosition
private int mHorizontalGravity = HorizontalPosition.LEFT;
private int mOffsetX;
private int mOffsetY;
public SmartPopupWindow(Context context) {
this(context, null);
}
public SmartPopupWindow(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SmartPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
}
public void init() {
setContentView(mContentView);
setHeight(mHeight);
setWidth(mWidth);
touchOutsideDismiss(isTouchOutsideDismiss);
if (mAnimationStyle != -1) {
setAnimationStyle(mAnimationStyle);
}
}
private void touchOutsideDismiss(boolean touchOutsideDismiss) {
if (!touchOutsideDismiss) {
setFocusable(true);
setOutsideTouchable(false);
setBackgroundDrawable(null);
getContentView().setFocusable(true);
getContentView().setFocusableInTouchMode(true);
getContentView().setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
dismiss();
return true;
}
return false;
}
});
//在Android 6.0以上 ,只能通過攔截事件來解決
setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
if ((event.getAction() == MotionEvent.ACTION_DOWN)
&& ((x < 0) || (x >= mWidth) || (y < 0) || (y >= mHeight))) {
//outside
return true;
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
//outside
return true;
}
return false;
}
});
} else {
setFocusable(true);
setOutsideTouchable(true);
setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
}
}
@Override
public void showAtLocation(View parent, int gravity, int x, int y) {
isOnlyGetWH = true;
mAnchorView = parent;
mOffsetX = x;
mOffsetY = y;
addGlobalLayoutListener(getContentView());
super.showAtLocation(parent, gravity, x, y);
}
public void showAtAnchorView(@NonNull View anchorView, @VerticalPosition int verticalPos, @HorizontalPosition int horizontalPos) {
showAtAnchorView(anchorView, verticalPos, horizontalPos, true);
}
public void showAtAnchorView(@NonNull View anchorView, @VerticalPosition int verticalPos, @HorizontalPosition int horizontalPos, boolean fitInScreen) {
showAtAnchorView(anchorView, verticalPos, horizontalPos, 0, 0, fitInScreen);
}
public void showAtAnchorView(@NonNull View anchorView, @VerticalPosition int verticalPos, @HorizontalPosition int horizontalPos, int x, int y) {
showAtAnchorView(anchorView, verticalPos, horizontalPos, x, y, true);
}
public void showAtAnchorView(@NonNull View anchorView, @VerticalPosition int verticalPos, @HorizontalPosition int horizontalPos, int x, int y, boolean fitInScreen) {
isOnlyGetWH = false;
mAnchorView = anchorView;
mOffsetX = x;
mOffsetY = y;
mVerticalGravity = verticalPos;
mHorizontalGravity = horizontalPos;
showBackgroundAnimator();
final View contentView = getContentView();
addGlobalLayoutListener(contentView);
setClippingEnabled(fitInScreen);
contentView.measure(makeDropDownMeasureSpec(getWidth()), makeDropDownMeasureSpec(getHeight()));
final int measuredW = contentView.getMeasuredWidth();
final int measuredH = contentView.getMeasuredHeight();
if (!fitInScreen) {
final int[] anchorLocation = new int[2];
anchorView.getLocationInWindow(anchorLocation);
x += anchorLocation[0];
y += anchorLocation[1] + anchorView.getHeight();
}
y = calculateY(anchorView, verticalPos, measuredH, y);
x = calculateX(anchorView, horizontalPos, measuredW, x);
if (fitInScreen) {
PopupWindowCompat.showAsDropDown(this, anchorView, x, y, Gravity.NO_GRAVITY);
} else {
showAtLocation(anchorView, Gravity.NO_GRAVITY, x, y);
}
}
/**
* 根據垂直gravity計算y偏移
*/
private int calculateY(View anchor, int verticalGravity, int measuredH, int y) {
switch (verticalGravity) {
case VerticalPosition.ABOVE:
y -= measuredH + anchor.getHeight();
break;
case VerticalPosition.ALIGN_BOTTOM:
y -= measuredH;
break;
case VerticalPosition.CENTER:
y -= anchor.getHeight() / 2 + measuredH / 2;
break;
case VerticalPosition.ALIGN_TOP:
y -= anchor.getHeight();
break;
case VerticalPosition.BELOW:
// Default position.
break;
}
return y;
}
/**
* 根據水平gravity計算x偏移
*/
private int calculateX(View anchor, int horizontalGravity, int measuredW, int x) {
switch (horizontalGravity) {
case HorizontalPosition.LEFT:
x -= measuredW;
break;
case HorizontalPosition.ALIGN_RIGHT:
x -= measuredW - anchor.getWidth();
break;
case HorizontalPosition.CENTER:
x += anchor.getWidth() / 2 - measuredW / 2;
break;
case HorizontalPosition.ALIGN_LEFT:
// Default position.
break;
case HorizontalPosition.RIGHT:
x += anchor.getWidth();
break;
}
return x;
}
@SuppressWarnings("ResourceType")
private static int makeDropDownMeasureSpec(int measureSpec) {
return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec), getDropDownMeasureSpecMode(measureSpec));
}
private static int getDropDownMeasureSpecMode(int measureSpec) {
switch (measureSpec) {
case ViewGroup.LayoutParams.WRAP_CONTENT:
return View.MeasureSpec.UNSPECIFIED;
default:
return View.MeasureSpec.EXACTLY;
}
}
//監聽器,用於PopupWindow彈出時獲取準確的寬高
private final ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mWidth = getContentView().getWidth();
mHeight = getContentView().getHeight();
//只獲取寬高時,不執行更新操作
if (isOnlyGetWH) {
removeGlobalLayoutListener();
return;
}
updateLocation(mWidth, mHeight, mAnchorView, mVerticalGravity, mHorizontalGravity, mOffsetX, mOffsetY);
removeGlobalLayoutListener();
}
};
private void updateLocation(int width, int height, @NonNull View anchor,
@VerticalPosition final int verticalGravity,
@HorizontalPosition int horizontalGravity,
int x, int y) {
x = calculateX(anchor, horizontalGravity, width, x);
y = calculateY(anchor, verticalGravity, height, y);
update(anchor, x, y, width, height);
}
private void removeGlobalLayoutListener() {
if (getContentView() != null) {
if (Build.VERSION.SDK_INT >= 16) {
getContentView().getViewTreeObserver().removeOnGlobalLayoutListener(mOnGlobalLayoutListener);
} else {
getContentView().getViewTreeObserver().removeGlobalOnLayoutListener(mOnGlobalLayoutListener);
}
}
}
private void addGlobalLayoutListener(View contentView) {
contentView.getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
}
@Override
public void dismiss() {
super.dismiss();
dismissBackgroundAnimator();
removeGlobalLayoutListener();
}
/**
* 窗口顯示,窗口背景透明度漸變動畫
*/
private void showBackgroundAnimator() {
if (mAlpha >= 1f) return;
ValueAnimator animator = ValueAnimator.ofFloat(1.0f, mAlpha);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float alpha = (float) animation.getAnimatedValue();
setWindowBackgroundAlpha(alpha);
}
});
animator.setDuration(360);
animator.start();
}
/**
* 窗口隱藏,窗口背景透明度漸變動畫
*/
private void dismissBackgroundAnimator() {
if (mAlpha >= 1f) return;
ValueAnimator animator = ValueAnimator.ofFloat(mAlpha, 1.0f);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float alpha = (float) animation.getAnimatedValue();
setWindowBackgroundAlpha(alpha);
}
});
animator.setDuration(360);
animator.start();
}
/**
* 控制窗口背景的不透明度
*/
private void setWindowBackgroundAlpha(float alpha) {
if (mContext == null) return;
if (mContext instanceof Activity) {
Window window = ((Activity) mContext).getWindow();
WindowManager.LayoutParams layoutParams = window.getAttributes();
layoutParams.alpha = alpha;
window.setAttributes(layoutParams);
}
}
public static class Builder {
private SmartPopupWindow mWindow;
private Builder(Activity activity, View view) {
mWindow = new SmartPopupWindow(activity);
mWindow.mContext = activity;
mWindow.mContentView = view;
}
public static Builder build(Activity activity, View view) {
return new Builder(activity, view);
}
public Builder setSize(int width, int height) {
mWindow.mWidth = width;
mWindow.mHeight = height;
return this;
}
public Builder setAnimationStyle(int animationStyle) {
mWindow.mAnimationStyle = animationStyle;
return this;
}
public Builder setAlpha(float alpha) {
mWindow.mAlpha = alpha;
return this;
}
public Builder setOutsideTouchDismiss(boolean dismiss) {
mWindow.isTouchOutsideDismiss = dismiss;
return this;
}
/**
* 創建PopupWindow
* @return
*/
public SmartPopupWindow createPopupWindow() {
mWindow.init();
return mWindow;
}
}
}
附錄:
@IntDef({
HorizontalPosition.CENTER,
HorizontalPosition.LEFT,
HorizontalPosition.RIGHT,
HorizontalPosition.ALIGN_LEFT,
HorizontalPosition.ALIGN_RIGHT,
})
@Retention(RetentionPolicy.SOURCE)
public @interface HorizontalPosition {
int CENTER = 0;
int LEFT = 1;
int RIGHT = 2;
int ALIGN_LEFT = 3;
int ALIGN_RIGHT = 4;
}
@IntDef({
VerticalPosition.CENTER,
VerticalPosition.ABOVE,
VerticalPosition.BELOW,
VerticalPosition.ALIGN_TOP,
VerticalPosition.ALIGN_BOTTOM,
})
@Retention(RetentionPolicy.SOURCE)
public @interface VerticalPosition {
int CENTER = 0;
int ABOVE = 1;
int BELOW = 2;
int ALIGN_TOP = 3;
int ALIGN_BOTTOM = 4;
}