自適應彈出 PopupWindow,根據popupContentView高度來判斷向上還是向下或者向左向右

一個通用的popupWindow,加了一個自適應彈出 PopupWindow的方法,根據popupContentView高度來判斷向上還是向下,請忽略動畫效果的錯覺,好多方向用的都是同一個動畫效果,動畫效果根據實際情況自行添加。。。

       

/**
 * created by Da Peng at 2019/11/4
 */

public class MyPopupWindow extends PopupWindow {

    private View view, popupView;
    private float bgLevel;
    private Context mContext;
    private int layoutResId, width, height, animationStyle;
    private int upAnimationStyle, downAnimationStyle;
    private boolean isShowBg, isOutSideCancel;
    private PopupViewInterface listener;
    private int rootGravity = Gravity.TOP | Gravity.START;       // 以左上角爲計算參照點

    private MyPopupWindow(Builder builder) {
        this.mContext = builder.context;
        this.view = builder.view;
        this.bgLevel = builder.bgLevel;
        this.layoutResId = builder.layoutResId;
        this.width = builder.width;
        this.height = builder.height;
        this.animationStyle = builder.animationStyle;
        this.upAnimationStyle = builder.upAnimationStyle;
        this.downAnimationStyle = builder.downAnimationStyle;
        this.isShowBg = builder.isShowBg;
        this.isOutSideCancel = builder.isOutSideCancel;
        this.listener = builder.listener;
        initView();
    }

    private void initView() {
        if (layoutResId != 0) {
            popupView = LayoutInflater.from(mContext).inflate(layoutResId, null, false);
        } else if (view != null) {
            popupView = view;
        }

        if (listener != null) {
            listener.getPopupContentView(popupView);
        }

        setContentView(popupView);
        setWidthAndHeight(width, height);
        //設置透明背景
        setBackgroundDrawable(new ColorDrawable(0x00000000));
        //設置outside可點擊
        setOutsideTouchable(isOutSideCancel);
        setFocusable(isOutSideCancel);

        // { PopupWindow.ANIMATION_STYLE_DEFAULT = -1}
        setAnimationStyle(animationStyle == 0 ? -1 : animationStyle);

    }

    private void setBackGroundLevel(boolean isShowBg, float bgLevel) {
        if (isShowBg) {
            Window window = ((Activity) mContext).getWindow();
            WindowManager.LayoutParams params = window.getAttributes();
            params.alpha = bgLevel == 0 ? 0.8f : bgLevel;
            window.setAttributes(params);
        }
    }

    private void setWidthAndHeight(int width, int height) {
        if (width == 0 || height == 0) {
            setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
            setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        } else {
            setWidth(width);
            setHeight(height);
        }
    }

    public interface PopupViewInterface {
        void getPopupContentView(View popupView);
    }

    /**
     * 根據位置自適應向上還是向下彈出
     *
     * @param anchorView 目標View
     * @param xoff       x方向偏移
     * @param yoff       y方向偏移
     */
    public void showVerticalAutomatic(View anchorView, int xoff, int yoff) {
        int[] offsets = calculateVerticalPopWindowPos(anchorView, popupView, 0, true);
        showAtLocation(anchorView, rootGravity, offsets[0] + xoff, offsets[1] + yoff);
    }

    /**
     * 根據位置自適應向左還是向右彈出
     *
     * @param anchorView 目標View
     * @param xoff       x方向偏移
     * @param yoff       y方向偏移
     */
    public void showHorizontalAutomatic(View anchorView, int xoff, int yoff) {
        int[] offsets = calculateHorizontalPopupWindow(anchorView, popupView, 0, true);
        showAtLocation(anchorView, rootGravity, offsets[0] + xoff, offsets[1] + yoff);
    }

    /**
     * 向左彈出
     *
     * @param anchorView
     * @param xoff
     * @param yoff
     */
    public void showAnchorViewLeft(View anchorView, int xoff, int yoff) {
        int[] offsets = calculateHorizontalPopupWindow(anchorView, popupView, Gravity.START, false);
        // 以左上角爲計算參照點  Gravity.TOP | Gravity.START
        showAtLocation(anchorView, rootGravity, offsets[0] + xoff, offsets[1] + yoff);
    }

    /**
     * 向右彈出
     *
     * @param anchorView
     * @param xoff
     * @param yoff
     */
    public void showAnchorViewRight(View anchorView, int xoff, int yoff) {
        int[] offsets = calculateHorizontalPopupWindow(anchorView, popupView, Gravity.END, false);
        showAtLocation(anchorView, rootGravity, offsets[0] + xoff, offsets[1] + yoff);
    }

    /**
     * 向上彈出
     *
     * @param anchorView
     * @param xoff
     * @param yoff
     */
    public void showAnchorViewTop(View anchorView, int xoff, int yoff) {
        int[] offsets = calculateVerticalPopWindowPos(anchorView, popupView, Gravity.TOP, false);
        showAtLocation(anchorView, rootGravity, offsets[0] + xoff, offsets[1] + yoff);
    }

    /**
     * 向下彈出
     *
     * @param anchorView
     * @param xoff
     * @param yoff
     */
    public void showAnchorViewBottom(View anchorView, int xoff, int yoff) {
        int[] offsets = calculateVerticalPopWindowPos(anchorView, popupView, Gravity.BOTTOM, false);
        showAtLocation(anchorView, rootGravity, offsets[0] + xoff, offsets[1] + yoff);
    }

    @Override
    public void dismiss() {
        super.dismiss();
        setBackGroundLevel(isShowBg, 1.0f);
    }

    @Override
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
        setBackGroundLevel(isShowBg, bgLevel);
        super.showAsDropDown(anchor, xoff, yoff, gravity);
    }

    @Override
    public void showAtLocation(View parent, int gravity, int x, int y) {
        setBackGroundLevel(isShowBg, bgLevel);
        super.showAtLocation(parent, gravity, x, y);
    }

    /**
     * @param anchorView       錨點View
     * @param popupContentView popupView
     * @param gravity          顯示在左邊或者右邊
     * @param showAutomatic    是否自適應 根據錨點View位置 左右顯示
     * @return
     */
    private int[] calculateHorizontalPopupWindow(View anchorView, View popupContentView, int gravity, boolean showAutomatic) {
        int[] popupWindowLoc = new int[2];
        int[] anchorLoc = new int[2];

        anchorView.getLocationOnScreen(anchorLoc);
        int anchorViewHeight = anchorView.getHeight();
        int anchorViewWidth = anchorView.getWidth();

        // 獲取屏幕的高寬
        final int screenHeight = AppUtils.getScreenHeight(mContext);
        final int screenWidth = AppUtils.getScreenWidth(mContext);

        popupContentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        // 計算popupView的高寬
        final int popupViewHeight = popupContentView.getMeasuredHeight();
        final int popupViewWidth = popupContentView.getMeasuredWidth();

        if (showAutomatic) {
            boolean isNeedShowLeft = screenWidth - anchorLoc[0] - anchorViewWidth < popupViewWidth;
            if (isNeedShowLeft) {
                popupWindowLoc[0] = anchorLoc[0] - popupViewWidth;
                popupWindowLoc[1] = anchorViewHeight / 2 + anchorLoc[1] - popupViewHeight / 2;
                setAnimationStyle(R.style.popup_anim_left);
            } else {
                popupWindowLoc[0] = anchorLoc[0] + anchorViewWidth;
                popupWindowLoc[1] = anchorViewHeight / 2 + anchorLoc[1] - popupViewHeight / 2;
                setAnimationStyle(R.style.popup_anim_left);
            }
        } else {
            if (gravity == Gravity.START) {
                popupWindowLoc[0] = anchorLoc[0] - popupViewWidth;
                popupWindowLoc[1] = anchorViewHeight / 2 + anchorLoc[1] - popupViewHeight / 2;
                setAnimationStyle(R.style.popup_anim_left);
            } else if (gravity == Gravity.END) {
                popupWindowLoc[0] = anchorLoc[0] + anchorViewWidth;
                popupWindowLoc[1] = anchorViewHeight / 2 + anchorLoc[1] - popupViewHeight / 2;
                setAnimationStyle(R.style.popup_anim_left);
            }
        }

        return popupWindowLoc;
    }

    /**
     * 計算豎直方向偏移
     */
    private int[] calculateVerticalPopWindowPos(final View anchorView, final View popupContentView, int gravity, boolean showAutomatic) {
        final int[] popupWindowLoc = new int[2];
        final int[] anchorLoc = new int[2];

        // 獲取錨點View在屏幕上的左上角座標位置
        anchorView.getLocationOnScreen(anchorLoc);
        final int anchorHeight = anchorView.getHeight();
        // 獲取屏幕的高寬
        final int screenHeight = AppUtils.getScreenHeight(mContext);
        final int screenWidth = AppUtils.getScreenWidth(mContext);

        popupContentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
        // 計算popupView的高寬
        final int popupViewHeight = popupContentView.getMeasuredHeight();
        final int popupViewWidth = popupContentView.getMeasuredWidth();

        Log.e("MyPopup", "screenHeight: " + screenHeight +
                "   anchorView Y方向: " + anchorLoc[1] +
                "   anchorView Height: " + anchorHeight +
                "   anchorView 下方剩餘高度:  " + (screenHeight - anchorLoc[1] - anchorHeight) +
                "   popupViewHeight:" + popupViewHeight);

        if (showAutomatic) {
            // 判斷需要向上彈出還是向下彈出顯示
            final boolean isNeedShowUp = (screenHeight - anchorLoc[1] - anchorHeight < popupViewHeight);
            if (isNeedShowUp) {
                //popupWindowLoc[0] = screenWidth - popupViewWidth;
                popupWindowLoc[0] = (screenWidth - popupViewWidth) / 2;
                popupWindowLoc[1] = anchorLoc[1] - popupViewHeight;
                if (upAnimationStyle != 0) {
                    setAnimationStyle(upAnimationStyle);
                }
            } else {
                //popupWindowLoc[0] = screenWidth - popupViewWidth;
                popupWindowLoc[0] = (screenWidth - popupViewWidth) / 2;
                popupWindowLoc[1] = anchorLoc[1] + anchorHeight;
                if (downAnimationStyle != 0) {
                    setAnimationStyle(downAnimationStyle);
                }
            }
        } else {

            if (gravity == Gravity.TOP) {
                //popupWindowLoc[0] = screenWidth - popupViewWidth;
                popupWindowLoc[0] = (screenWidth - popupViewWidth) / 2;
                popupWindowLoc[1] = anchorLoc[1] - popupViewHeight;
                if (upAnimationStyle != 0) {
                    setAnimationStyle(upAnimationStyle);
                }
            } else if (gravity == Gravity.BOTTOM) {
                //popupWindowLoc[0] = screenWidth - popupViewWidth;
                popupWindowLoc[0] = (screenWidth - popupViewWidth) / 2;
                popupWindowLoc[1] = anchorLoc[1] + anchorHeight;
                if (downAnimationStyle != 0) {
                    setAnimationStyle(downAnimationStyle);
                }
            }

        }


        return popupWindowLoc;
    }

    public static class Builder {

        public View view;
        public Context context;
        public float bgLevel;
        public PopupViewInterface listener;
        public boolean isShowBg, isOutSideCancel;
        public int layoutResId, width, height, animationStyle, upAnimationStyle, downAnimationStyle;

        public Builder(Context context) {
            this.context = context;
        }

        public Builder setView(int layoutResId) {
            this.view = null;
            this.layoutResId = layoutResId;
            return this;
        }

        public Builder setView(View view) {
            this.view = view;
            this.layoutResId = 0;
            return this;
        }

        public Builder setContentViewClickListener(PopupViewInterface listener) {
            this.listener = listener;
            return this;
        }

        public Builder setParams(int width, int height) {
            this.width = width;
            this.height = height;
            return this;
        }

        public Builder setBackGroundLevel(float level) {
            this.bgLevel = level;
            return this;
        }

        public Builder setIsShowBg(boolean isShowBg) {
            this.isShowBg = isShowBg;
            return this;
        }

        public Builder setOutSideCancel(boolean isOutSideCancel) {
            this.isOutSideCancel = isOutSideCancel;
            return this;
        }

        public Builder setAnimationStyle(int animationStyle) {
            this.animationStyle = animationStyle;
            return this;
        }

        public Builder setUpShowAnimationStyle(int upAnimationStyle) {
            this.upAnimationStyle = upAnimationStyle;
            return this;
        }

        public Builder setDownShowAnimationStyle(int downAnimationStyle) {
            this.downAnimationStyle = downAnimationStyle;
            return this;
        }

        public MyPopupWindow build() {
            return new MyPopupWindow(this);
        }

    }

}

簡單用法(這邊用的Kotlin):

    private fun initView() {
        val popupWindow = MyPopupWindow.Builder(this)
                .setIsShowBg(true) // 是否背景變灰
                .setParams(1000, ViewGroup.LayoutParams.WRAP_CONTENT) // 設置寬高
                .setView(R.layout.dialog_popup_test) // 設置佈局
                .setUpShowAnimationStyle(R.style.popup_dialog_anim2) // 向上展開的動畫
                .setDownShowAnimationStyle(R.style.popup_dialog_anim)  //向下展開的動畫
                .setOutSideCancel(true) // 點擊外面關閉
                .setBackGroundLevel(0.8f)  //背景顏色深度
                .setContentViewClickListener(this) // view操作回調
                .build()


        btn.setOnClickListener {
           // 自適應
           popupWindow.showAutomatic(btn, -350, 0)

           //  val array = IntArray(2)
           //  btn.getLocationOnScreen(array)
           //  Log.e("x-y", array[0].toString() + "  " + array[1].toString())

        }

        btn2.setOnClickListener {
            // 原本的用法 以底部彈出爲例,其他類似
            popupWindow.showAtLocation(it,Gravity.BOTTOM,0,0)
        }

    }


    override fun getPopupContentView(popupView: View?) {
        popupView?.let {
            it.findViewById<TextView>(R.id.test_tv).setOnClickListener {
                Toast.makeText(this, "clicked", Toast.LENGTH_SHORT).show()
            }
        }
    }

(補充)獲取屏幕寬高:


    public static int getScreenHeight(Context context) {

        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        Point size = new Point();

        ((Activity) context).getWindowManager().getDefaultDisplay().getSize(size);

        return size.y;// 高  return size.x 寬

    }

注意一個問題

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