一、Builder設(shè)計(jì)模式的定義
將一個復(fù)雜對象的構(gòu)建和它的表現(xiàn)分離,使得同樣的構(gòu)建過程,可以創(chuàng)建不同的表示。
建造步驟:
1. 明確建造的對象,如QuickDialog、QuickPopup
2. 確定建造者,如QuickBuild(里面包含建造參數(shù)QuickParams)
3. 一些View的輔助類(QuickViewHelper)
二、Android使用系統(tǒng)的Dialog
//1.得到對話框的構(gòu)造器,可以構(gòu)造對話的模版
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("標(biāo)題");
builder.setMessage("包含內(nèi)容");
//2.添加一個確定按鈕
builder.setPositiveButton("確定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
//3.添加一個取消按鈕
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
//4.使用構(gòu)造器創(chuàng)建一個對話框的對象
AlertDialog dialog = builder.create();
//5.顯示對話框
dialog.show();
三、安際開發(fā)情況
由于每個APP的風(fēng)格不一樣,UI設(shè)計(jì)的彈窗五花八門,系統(tǒng)的Dialog用得不多。系統(tǒng)的Dialog定制性不強(qiáng)等等。
由于系統(tǒng)的Dialog有諸多的不便,自己寫一個通用的可定制的Dialog就是必要的了。
四、Dialog都有哪些通用特性?
1.Dialog的顯示動畫、位置,常用的是由下面出來和中間出來。
2.Dialog的背景,包括背景色、背景圓角。
3.其它的一些設(shè)置,用Dialog系統(tǒng)有的就行了,比如取消監(jiān)聽等等
五、Dialog、PopupWindow建造類(QuickBuild ),主要是設(shè)置構(gòu)建的一些參數(shù)。
/**
* Dialog、PopupWindow 構(gòu)建器
*/
public class QuickBuild {
private Activity mActivity;
// 默認(rèn)主題
private int mTheme = R.style.QuickDialogTheme;
// 構(gòu)建Dialog的參數(shù)
private QuickParams mParams;
public QuickBuild(Activity activity) {
this.mActivity = activity;
mParams = new QuickParams(activity);
}
public static QuickBuild crate(Activity activity) {
return new QuickBuild(activity);
}
/**
* 設(shè)置主題
*/
public QuickBuild setTheme(int theme) {
this.mTheme = theme;
return this;
}
/**
* 設(shè)置寬度
*/
public QuickBuild setWidth(int width) {
mParams.mWidth = width;
return this;
}
/**
* 設(shè)置高度
*/
public QuickBuild setHeight(int height) {
mParams.mHeight = height;
return this;
}
/**
* 設(shè)置寬高
*/
public QuickBuild setWidthHeight(int width, int height) {
mParams.mWidth = width;
mParams.mHeight = height;
return this;
}
/**
* 設(shè)置布局ID
*/
public QuickBuild setContentView(int layoutId) {
mParams.mLayoutId = layoutId;
return this;
}
/**
* 設(shè)置布局View
*/
public QuickBuild setContentView(View view) {
mParams.mContentView = view;
return this;
}
/**
* 設(shè)置ContentView背景的顏色:默認(rèn)白色
*/
public QuickBuild setBackgroundColor(int color) {
mParams.mBgColor = color;
return this;
}
/**
* 設(shè)置Window背景
*
* @param isSetBg false 就不設(shè)置默認(rèn)圓角
*/
public QuickBuild isSetBackground(boolean isSetBg) {
mParams.mIsSetBg = isSetBg;
return this;
}
/**
* 設(shè)置ContentView背景的圓角:默認(rèn)10dp
*
* @param radius 內(nèi)部已轉(zhuǎn)成dp
* @return
*/
public QuickBuild setBackgroundRadius(int radius) {
mParams.mBgRadius = radius;
return this;
}
/**
* 設(shè)置背景是否模糊:默認(rèn)是模糊的
*
* @param isDimEnabled
* @return
*/
public QuickBuild isDimEnabled(boolean isDimEnabled) {
mParams.mIsDimEnabled = isDimEnabled;
return this;
}
/**
* 設(shè)置寬度占滿的比例
*/
public QuickBuild setWidthScale(float scale) {
mParams.mScale = scale;
return this;
}
/**
* 設(shè)置寬度占滿
*/
public QuickBuild setFullWidth() {
setWidthScale(1.0f);
return this;
}
/**
* 設(shè)置高度占滿
*/
public QuickBuild setFullHeight() {
mParams.mIsHeightFull = true;
mParams.mHeight = ViewGroup.LayoutParams.MATCH_PARENT;
return this;
}
/**
* 從底部彈出
*/
public QuickBuild fromBottom(boolean isAnim) {
if (isAnim) {
mParams.mAnimation = R.style.Anim_Dialog_Bottom;
}
mParams.mGravity = Gravity.BOTTOM;
return this;
}
/**
* 從頂部彈出
*/
public QuickBuild fromTop(boolean isAnim) {
if (isAnim) {
mParams.mAnimation = R.style.Anim_Dialog_Top;
}
mParams.mGravity = Gravity.TOP;
return this;
}
/**
* 設(shè)置自定義的彈出動畫
*/
public QuickBuild animation(int resId) {
mParams.mAnimation = resId;
return this;
}
/**
* 設(shè)置點(diǎn)擊空白是否消失
*/
public QuickBuild cancelable(boolean cancelable) {
mParams.mCancelable = cancelable;
return this;
}
/**
* 設(shè)置取消的監(jiān)聽
*/
public QuickBuild setOnCancelListener(DialogInterface.OnCancelListener onCancelListener) {
mParams.mOnCancelListener = onCancelListener;
return this;
}
/**
* 設(shè)置Dialog消息的監(jiān)聽
*/
public QuickBuild setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
mParams.mOnDismissListener = onDismissListener;
return this;
}
/**
* 設(shè)置按鍵的監(jiān)聽
*/
public QuickBuild setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) {
mParams.mOnKeyListener = onKeyListener;
return this;
}
/**
* 根據(jù)布局ID設(shè)置文本信息
*/
public QuickBuild setText(int viewId, CharSequence text) {
mParams.setText(viewId, text);
return this;
}
/**
* 根據(jù)布局ID設(shè)置點(diǎn)擊事件
*/
public QuickBuild setOnClickListener(int viewId, View.OnClickListener onClickListener) {
mParams.setOnClickListener(viewId, onClickListener);
return this;
}
/**
* 構(gòu)建Dialog
*/
public QuickDialog build() {
QuickDialog dialog = new QuickDialog(mActivity, mTheme);
dialog.apply(mParams);
return dialog;
}
/**
* 構(gòu)建并顯示Dialog
*/
public QuickDialog show() {
QuickDialog dialog = build();
dialog.show();
return dialog;
}
/**
* 構(gòu)建QuickPopup
*/
public QuickPopup buildPopup() {
QuickPopup popup = new QuickPopup(mActivity);
popup.apply(mParams);
return popup;
}
/**
* Dialog構(gòu)建參數(shù)
*/
static class QuickParams {
// 存放文本的集合
SparseArray<CharSequence> mTextArray = new SparseArray<>();
// 存放點(diǎn)擊事件的集合
SparseArray<View.OnClickListener> mClickArray = new SparseArray<>();
DialogInterface.OnCancelListener mOnCancelListener;
DialogInterface.OnDismissListener mOnDismissListener;
DialogInterface.OnKeyListener mOnKeyListener;
View mContentView;
int mLayoutId;
int mWidth;
int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
int mGravity = Gravity.CENTER;
int mAnimation;
float mScale = 0.75f;
int mBgRadius = 5;
int mBgColor = Color.parseColor("#ffffff");
boolean mIsSetBg = true;
boolean mIsDimEnabled = true;
boolean mCancelable = true;
//
boolean mIsHeightFull = false;
QuickParams(Context context) {
DisplayMetrics dm = context.getResources().getDisplayMetrics();
mWidth = (int) Math.floor(dm.widthPixels);
}
/**
* 根據(jù)布局ID設(shè)置文本信息
*/
void setText(int viewId, CharSequence text) {
mTextArray.put(viewId, text);
}
/**
* 根據(jù)布局ID設(shè)置點(diǎn)擊事件
*/
void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
mClickArray.put(viewId, onClickListener);
}
}
}
六、View的輔助類,主要是加載View布局和找到里面的View等
/**
* View顯示的輔助類
*/
public class QuickViewHelper {
private View mContentView;
private SparseArray<WeakReference<View>> mViews = new SparseArray<>();
public QuickViewHelper(Context context, int layoutResId) {
mContentView = LayoutInflater.from(context).inflate(layoutResId, null, false);
}
public QuickViewHelper(View mView) {
mContentView = mView;
}
public View getContentView() {
return mContentView;
}
/**
* 設(shè)置文本
*/
public void setText(int viewId, CharSequence charSequence) {
TextView tv = getView(viewId);
if (tv != null && charSequence != null) {
if (TextUtils.isEmpty(charSequence)) {
tv.setText("");
return;
}
tv.setText(charSequence);
}
}
public <T extends View> T getView(int viewId) {
WeakReference<View> viewWeakReference = mViews.get(viewId);
View view = null;
if (viewWeakReference == null) {
view = mContentView.findViewById(viewId);
if (view != null) {
mViews.put(viewId, new WeakReference<>(view));
}
} else {
view = viewWeakReference.get();
}
return (T) view;
}
/**
* 設(shè)置點(diǎn)擊事件
*/
public void setOnClickListener(int viewId, View.OnClickListener onClickListener) {
View view = getView(viewId);
if (view != null && onClickListener != null) {
view.setOnClickListener(onClickListener);
}
}
}
七、自定義通用的Dialog
/**
* 自定義的Dialog
*/
public class QuickDialog extends Dialog {
private QuickViewHelper viewHelper;
QuickDialog(@NonNull Context context, int themeResId) {
super(context, themeResId);
}
/**
* 根據(jù)布局ID設(shè)置文本信息
*/
public QuickDialog setText(int viewId, CharSequence text) {
if (viewHelper != null) {
viewHelper.setText(viewId, text);
}
return this;
}
/**
* 根據(jù)布局ID設(shè)置點(diǎn)擊事件
*/
public QuickDialog setOnClickListener(int viewId, View.OnClickListener onClickListener) {
if (viewHelper != null) {
viewHelper.setOnClickListener(viewId, onClickListener);
}
return this;
}
@Override
public void dismiss() {
closeKeyboard();
super.dismiss();
}
/**
* 關(guān)閉軟鍵盤
*/
private void closeKeyboard() {
View view = getCurrentFocus();
if (view instanceof TextView) {
InputMethodManager mInputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
mInputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
}
}
public void apply(QuickBuild.QuickParams params) {
// 1.設(shè)置布局
if (params.mLayoutId != 0) {
viewHelper = new QuickViewHelper(getContext(), params.mLayoutId);
}
if (params.mContentView != null) {
viewHelper = new QuickViewHelper(params.mContentView);
}
if (viewHelper == null) {
throw new IllegalArgumentException("請調(diào)用setContentView方法設(shè)置布局");
}
View contentView = viewHelper.getContentView();
setContentView(contentView);
// 2.設(shè)置文本
int textSize = params.mTextArray.size();
for (int i = 0; i < textSize; i++) {
viewHelper.setText(params.mTextArray.keyAt(i), params.mTextArray.valueAt(i));
}
// 3.設(shè)置點(diǎn)擊
int clickSize = params.mClickArray.size();
for (int i = 0; i < clickSize; i++) {
viewHelper.setOnClickListener(params.mClickArray.keyAt(i), params.mClickArray.valueAt(i));
}
// 4.設(shè)置Window
Window window = getWindow();
// 位置
window.setGravity(params.mGravity);
// 動畫
if (params.mAnimation != 0) {
window.setWindowAnimations(params.mAnimation);
}
// 寬高
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = (int) (params.mWidth * params.mScale);
lp.height = params.mHeight;
window.setAttributes(lp);
// 設(shè)置Window背景
if (params.mIsSetBg) {
final GradientDrawable bg = new GradientDrawable();
int radius = dp2px(getContext(), params.mBgRadius);
// 1 2 3 4(順時針)
bg.setCornerRadii(new float[]{radius, radius, radius, radius, radius, radius, radius, radius});
bg.setColor(params.mBgColor);
window.setBackgroundDrawable(bg);
}
// 設(shè)置背景是否模糊
if (!params.mIsDimEnabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
window.setDimAmount(0f);
}
}
// 設(shè)置外面能不能點(diǎn)擊
setCancelable(params.mCancelable);
setCanceledOnTouchOutside(params.mCancelable);
// 設(shè)置事件監(jiān)聽
setOnCancelListener(params.mOnCancelListener);
setOnDismissListener(params.mOnDismissListener);
setOnKeyListener(params.mOnKeyListener);
}
public static int dp2px(Context context, float dp) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}
八、 自定義PopupWindow
/**
* 自定義的PopupWindow
*/
public class QuickPopup extends PopupWindow {
private QuickViewHelper viewHelper;
private Activity mActivity;
public QuickPopup(Activity activity) {
super(activity);
this.mActivity = activity;
}
/**
* 根據(jù)布局ID設(shè)置文本信息
*/
public QuickPopup setText(int viewId, CharSequence text) {
if (viewHelper != null) {
viewHelper.setText(viewId, text);
}
return this;
}
/**
* 根據(jù)布局ID設(shè)置點(diǎn)擊事件
*/
public QuickPopup setOnClickListener(int viewId, View.OnClickListener onClickListener) {
if (viewHelper != null) {
viewHelper.setOnClickListener(viewId, onClickListener);
}
return this;
}
@Override
public void dismiss() {
// 關(guān)閉鍵盤
closeKeyboard();
// 背景還原
setWindowDim(mActivity, false);
super.dismiss();
}
/**
* 關(guān)閉軟鍵盤
*/
private void closeKeyboard() {
if (mActivity == null) {
return;
}
View view = mActivity.getCurrentFocus();
if (view instanceof TextView) {
InputMethodManager mInputMethodManager = (InputMethodManager) mActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
mInputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);
}
}
public void apply(QuickBuild.QuickParams params) {
// 1.設(shè)置布局
if (params.mLayoutId != 0) {
viewHelper = new QuickViewHelper(mActivity, params.mLayoutId);
}
if (params.mContentView != null) {
viewHelper = new QuickViewHelper(params.mContentView);
}
if (viewHelper == null) {
throw new IllegalArgumentException("請調(diào)用setContentView方法設(shè)置布局");
}
View contentView = viewHelper.getContentView();
measureView(contentView);
setContentView(contentView);
// 設(shè)置寬高
if (params.mWidth > 0) {
setWidth(params.mWidth);
} else {
setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
}
if (params.mHeight > 0) {
setHeight(params.mHeight);
} else {
setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
}
if (params.mIsHeightFull) {
setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
}
setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
setOutsideTouchable(params.mCancelable);
setFocusable(params.mCancelable);
// 動畫
if (params.mAnimation != 0) {
setAnimationStyle(params.mAnimation);
}
// 2.設(shè)置文本
int textSize = params.mTextArray.size();
for (int i = 0; i < textSize; i++) {
viewHelper.setText(params.mTextArray.keyAt(i), params.mTextArray.valueAt(i));
}
// 3.設(shè)置點(diǎn)擊
int clickSize = params.mClickArray.size();
for (int i = 0; i < clickSize; i++) {
viewHelper.setOnClickListener(params.mClickArray.keyAt(i), params.mClickArray.valueAt(i));
}
// 背景模糊
if (params.mIsDimEnabled) {
setWindowDim(mActivity, true);
}
}
/**
* 測量View的寬高
*
* @param view View
*/
private void measureView(View view) {
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
view.measure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 給整個屏幕添加陰影背景
*
* @param activity
* @param isDim TRUE 添加 false 不添加
*/
public void setWindowDim(Activity activity, boolean isDim) {
if (null != activity) {
WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
lp.alpha = isDim ? .7f : 1.0f;
if (isDim) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
} else {
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
activity.getWindow().setAttributes(lp);
}
}
}
七、Dialog默認(rèn)顯示的主題
<!--從下彈出來的基本動畫-->
<style name="Anim_Dialog_Bottom" parent="@style/Theme.AppCompat.Dialog">
<item name="android:windowEnterAnimation">@anim/slide_in</item>
<item name="android:windowExitAnimation">@anim/slide_out</item>
</style>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--slide_in-->
<translate
android:duration="250"
android:fromXDelta="0"
android:fromYDelta="100%"
android:toXDelta="0"
android:toYDelta="0" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<!--slide_out-->
<translate
android:duration="250"
android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="100%" />
</set>
八、Dialog的使用
QuickDialog dialog = QuickBuild.create(this)
.setContentView(R.layout.dialog_loading)
.fromBottom(true)
.setCancelable(false)
.setIsDimEnabled(false)
.build();
// 設(shè)置Dialog布局里面View的點(diǎn)擊事件,文本設(shè)置一樣
dialog.setOnClickListener(R.id.dialog_message, new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
dialog.show();