日常開發中或多或少都會使用到Dialog,每次都需要自定義繼承Dialog,寫多了不勝其煩,今天我們對Dialog做個封裝,可通過鍊式建構,以ViewDataBinding方式設定ContentView(之後會擴充支援通過View方式設定ContentView,類似的封裝可看我的另外一篇文章Android通用PopupWindow的封裝)。
先看使用示例:
這裡的layout_sorting_filter是我們自定義View的xml檔案,LayoutSortingFilterBinding是對應的ViewDataBinding
GeneralDialog mGeneralDialog = new GeneralDialog.Builder<LayoutSortingFilterBinding>()
.layoutId(R.layout.layout_sorting_filter)
.intercept(new GeneralDialog.GeneralDialogEvent<LayoutSortingFilterBinding>() {
@Override
public void getView(LayoutSortingFilterBinding mBinding) {
/*在這裡可做初始化相關操作*/
}
})
.width(WindowManager.LayoutParams.MATCH_PARENT)
.height(WindowManager.LayoutParams.WRAP_CONTENT)
.setCanceledOnTouchOutside(true)
.gravity(Gravity.BOTTOM)
.addClickViews(R.id.tv_cancel, R.id.tv_sure)
.callbackClickOn(new GeneralDialog.OnClickCallback() {
@Override
public void onClick(GeneralDialog generalDialog, View view) {
int id = view.getId();
if (id == R.id.tv_cancel) {
/*點選了取消*/
} else if (id == R.id.tv_sure) {
/*點選了确定*/
}
generalDialog.dismiss();
}
}).Build(this);
Window mWindow = mGeneralDialog.getWindow();
/*設定視窗出現和消失的動畫*/
if (mWindow != null) {
mWindow.setWindowAnimations(R.style.anim_slide);
}
mGeneralDialog.show();
addClickViews方法是設定點選事件監聽的,支援設定多個View,如僅需設定一個也可通過方法addClickView(int id),如:
GeneralDialog mGeneralDialog = new GeneralDialog.Builder<LayoutSortingFilterBinding>()
.layoutId(R.layout.layout_sorting_filter)
.intercept(new GeneralDialog.GeneralDialogEvent<LayoutSortingFilterBinding>() {
@Override
public void getView(LayoutSortingFilterBinding mBinding) {
/*在這裡可做初始化相關操作*/
}
})
.width(WindowManager.LayoutParams.MATCH_PARENT)
.height(WindowManager.LayoutParams.WRAP_CONTENT)
.setCanceledOnTouchOutside(true)
.gravity(Gravity.BOTTOM)
.addClickView(R.id.tv_sure)
.callbackClickOn(new GeneralDialog.OnClickCallback() {
@Override
public void onClick(GeneralDialog generalDialog, View view) {
/*點選了确定*/
generalDialog.dismiss();
}
}).Build(this);
Window mWindow = mGeneralDialog.getWindow();
/*設定視窗出現和消失的動畫*/
if (mWindow != null) {
mWindow.setWindowAnimations(R.style.anim_slide);
}
mGeneralDialog.show();
GeneralDialog完整的代碼如下:
import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import java.util.ArrayList;
/**
* 作者:可口可樂的可樂 on 2019/9/02 14:45
* Email : [email protected]
* Describe:通用Dialog(用于各種提示或對話框形式的操作)
* update on 2020/3/02 16:52
*/
public class GeneralDialog<T extends ViewDataBinding> extends Dialog implements View.OnClickListener {
/**
* Dialog layout視圖
*/
private T mBinding;
/**
* 點選事件監聽(不帶layout視圖)
*/
private OnClickCallback mClickEvent;
/**
* 點選事件監聽(帶layout視圖)
*/
private OnBindingClickCallback<T> mBindingClickEvent;
@SuppressWarnings("unchecked")
private GeneralDialog(Context context, Builder builder) {
this(context);
mBinding = DataBindingUtil.inflate(getLayoutInflater(), builder.layoutId, null, false);
setContentView(mBinding.getRoot());
initSetting(builder.mDialogSet, builder.mCancelable, builder.canceledOnTouchOutside);
intercept(builder.event);
setClickEvent(builder.clickViews);
mClickEvent = builder.mClickEvent;
mBindingClickEvent = builder.mBindingClickEvent;
}
private GeneralDialog(@NonNull Context context) {
super(context, R.style.DarkBackgroundDialog);
}
/**
* 窗體的一些設定
*
* @param mDialogSet 窗體的設定類
* @param mCancelable 是否可取消(按實體傳回鍵)
* @param canceledOnTouchOutside 是否可點選外部取消
**/
private void initSetting(DialogSet mDialogSet, boolean mCancelable, boolean canceledOnTouchOutside) {
Window window = getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
if (mDialogSet != null) {
lp.width = mDialogSet.width;
lp.height = mDialogSet.height;
lp.gravity = mDialogSet.gravity;
setCancelable(mDialogSet.mCancelable);
setCanceledOnTouchOutside(mDialogSet.canceledOnTouchOutside);
} else {
lp.width = getContext().getResources().getDimensionPixelOffset(R.dimen.x812);
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.CENTER;
setCancelable(mCancelable);
setCanceledOnTouchOutside(canceledOnTouchOutside);
}
window.setAttributes(lp);
}
/**
* 設定點選事件
*
* @param clickViews 需要設定點選事件監聽的View的id集合
*/
private void setClickEvent(ArrayList<Integer> clickViews) {
if (clickViews != null && mBinding != null) {
for (int id : clickViews) {
if (mBinding.getRoot().findViewById(id) != null) {
mBinding.getRoot().findViewById(id).setOnClickListener(this);
}
}
}
}
/**
* 攔截,可根據傳回的ViewDataBinding實作需要的邏輯
*
* @param event 回調事件,可傳回目前Dialog視圖
*/
private void intercept(GeneralDialogEvent<T> event) {
if (event != null && mBinding != null) {
event.getView(mBinding);
}
}
/**
* 設定點選事件回調
*
* @param event 回調事件
*/
private void clickCallback(OnClickCallback event) {
if (event != null && mBinding != null) {
mClickEvent = event;
}
}
/**
* 設定點選事件回調
*
* @param event 回調事件
*/
private void bindingClickCallback(OnBindingClickCallback<T> event) {
if (event != null && mBinding != null) {
mBindingClickEvent = event;
}
}
@Override
public void onClick(View v) {
if (mClickEvent != null) {
mClickEvent.onClick(this, v);
}
if (mBindingClickEvent != null) {
mBindingClickEvent.onClick(this, mBinding, v);
}
}
public interface GeneralDialogEvent<T> {
/**
* 傳回Dialog的layout視圖(DataBinding類型)
*/
void getView(T mBinding);
}
public interface OnClickCallback {
/**
* 點選事件回調
*
* @param dialog dialog本身
* @param v 産生點選事件的View
*/
void onClick(GeneralDialog dialog, View v);
}
public interface OnBindingClickCallback<T> {
/**
* 點選事件回調
*
* @param dialog dialog本身
* @param mBinding dialog的layout視圖(DataBinding類型)
* @param v 産生點選事件的View
*/
void onClick(GeneralDialog dialog, T mBinding, View v);
}
public static class Builder<T extends ViewDataBinding> {
/**
* Dialog的布局檔案id
*/
private int layoutId;
/**
* 用于傳回Dialog的layout視圖(DataBinding類型)
*/
private GeneralDialogEvent event;
/**
* 點選事件監聽(不帶layout視圖)
*/
private OnClickCallback mClickEvent;
/**
* 點選事件監聽(帶layout視圖)
*/
private OnBindingClickCallback mBindingClickEvent;
/**
* 設定此對話框在觸摸視窗外時是否被取消,優先級比cancel高
*/
private DialogSet mDialogSet;
/**
* 設定此對話框在觸摸視窗外時是否被取消,優先級比mDialogSet低
*/
private boolean canceledOnTouchOutside = true;
/**
* 設定此對話框在能否被按傳回鍵銷毀,優先級比mDialogSet低
*/
private boolean mCancelable;
/**
* 存儲所有的帶點選事件的View id集合
*/
private ArrayList<Integer> clickViews;
/**
* 設定Dialog的布局檔案id
*
* @param layoutId 布局檔案id
*/
public Builder layoutId(int layoutId) {
this.layoutId = layoutId;
return this;
}
/**
* 設定Dialog的寬
*
* @param width 寬
*/
public Builder width(int width) {
if (mDialogSet == null) {
mDialogSet = new DialogSet();
}
mDialogSet.width = width;
return this;
}
/**
* 設定Dialog的高
*
* @param height 高
*/
public Builder height(int height) {
if (mDialogSet == null) {
mDialogSet = new DialogSet();
}
mDialogSet.height = height;
return this;
}
/**
* 設定Dialog的對齊方式
*
* @param gravity 對齊方式
*/
public Builder gravity(int gravity) {
if (mDialogSet == null) {
mDialogSet = new DialogSet();
}
mDialogSet.gravity = gravity;
return this;
}
/**
* 設定Dialog點選外部是否可以取消
*
* @param cancel 是否可取消
*/
public Builder setCanceledOnTouchOutside(boolean cancel) {
if (mDialogSet == null) {
mDialogSet = new DialogSet();
}
mDialogSet.canceledOnTouchOutside = cancel;
this.canceledOnTouchOutside = cancel;
return this;
}
/**
* 設定Dialog是否可以取消(按實體傳回鍵)
*
* @param cancel 是否可取消
*/
public Builder setCancelable(boolean cancel) {
if (mDialogSet == null) {
mDialogSet = new DialogSet();
}
mDialogSet.mCancelable = cancel;
this.mCancelable = cancel;
if (!cancel) {
/*this.canceledOnTouchOutside預設為true,當設定mCancelable為false時,設定canceledOnTouchOutside會影響
* mCancelable的值,是以如果設定了mCancelable為false,則canceledOnTouchOutside先要設定為false
* */
mDialogSet.canceledOnTouchOutside = false;
this.canceledOnTouchOutside = false;
}
return this;
}
/**
* 攔截事件
*/
public Builder intercept(GeneralDialogEvent event) {
this.event = event;
return this;
}
/**
* 點選事件回調
*/
public Builder callbackClickOn(OnClickCallback event) {
this.mClickEvent = event;
return this;
}
/**
* 點選事件回調
*/
public Builder bindingCallbackClickOn(OnBindingClickCallback event) {
this.mBindingClickEvent = event;
return this;
}
/**
* 增加點選事件
*
* @param id 點選監聽的View的id
*/
public Builder addClickView(int id) {
if (clickViews == null) {
clickViews = new ArrayList<>();
}
clickViews.add(id);
return this;
}
/**
* 增加點選事件
*
* @param ids 點選監聽的Views的id
*/
public Builder addClickViews(int... ids) {
if (ids != null && ids.length > 0) {
if (clickViews == null) {
clickViews = new ArrayList<>();
}
for (int id : ids) {
clickViews.add(id);
}
}
return this;
}
/**
* 傳回帶DialogSet的GeneralDialog
*
* @param context 上下文
*/
public GeneralDialog Build(Context context) {
if (mDialogSet == null) {
throw new RuntimeException("未設定窗體大小和位置資訊");
}
return new GeneralDialog<T>(context, this);
}
/**
* 傳回不帶DialogSet的GeneralDialog
*
* @param context 上下文
*/
public GeneralDialog getDefault(Context context) {
if (mDialogSet != null) {
mDialogSet = null;
}
return new GeneralDialog<T>(context, this);
}
}
/**
* 窗體設定類
*/
public static class DialogSet {
/**
* Dialog的寬
*/
private int width;
/**
* Dialog的高
*/
private int height;
/**
* Dialog對齊模式
*/
private int gravity;
/**
* 點選外部是否能取消
*/
boolean canceledOnTouchOutside;
/**
* 是否能取消
*/
boolean mCancelable;
}
}
用到的R.style.DarkBackgroundDialog對應如下:
<resources>
<style name="DarkBackgroundDialog" parent="@android:style/Theme.Dialog">
<!-- 是否漂現在activity上 -->
<item name="android:windowIsFloating">true</item>
<!-- 是否隐藏标題 -->
<item name="android:windowNoTitle">true</item>
<!-- 是否允許背景變暗 -->
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
</resources>
代碼的注釋比較清晰,就不做過多說明了,有疑問的朋友歡迎在評論區留言。
搞定,收工。
希望本文可以幫助到您,也希望各位不吝賜教,提出您在使用中的寶貴意見,謝謝。
如果可以的話,也可以掃一掃下方的二維碼請作者喝一杯奶茶哈
謝謝您的觀看。
有問題可發送至:[email protected]
歡迎交流,共同進步。