自定義指定位置顯示的彈窗

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章