Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说Android通用PopupWindow的封装,希望能够帮助你!!!。
日常开发中或多或少都会使用到PopupWindow,每次都需要自定义继承至PopupWindow的view,写多了不胜其烦,今天我们对PopupWindow做个封装,可通过链式构建,支持View方式或ViewDataBinding方式设置ContentView(类似的封装可看我的另外一篇文章Android通用Dialog的封装)。
先看使用示例:
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);
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);
}
}
代码的注释比较清晰,就不做过多说明了,有疑问的朋友欢迎在评论区留言。
搞定,收工。
希望本文可以帮助到您,也希望各位不吝赐教,提出您在使用中的宝贵意见,谢谢。
今天的分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。