一、Builder设计模式的定义
将一个复杂对象的构建和它的表现分离,使得同样的构建过程,可以创建不同的表示。
建造步骤:
1. 明确建造的对象,如QuickDialog、QuickPopup
2. 确定建造者,如QuickBuild(里面包含建造参数QuickParams)
3. 一些View的辅助类(QuickViewHelper)
二、Android使用系统的Dialog
//1.得到对话框的构造器,可以构造对话的模版
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("标题");
builder.setMessage("包含内容");
//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.使用构造器创建一个对话框的对象
AlertDialog dialog = builder.create();
//5.显示对话框
dialog.show();
三、安际开发情况
由于每个APP的风格不一样,UI设计的弹窗五花八门,系统的Dialog用得不多。系统的Dialog定制性不强等等。
由于系统的Dialog有诸多的不便,自己写一个通用的可定制的Dialog就是必要的了。
四、Dialog都有哪些通用特性?
1.Dialog的显示动画、位置,常用的是由下面出来和中间出来。
2.Dialog的背景,包括背景色、背景圆角。
3.其它的一些设置,用Dialog系统有的就行了,比如取消监听等等
五、Dialog、PopupWindow建造类(QuickBuild ),主要是设置构建的一些参数。
/**
* Dialog、PopupWindow 构建器
*/
public class QuickBuild {
private Activity mActivity;
// 默认主题
private int mTheme = R.style.QuickDialogTheme;
// 构建Dialog的参数
private QuickParams mParams;
public QuickBuild(Activity activity) {
this.mActivity = activity;
mParams = new QuickParams(activity);
}
public static QuickBuild crate(Activity activity) {
return new QuickBuild(activity);
}
/**
* 设置主题
*/
public QuickBuild setTheme(int theme) {
this.mTheme = theme;
return this;
}
/**
* 设置宽度
*/
public QuickBuild setWidth(int width) {
mParams.mWidth = width;
return this;
}
/**
* 设置高度
*/
public QuickBuild setHeight(int height) {
mParams.mHeight = height;
return this;
}
/**
* 设置宽高
*/
public QuickBuild setWidthHeight(int width, int height) {
mParams.mWidth = width;
mParams.mHeight = height;
return this;
}
/**
* 设置布局ID
*/
public QuickBuild setContentView(int layoutId) {
mParams.mLayoutId = layoutId;
return this;
}
/**
* 设置布局View
*/
public QuickBuild setContentView(View view) {
mParams.mContentView = view;
return this;
}
/**
* 设置ContentView背景的颜色:默认白色
*/
public QuickBuild setBackgroundColor(int color) {
mParams.mBgColor = color;
return this;
}
/**
* 设置Window背景
*
* @param isSetBg false 就不设置默认圆角
*/
public QuickBuild isSetBackground(boolean isSetBg) {
mParams.mIsSetBg = isSetBg;
return this;
}
/**
* 设置ContentView背景的圆角:默认10dp
*
* @param radius 内部已转成dp
* @return
*/
public QuickBuild setBackgroundRadius(int radius) {
mParams.mBgRadius = radius;
return this;
}
/**
* 设置背景是否模糊:默认是模糊的
*
* @param isDimEnabled
* @return
*/
public QuickBuild isDimEnabled(boolean isDimEnabled) {
mParams.mIsDimEnabled = isDimEnabled;
return this;
}
/**
* 设置宽度占满的比例
*/
public QuickBuild setWidthScale(float scale) {
mParams.mScale = scale;
return this;
}
/**
* 设置宽度占满
*/
public QuickBuild setFullWidth() {
setWidthScale(1.0f);
return this;
}
/**
* 设置高度占满
*/
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;
}
/**
* 设置自定义的弹出动画
*/
public QuickBuild animation(int resId) {
mParams.mAnimation = resId;
return this;
}
/**
* 设置点击空白是否消失
*/
public QuickBuild cancelable(boolean cancelable) {
mParams.mCancelable = cancelable;
return this;
}
/**
* 设置取消的监听
*/
public QuickBuild setOnCancelListener(DialogInterface.OnCancelListener onCancelListener) {
mParams.mOnCancelListener = onCancelListener;
return this;
}
/**
* 设置Dialog消息的监听
*/
public QuickBuild setOnDismissListener(DialogInterface.OnDismissListener onDismissListener) {
mParams.mOnDismissListener = onDismissListener;
return this;
}
/**
* 设置按键的监听
*/
public QuickBuild setOnKeyListener(DialogInterface.OnKeyListener onKeyListener) {
mParams.mOnKeyListener = onKeyListener;
return this;
}
/**
* 根据布局ID设置文本信息
*/
public QuickBuild setText(int viewId, CharSequence text) {
mParams.setText(viewId, text);
return this;
}
/**
* 根据布局ID设置点击事件
*/
public QuickBuild setOnClickListener(int viewId, View.OnClickListener onClickListener) {
mParams.setOnClickListener(viewId, onClickListener);
return this;
}
/**
* 构建Dialog
*/
public QuickDialog build() {
QuickDialog dialog = new QuickDialog(mActivity, mTheme);
dialog.apply(mParams);
return dialog;
}
/**
* 构建并显示Dialog
*/
public QuickDialog show() {
QuickDialog dialog = build();
dialog.show();
return dialog;
}
/**
* 构建QuickPopup
*/
public QuickPopup buildPopup() {
QuickPopup popup = new QuickPopup(mActivity);
popup.apply(mParams);
return popup;
}
/**
* Dialog构建参数
*/
static class QuickParams {
// 存放文本的集合
SparseArray<CharSequence> mTextArray = new SparseArray<>();
// 存放点击事件的集合
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);
}
/**
* 根据布局ID设置文本信息
*/
void setText(int viewId, CharSequence text) {
mTextArray.put(viewId, text);
}
/**
* 根据布局ID设置点击事件
*/
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;
}
/**
* 设置文本
*/
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;
}
/**
* 设置点击事件
*/
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);
}
/**
* 根据布局ID设置文本信息
*/
public QuickDialog setText(int viewId, CharSequence text) {
if (viewHelper != null) {
viewHelper.setText(viewId, text);
}
return this;
}
/**
* 根据布局ID设置点击事件
*/
public QuickDialog setOnClickListener(int viewId, View.OnClickListener onClickListener) {
if (viewHelper != null) {
viewHelper.setOnClickListener(viewId, onClickListener);
}
return this;
}
@Override
public void dismiss() {
closeKeyboard();
super.dismiss();
}
/**
* 关闭软键盘
*/
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.设置布局
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("请调用setContentView方法设置布局");
}
View contentView = viewHelper.getContentView();
setContentView(contentView);
// 2.设置文本
int textSize = params.mTextArray.size();
for (int i = 0; i < textSize; i++) {
viewHelper.setText(params.mTextArray.keyAt(i), params.mTextArray.valueAt(i));
}
// 3.设置点击
int clickSize = params.mClickArray.size();
for (int i = 0; i < clickSize; i++) {
viewHelper.setOnClickListener(params.mClickArray.keyAt(i), params.mClickArray.valueAt(i));
}
// 4.设置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);
// 设置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);
}
// 设置背景是否模糊
if (!params.mIsDimEnabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
window.setDimAmount(0f);
}
}
// 设置外面能不能点击
setCancelable(params.mCancelable);
setCanceledOnTouchOutside(params.mCancelable);
// 设置事件监听
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;
}
/**
* 根据布局ID设置文本信息
*/
public QuickPopup setText(int viewId, CharSequence text) {
if (viewHelper != null) {
viewHelper.setText(viewId, text);
}
return this;
}
/**
* 根据布局ID设置点击事件
*/
public QuickPopup setOnClickListener(int viewId, View.OnClickListener onClickListener) {
if (viewHelper != null) {
viewHelper.setOnClickListener(viewId, onClickListener);
}
return this;
}
@Override
public void dismiss() {
// 关闭键盘
closeKeyboard();
// 背景还原
setWindowDim(mActivity, false);
super.dismiss();
}
/**
* 关闭软键盘
*/
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.设置布局
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("请调用setContentView方法设置布局");
}
View contentView = viewHelper.getContentView();
measureView(contentView);
setContentView(contentView);
// 设置宽高
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.设置文本
int textSize = params.mTextArray.size();
for (int i = 0; i < textSize; i++) {
viewHelper.setText(params.mTextArray.keyAt(i), params.mTextArray.valueAt(i));
}
// 3.设置点击
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默认显示的主题
<!--从下弹出来的基本动画-->
<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();
// 设置Dialog布局里面View的点击事件,文本设置一样
dialog.setOnClickListener(R.id.dialog_message, new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
dialog.show();