Android通用PopupWindow的封装

Android (30) 2024-01-24 09:12

Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Android通用PopupWindow的封装,希望能够帮助你!!!。

日常开发中或多或少都会使用到PopupWindow,每次都需要自定义继承至PopupWindow的view,写多了不胜其烦,今天我们对PopupWindow做个封装,可通过链式构建,支持View方式或ViewDataBinding方式设置ContentView(类似的封装可看我的另外一篇文章Android通用Dialog的封装)。

Android通用PopupWindow的封装_https://bianchenghao6.com/blog_Android_第1张

先看使用示例:

1、通过View的方式构建,这里的SortingFilterLayout是我们自定义的一个View

new CommonPopupWindow.ViewBuilder<SortingFilterLayout>()
                    .width(ConstraintLayout.LayoutParams.WRAP_CONTENT)
                    .height(ConstraintLayout.LayoutParams.WRAP_CONTENT)
                    .outsideTouchable(true)
                    .focusable(true)
                    .animationStyle(R.style.sorting_filter_pop_animation)
                    .clippingEnabled(false)
                    .alpha(0.618f)
                    .view(new SortingFilterLayout(context))
                    .intercept(new CommonPopupWindow.ViewEvent<SortingFilterLayout>() {
                        @Override
                        public void getView(CommonPopupWindow commonPopupWindow, SortingFilterLayout view) {
                            /*在这里可做初始化相关操作*/
                        }
                    })
                    .onShowBefore(new CommonPopupWindow.OnShowBefore<SortingFilterLayout>() {
                        @Override
                        public void showBefore(CommonPopupWindow popupWindow, SortingFilterLayout view) {
                            /*每次show之前都会回调*/
                        }
                    })
                    .onDismissListener(new PopupWindow.OnDismissListener() {
                        @Override
                        public void onDismiss() {
                             /*每次dismiss都会回调*/
                        }
                    })
                    .build(context)
                    .showAtLocation(view, Gravity.RIGHT, 0, 0);

2、通过ViewDataBinding的方式构建,这里的layout_sorting_filter是我们自定义View的xml文件,LayoutSortingFilterBinding是对应的ViewDataBinding

new CommonPopupWindow.ViewDataBindingBuilder<LayoutSortingFilterBinding>()
            .width(ConstraintLayout.LayoutParams.WRAP_CONTENT)
            .height(ConstraintLayout.LayoutParams.WRAP_CONTENT)
            .outsideTouchable(true)
            .focusable(true)
            .animationStyle(R.style.sorting_filter_pop_animation)
            .clippingEnabled(false)
            .alpha(0.618f)
            .layoutId(mRootView.getActivity(),R.layout.layout_sorting_filter)
            .intercept(new CommonPopupWindow.ViewEvent<LayoutSortingFilterBinding>() {
                @Override
                public void getView(CommonPopupWindow commonPopupWindow, LayoutSortingFilterBinding binding) {
                    /*在这里可做初始化相关操作*/
                }
            })
            .onShowBefore(new CommonPopupWindow.OnShowBefore<LayoutSortingFilterBinding>() {
                @Override
                public void showBefore(CommonPopupWindow popupWindow, LayoutSortingFilterBinding binding) {
                     /*每次show之前都会回调*/
                }
            })
            .onDismissListener(new PopupWindow.OnDismissListener() {
                @Override
                public void onDismiss() {
                     /*每次dismiss都会回调*/
                }
            })
            .build(context)
            .showAtLocation(view,Gravity.RIGHT,0,0);
Android通用PopupWindow的封装_https://bianchenghao6.com/blog_Android_第2张

CommonPopupWindow完整的代码如下:

import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.PopupWindow;

import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;

/**
 * 作者:可口可乐的可乐 on 2020/01/07 17:57
 * Email : 1966353889@qq.com
 * Describe:通用PopupWindow
 */
public abstract class CommonPopupWindow<V extends View, Binding extends ViewDataBinding> extends PopupWindow {
    private OnDismissListener onDismissListener;

    private CommonPopupWindow(Context context, ViewDataBindingBuilder<Binding> builder) {
        super(context);
        getIntercept().intercept();
        setContentView(builder.mBinding.getRoot());
        setWidth(builder.mWidth);
        setHeight(builder.mHeight);
        setOutsideTouchable(builder.mOutsideTouchable);
        setBackgroundDrawable(builder.mBackground);
        setFocusable(builder.mFocusable);
        /*
         * 结合showAtLocation使用精准定位,需设置mClippingEnabled为false,否则当内容过多时会移位,比如设置在某
         * 个控件底下内容过多时popupwindow会上移
         */
        setClippingEnabled(builder.mClippingEnabled);
        setAnimationStyle(builder.mAnimationStyle);
        onDismissListener = builder.onDismissListener;
        super.setOnDismissListener(new OnDismissListener() {
            @Override
            public void onDismiss() {
                if (context instanceof Activity) {
                    WindowManager.LayoutParams lp = ((Activity) context).getWindow().getAttributes();
                    if (lp != null && lp.alpha != 1.0f) {
                        lp.alpha = 1.0f; /* 0.0~1.0*/
                        ((Activity) context).getWindow().setAttributes(lp);
                    }
                }
                if (onDismissListener != null) {
                    onDismissListener.onDismiss();
                }
                if (builder.mBinding != null) {
                    builder.mBinding.unbind();
                }
            }
        });
    }


    private CommonPopupWindow(Context context, ViewBuilder<V> builder) {
        super(context);
        getIntercept().intercept();
        setContentView(builder.view);
        setWidth(builder.mWidth);
        setHeight(builder.mHeight);
        setOutsideTouchable(builder.mOutsideTouchable);
        setBackgroundDrawable(builder.mBackground);
        setFocusable(builder.mFocusable);
        /*
         * 结合showAtLocation使用精准定位,需设置mClippingEnabled为false,否则当内容过多时会移位,比如设置在某
         * 个控件底下内容过多时popupwindow会上移
         */
        setClippingEnabled(builder.mClippingEnabled);
        setAnimationStyle(builder.mAnimationStyle);
        onDismissListener = builder.onDismissListener;
        super.setOnDismissListener(new OnDismissListener() {
            @Override
            public void onDismiss() {
                if (context instanceof Activity) {
                    WindowManager.LayoutParams lp = ((Activity) context).getWindow().getAttributes();
                    if (lp != null && lp.alpha != 1.0f) {
                        lp.alpha = 1.0f; /* 0.0~1.0*/
                        ((Activity) context).getWindow().setAttributes(lp);
                    }
                }
                if (onDismissListener != null) {
                    onDismissListener.onDismiss();
                }
            }
        });
    }

    @Override
    public void showAsDropDown(View anchor, int xoff, int yoff) {
        if (getContentView() != null) {
            Context context = getContentView().getContext();
            if (context instanceof Activity) {
                WindowManager.LayoutParams lp = ((Activity) context).getWindow().getAttributes();
                if (lp != null && lp.alpha != getAlpha()) {
                    lp.alpha = getAlpha();
                    ((Activity) context).getWindow().setAttributes(lp);
                }
            }
        }
        getIntercept().showBefore();
        super.showAsDropDown(anchor, xoff, yoff);
        getIntercept().showAfter();
    }

    @Override
    public void showAtLocation(View parent, int gravity, int x, int y) {
        if (getContentView() != null) {
            Context context = getContentView().getContext();
            if (context instanceof Activity) {
                WindowManager.LayoutParams lp = ((Activity) context).getWindow().getAttributes();
                if (lp != null && lp.alpha != getAlpha()) {
                    lp.alpha = getAlpha();
                    ((Activity) context).getWindow().setAttributes(lp);
                }
            }
        }
        getIntercept().showBefore();
        super.showAtLocation(parent, gravity, x, y);
        getIntercept().showAfter();
    }

    @Override
    public void setOnDismissListener(OnDismissListener onDismissListener) {
        this.onDismissListener = onDismissListener;
    }

    abstract float getAlpha();

    abstract CommonPopupWindow getInstance();

    abstract InterceptTransform getIntercept();

    public static class ViewDataBindingBuilder<Binding extends ViewDataBinding> {
        private Binding mBinding;
        private int mWidth;
        private int mHeight;
        private boolean mOutsideTouchable;
        private ViewEvent<Binding> mEvent;
        private Drawable mBackground;
        private boolean mFocusable;
        /**
         * 设置窗口弹出时背景的透明度
         * 0f(透明)~1.0f(正常)
         * 设置了alpha时需要在onDismiss恢复窗口的alpha至默认值即1.0f
         */
        private float alpha = 1.0f;
        /**
         * 结合showAtLocation使用精准定位,需设置mClippingEnabled为false,否则当内容过多时会移位,比如设置在某
         * 个控件底下内容过多时popupwindow会上移
         */
        private boolean mClippingEnabled = true;
        private OnShowBefore<Binding> mOnShowBefore;
        private OnShowAfter<Binding> mOnShowAfter;
        private int mAnimationStyle = -1;
        private OnDismissListener onDismissListener;

        public ViewDataBindingBuilder<Binding> layoutId(Context context, int layoutId) {
            this.mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), layoutId, null, false);
            return this;
        }

        public ViewDataBindingBuilder<Binding> viewDataBinding(Binding mBinding) {
            this.mBinding = mBinding;
            return this;
        }

        public ViewDataBindingBuilder<Binding> width(int width) {
            this.mWidth = width;
            return this;
        }

        public ViewDataBindingBuilder<Binding> height(int height) {
            this.mHeight = height;
            return this;
        }

        public ViewDataBindingBuilder<Binding> outsideTouchable(boolean outsideTouchable) {
            this.mOutsideTouchable = outsideTouchable;
            return this;
        }

        public ViewDataBindingBuilder<Binding> backgroundDrawable(Drawable background) {
            this.mBackground = background;
            return this;
        }

        public ViewDataBindingBuilder<Binding> focusable(boolean focusable) {
            this.mFocusable = focusable;
            return this;
        }

        public ViewDataBindingBuilder<Binding> alpha(float alpha) {
            this.alpha = alpha;
            return this;
        }

        public ViewDataBindingBuilder<Binding> clippingEnabled(boolean clippingEnabled) {
            this.mClippingEnabled = clippingEnabled;
            return this;
        }

        public ViewDataBindingBuilder<Binding> onShowBefore(OnShowBefore<Binding> showBefore) {
            this.mOnShowBefore = showBefore;
            return this;
        }

        public ViewDataBindingBuilder<Binding> onShowAfter(OnShowAfter<Binding> showAfter) {
            this.mOnShowAfter = showAfter;
            return this;
        }

        public ViewDataBindingBuilder<Binding> animationStyle(int animationStyle) {
            this.mAnimationStyle = animationStyle;
            return this;
        }

        public ViewDataBindingBuilder<Binding> intercept(ViewEvent<Binding> event) {
            this.mEvent = event;
            return this;
        }

        public ViewDataBindingBuilder<Binding> onDismissListener(OnDismissListener onDismissListener) {
            this.onDismissListener = onDismissListener;
            return this;
        }

        public <V extends View> CommonPopupWindow<V, Binding> build(Context context) {
            return new CommonPopupWindow<V, Binding>(context, this) {
                @Override
                public float getAlpha() {
                    return alpha;
                }

                @Override
                CommonPopupWindow getInstance() {
                    return this;
                }

                @Override
                InterceptTransform getIntercept() {
                    return new InterceptTransform<Binding>() {
                        @Override
                        public void showBefore() {
                            if (mOnShowBefore != null) {
                                mOnShowBefore.showBefore(getInstance(), mBinding);
                            }
                        }

                        @Override
                        public void showAfter() {
                            if (mOnShowAfter != null) {
                                mOnShowAfter.showAfter(getInstance(), mBinding);
                            }
                        }

                        @Override
                        public void intercept() {
                            if (mEvent != null) {
                                mEvent.getView(getInstance(), mBinding);
                            }
                        }
                    };
                }
            };
        }
    }

    public static class ViewBuilder<V extends View> {
        private V view;
        private int mWidth;
        private int mHeight;
        private boolean mOutsideTouchable;
        private ViewEvent<V> mEvent;
        private Drawable mBackground;
        private boolean mFocusable;
        /**
         * 设置窗口弹出时背景的透明度
         * 0f(透明)~1.0f(正常)
         * 设置了alpha时需要在onDismiss恢复窗口的alpha至默认值即1.0f
         */
        private float alpha = 1.0f;
        /**
         * 结合showAtLocation使用精准定位,需设置mClippingEnabled为false,否则当内容过多时会移位,比如设置在某
         * 个控件底下内容过多时popupwindow会上移
         */
        private boolean mClippingEnabled = true;
        private OnShowBefore<V> mOnShowBefore;
        private OnShowAfter<V> mOnShowAfter;
        private int mAnimationStyle = -1;
        private OnDismissListener onDismissListener;

        public ViewBuilder<V> view(@NonNull V view) {
            this.view = view;
            return this;
        }

        public ViewBuilder<V> width(int width) {
            this.mWidth = width;
            return this;
        }

        public ViewBuilder<V> height(int height) {
            this.mHeight = height;
            return this;
        }

        public ViewBuilder<V> outsideTouchable(boolean outsideTouchable) {
            this.mOutsideTouchable = outsideTouchable;
            return this;
        }

        public ViewBuilder<V> backgroundDrawable(Drawable background) {
            this.mBackground = background;
            return this;
        }

        public ViewBuilder<V> focusable(boolean focusable) {
            this.mFocusable = focusable;
            return this;
        }

        public ViewBuilder<V> alpha(float alpha) {
            this.alpha = alpha;
            return this;
        }

        public ViewBuilder<V> clippingEnabled(boolean clippingEnabled) {
            this.mClippingEnabled = clippingEnabled;
            return this;
        }

        public ViewBuilder<V> onShowBefore(OnShowBefore<V> showBefore) {
            this.mOnShowBefore = showBefore;
            return this;
        }

        public ViewBuilder<V> onShowAfter(OnShowAfter<V> showAfter) {
            this.mOnShowAfter = showAfter;
            return this;
        }

        public ViewBuilder<V> animationStyle(int animationStyle) {
            this.mAnimationStyle = animationStyle;
            return this;
        }

        public ViewBuilder<V> intercept(ViewEvent<V> event) {
            this.mEvent = event;
            return this;
        }

        public ViewBuilder<V> onDismissListener(OnDismissListener onDismissListener) {
            this.onDismissListener = onDismissListener;
            return this;
        }

        public <T extends ViewDataBinding> CommonPopupWindow<V, T> build(Context context) {
            return new CommonPopupWindow<V, T>(context, this) {

                @Override
                public float getAlpha() {
                    return alpha;
                }

                @Override
                CommonPopupWindow getInstance() {
                    return this;
                }

                @Override
                InterceptTransform getIntercept() {
                    return new InterceptTransform<V>() {
                        @Override
                        public void showBefore() {
                            if (mOnShowBefore != null) {
                                mOnShowBefore.showBefore(getInstance(), view);
                            }
                        }

                        @Override
                        public void showAfter() {
                            if (mOnShowAfter != null) {
                                mOnShowAfter.showAfter(getInstance(), view);
                            }
                        }

                        @Override
                        public void intercept() {
                            if (mEvent != null) {
                                mEvent.getView(getInstance(), view);
                            }
                        }
                    };
                }
            };
        }
    }

    public interface ViewEvent<T> {
        void getView(CommonPopupWindow popupWindow, T view);
    }

    public static abstract class InterceptTransform<V> {
        public abstract void showBefore();

        public abstract void showAfter();

        public abstract void intercept();
    }

    public interface OnShowBefore<V> {
        void showBefore(CommonPopupWindow popupWindow, V view);
    }

    public interface OnShowAfter<V> {
        void showAfter(CommonPopupWindow popupWindow, V view);
    }
}

代码的注释比较清晰,就不做过多说明了,有疑问的朋友欢迎在评论区留言。

Android通用PopupWindow的封装_https://bianchenghao6.com/blog_Android_第3张

搞定,收工。

希望本文可以帮助到您,也希望各位不吝赐教,提出您在使用中的宝贵意见,谢谢。

今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。

发表回复