Android通用PopupWindow的封裝

日常開發中或多或少都會使用到PopupWindow,每次都需要自定義繼承至PopupWindow的view,寫多了不勝其煩,今天我們對PopupWindow做個封裝,可透過鏈式構建,支援View方式或ViewDataBinding方式設定ContentView(類似的封裝可看我的另外一篇文章Android通用Dialog的封裝)。

Android通用PopupWindow的封裝

先看使用示例:

1、透過View的方式構建,這裡的SortingFilterLayout是我們自定義的一個View

new CommonPopupWindow。ViewBuilder() 。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() { @Override public void getView(CommonPopupWindow commonPopupWindow, SortingFilterLayout view) { /*在這裡可做初始化相關操作*/ } }) 。onShowBefore(new CommonPopupWindow。OnShowBefore() { @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() 。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() { @Override public void getView(CommonPopupWindow commonPopupWindow, LayoutSortingFilterBinding binding) { /*在這裡可做初始化相關操作*/ } }) 。onShowBefore(new CommonPopupWindow。OnShowBefore() { @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的封裝

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 extends PopupWindow { private OnDismissListener onDismissListener; private CommonPopupWindow(Context context, ViewDataBindingBuilder 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 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 { private Binding mBinding; private int mWidth; private int mHeight; private boolean mOutsideTouchable; private ViewEvent 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 mOnShowBefore; private OnShowAfter mOnShowAfter; private int mAnimationStyle = -1; private OnDismissListener onDismissListener; public ViewDataBindingBuilder layoutId(Context context, int layoutId) { this。mBinding = DataBindingUtil。inflate(LayoutInflater。from(context), layoutId, null, false); return this; } public ViewDataBindingBuilder viewDataBinding(Binding mBinding) { this。mBinding = mBinding; return this; } public ViewDataBindingBuilder width(int width) { this。mWidth = width; return this; } public ViewDataBindingBuilder height(int height) { this。mHeight = height; return this; } public ViewDataBindingBuilder outsideTouchable(boolean outsideTouchable) { this。mOutsideTouchable = outsideTouchable; return this; } public ViewDataBindingBuilder backgroundDrawable(Drawable background) { this。mBackground = background; return this; } public ViewDataBindingBuilder focusable(boolean focusable) { this。mFocusable = focusable; return this; } public ViewDataBindingBuilder alpha(float alpha) { this。alpha = alpha; return this; } public ViewDataBindingBuilder clippingEnabled(boolean clippingEnabled) { this。mClippingEnabled = clippingEnabled; return this; } public ViewDataBindingBuilder onShowBefore(OnShowBefore showBefore) { this。mOnShowBefore = showBefore; return this; } public ViewDataBindingBuilder onShowAfter(OnShowAfter showAfter) { this。mOnShowAfter = showAfter; return this; } public ViewDataBindingBuilder animationStyle(int animationStyle) { this。mAnimationStyle = animationStyle; return this; } public ViewDataBindingBuilder intercept(ViewEvent event) { this。mEvent = event; return this; } public ViewDataBindingBuilder onDismissListener(OnDismissListener onDismissListener) { this。onDismissListener = onDismissListener; return this; } public CommonPopupWindow build(Context context) { return new CommonPopupWindow(context, this) { @Override public float getAlpha() { return alpha; } @Override CommonPopupWindow getInstance() { return this; } @Override InterceptTransform getIntercept() { return new InterceptTransform() { @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 { private V view; private int mWidth; private int mHeight; private boolean mOutsideTouchable; private ViewEvent 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 mOnShowBefore; private OnShowAfter mOnShowAfter; private int mAnimationStyle = -1; private OnDismissListener onDismissListener; public ViewBuilder view(@NonNull V view) { this。view = view; return this; } public ViewBuilder width(int width) { this。mWidth = width; return this; } public ViewBuilder height(int height) { this。mHeight = height; return this; } public ViewBuilder outsideTouchable(boolean outsideTouchable) { this。mOutsideTouchable = outsideTouchable; return this; } public ViewBuilder backgroundDrawable(Drawable background) { this。mBackground = background; return this; } public ViewBuilder focusable(boolean focusable) { this。mFocusable = focusable; return this; } public ViewBuilder alpha(float alpha) { this。alpha = alpha; return this; } public ViewBuilder clippingEnabled(boolean clippingEnabled) { this。mClippingEnabled = clippingEnabled; return this; } public ViewBuilder onShowBefore(OnShowBefore showBefore) { this。mOnShowBefore = showBefore; return this; } public ViewBuilder onShowAfter(OnShowAfter showAfter) { this。mOnShowAfter = showAfter; return this; } public ViewBuilder animationStyle(int animationStyle) { this。mAnimationStyle = animationStyle; return this; } public ViewBuilder intercept(ViewEvent event) { this。mEvent = event; return this; } public ViewBuilder onDismissListener(OnDismissListener onDismissListener) { this。onDismissListener = onDismissListener; return this; } public CommonPopupWindow build(Context context) { return new CommonPopupWindow(context, this) { @Override public float getAlpha() { return alpha; } @Override CommonPopupWindow getInstance() { return this; } @Override InterceptTransform getIntercept() { return new InterceptTransform() { @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 { void getView(CommonPopupWindow popupWindow, T view); } public static abstract class InterceptTransform { public abstract void showBefore(); public abstract void showAfter(); public abstract void intercept(); } public interface OnShowBefore { void showBefore(CommonPopupWindow popupWindow, V view); } public interface OnShowAfter { void showAfter(CommonPopupWindow popupWindow, V view); }}

程式碼的註釋比較清晰,就不做過多說明了,有疑問的朋友歡迎在評論區留言。

Android通用PopupWindow的封裝

搞定,收工。

希望本文可以幫助到您,也希望各位不吝賜教,提出您在使用中的寶貴意見,謝謝。