一. Builder设计模式
定义
将一个复杂对象的构建与它的表示分离,使得不同的构建过程可以创建不同的显示,但其根本还是不变。
使用场景
1、相同的方法,不同的执行顺序,产生不同的事件结果时;
2、多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时;
3、产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个时候使用建造者模式非常合适。
UML图
介绍
Product 产品类 : 产品的抽象类;
Builder : 抽象类, 规范产品的组建,一般是由子类实现具体的组件过程;
ConcreteBuilder : 具体的构建器;
Director : 统一组装过程(可省略)。
Builder设计模式的工作流程
添加参数 (P) –> 组装参数 –> 显示
二. 使用Builder设计模式构建万能Dialog
主要的对象
AlertDialog (电脑对象)
AlertDialog.Builder 规范一系列的组装过程
AlertController 具体的构建器
AlertController.AlertParams 存放参数 , 一部分设置参数的功能
DialogViewHelper Dialog View 的辅助处理类
效果视图
实现的功能,基本囊括一般开发的需求
自定义布局
自定义弹窗宽高
设置弹窗位置
根据id设置控件的文本
根据id设置控件的点击事件
设置弹出动画效果,有默认动画
具体实现
DialogViewHelper.java
/** * @creation_time: 2017/6/26 * @author: Vegen * @e-mail: vegenhu@163.com * @description: Dialog View 的辅助处理类 */class DialogViewHelper { private View mContentView = null; // WeakReference软引用,防止霸气侧漏 private SparseArray<WeakReference<View>> mViews; public DialogViewHelper(Context mContext, int mViewLayoutResId) { this(); // 很重要,没有就报mViews空指针 mContentView = LayoutInflater.from(mContext).inflate(mViewLayoutResId, null); } public DialogViewHelper() { mViews = new SparseArray<>(); } /** * 设置布局 * @param contentView */ public void setContentView(View contentView) { this.mContentView = contentView; } /** * 设置文本 * @param viewId * @param text */ public void setText(int viewId, CharSequence text) { // 优化,减少findViewById的次数,做缓存 TextView tv = getView(viewId); if (tv != null){ tv.setText(text); } } public <T extends View> T getView(int viewId) { WeakReference<View> viewWeakReference = mViews.get(viewId); View view = null; if (viewWeakReference != null){ view = viewWeakReference.get(); } if (view == null){ view = mContentView.findViewById(viewId); if (view != null) { mViews.put(viewId, new WeakReference<>(view)); } } return (T) view; } /** * 设置点击事件 * @param viewId * @param listener */ public void setOnClickListener(int viewId, View.OnClickListener listener) { View view = getView(viewId); if (view != null){ view.setOnClickListener(listener); } } /** * 获取ContentView * @return */ public View getContentView() { return mContentView; } }
AlertController.java
/** * @creation_time: 2017/6/26 * @author: Vegen * @e-mail: vegenhu@163.com * @description: 具体的构建器 */class AlertController { private AlertDialog mDialog; private Window mWindow; private DialogViewHelper mViewHelper; /** * 获取Dialog * @return */ public AlertDialog getDialog() { return mDialog; } /** * 获取Dialog的Window * @return */ public Window getWindow() { return mWindow; } public AlertController(AlertDialog dialog, Window window) { this.mDialog = dialog; this.mWindow = window; } /** * 设置文本 * @param viewId * @param text */ public void setText(int viewId, CharSequence text) { mViewHelper.setText(viewId, text); } public <T extends View> T getView(int viewId) { return mViewHelper.getView(viewId); } /** * 设置点击事件 * @param viewId * @param listener */ public void setOnClickListener(int viewId, View.OnClickListener listener) { mViewHelper.setOnClickListener(viewId, listener); } public void setViewHelper(DialogViewHelper viewHelper) { this.mViewHelper = viewHelper; } public static class AlertParams{ public Context mContext; public int mThemeResId; // 点击空白地方是否能够取消,默认能 public boolean mCancelable = true; // dialog Cancel监听 public DialogInterface.OnCancelListener mOnCancelListener; // dialog 消失监听 public DialogInterface.OnDismissListener mOnDismissListener; // dialog 按键监听 public DialogInterface.OnKeyListener mOnKeyListener; // 布局 View public View mView; // 布局 layout id public int mViewLayoutResId; // 存放字体的修改 public SparseArray<CharSequence> mTextArray = new SparseArray<>(); // 存放点击时间 WeakReference软引用,优化,防止内存泄漏 public SparseArray<View.OnClickListener> mClickArray = new SparseArray<>(); // Dialog 的宽度 public int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT; // 动画 public int mAnimations = 0; // 位置 public int mGravity = Gravity.CENTER; // Dialog 的高度 public int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT; public AlertParams(Context context, int themeResId) { this.mContext = context; this.mThemeResId = themeResId; } /** * 绑定和设置参数 * @param mAlert */ public void apply(AlertController mAlert) { // 设置参数 // 1.设置Dialog布局 DialogViewHelper DialogViewHelper viewHelper = null; if (mViewLayoutResId != 0){ viewHelper = new DialogViewHelper(mContext, mViewLayoutResId); } if (mView != null){ viewHelper = new DialogViewHelper(); viewHelper.setContentView(mView); } if (viewHelper == null){ throw new IllegalArgumentException("请设置布局setContentView()"); } // 给Dialog 设置布局 mAlert.getDialog().setContentView(viewHelper.getContentView()); // 设置 Controller的辅助类 mAlert.setViewHelper(viewHelper); // 2.设置文本 int textArraySize = mTextArray.size(); for (int i = 0; i < textArraySize; i ++){ mAlert.setText(mTextArray.keyAt(i), mTextArray.valueAt(i)); } // 3.设置点击 int clickArraySize = mClickArray.size(); for (int i = 0; i < clickArraySize; i ++){ mAlert.setOnClickListener(mClickArray.keyAt(i), mClickArray.valueAt(i)); } // 4.配置自定义的效果 全屏 从底部弹出 默认动画 Window window = mAlert.getWindow(); // 设置位置 window.setGravity(mGravity); // 设置动画 if (mAnimations != 0) { window.setWindowAnimations(mAnimations); } // 设置宽高 WindowManager.LayoutParams params = window.getAttributes(); params.width = mWidth; params.height = mHeight; window.setAttributes(params); } } }
AlertDialog.java,自定义的万能Dialog(参考系统的AlertDialog)
/** * @creation_time: 2017/6/26 * @author: Vegen * @e-mail: vegenhu@163.com * @description: 自定义的万能Dialog */public class AlertDialog extends Dialog { private AlertController mAlert; public AlertDialog(@NonNull Context context, @StyleRes int themeResId) { super(context, themeResId); mAlert = new AlertController(this, getWindow()); } /** * 设置文本 * @param viewId * @param text */ public void setText(int viewId, CharSequence text) { mAlert.setText(viewId, text); } public <T extends View> T getView(int viewId) { return mAlert.getView(viewId); } /** * 设置点击事件 * @param viewId * @param listener */ public void setOnClickListener(int viewId, View.OnClickListener listener) { mAlert.setOnClickListener(viewId, listener); } public static class Builder{ private final AlertController.AlertParams P; /** * Creates a builder for an alert dialog that uses the default alert * dialog theme. * <p> * The default alert dialog theme is defined by * {@link android.R.attr#alertDialogTheme} within the parent * {@code context}'s theme. * * @param context the parent context */ public Builder(Context context) { this(context, R.style.dialog); } /** * Creates a builder for an alert dialog that uses an explicit theme * resource. * <p> * The specified theme resource ({@code themeResId}) is applied on top * of the parent {@code context}'s theme. It may be specified as a * style resource containing a fully-populated theme, such as * {@link android.R.style#Theme_Material_Dialog}, to replace all * attributes in the parent {@code context}'s theme including primary * and accent colors. * <p> * To preserve attributes such as primary and accent colors, the * {@code themeResId} may instead be specified as an overlay theme such * as {@link android.R.style#ThemeOverlay_Material_Dialog}. This will * override only the window attributes necessary to style the alert * window as a dialog. * <p> * Alternatively, the {@code themeResId} may be specified as {@code 0} * to use the parent {@code context}'s resolved value for * {@link android.R.attr#alertDialogTheme}. * * @param context the parent context * @param themeResId the resource ID of the theme against which to inflate * this dialog, or {@code 0} to use the parent * {@code context}'s default alert dialog theme */ public Builder(Context context, int themeResId) { P = new AlertController.AlertParams(context, themeResId); } /** * Sets a custom view to be the contents of the alert dialog. * <p> * When using a pre-Holo theme, if the supplied view is an instance of * a {@link } then the light background will be used. * <p> * <strong>Note:</strong> To ensure consistent styling, the custom view * should be inflated or constructed using the alert dialog's themed * context obtained via {@link #getContext()}. * * @param view the view to use as the contents of the alert dialog * @return this Builder object to allow for chaining of calls to set * methods */ public Builder setView(View view) { P.mView = view; P.mViewLayoutResId = 0; return this; } /** * 设置布局内容的layout id * @param layoutResId * @return */ public Builder setContentView(int layoutResId) { P.mView = null; P.mViewLayoutResId = layoutResId; return this; } /** * 设置文本 * @param viewId * @param text * @return */ public Builder setText(int viewId, CharSequence text){ P.mTextArray.put(viewId, text); return this; } /** * 设置点击事件 * @param view * @param listener * @return */ public Builder setOnClickListener(int view, View.OnClickListener listener){ P.mClickArray.put(view, listener); return this; } /** * Sets the callback that will be called if the dialog is canceled. * * <p>Even in a cancelable dialog, the dialog may be dismissed for reasons other than * being canceled or one of the supplied choices being selected. * If you are interested in listening for all cases where the dialog is dismissed * and not just when it is canceled, see * {@link #setOnDismissListener(android.content.DialogInterface.OnDismissListener) setOnDismissListener}.</p> * @see #setCancelable(boolean) * @see #setOnDismissListener(android.content.DialogInterface.OnDismissListener) * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setOnCancelListener(OnCancelListener onCancelListener) { P.mOnCancelListener = onCancelListener; return this; } /** * Sets the callback that will be called when the dialog is dismissed for any reason. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setOnDismissListener(OnDismissListener onDismissListener) { P.mOnDismissListener = onDismissListener; return this; } /** * Sets the callback that will be called if a key is dispatched to the dialog. * * @return This Builder object to allow for chaining of calls to set methods */ public Builder setOnKeyListener(OnKeyListener onKeyListener) { P.mOnKeyListener = onKeyListener; return this; } /** * 全屏 * @return */ public Builder fullWidth(){ P.mWidth = ViewGroup.LayoutParams.MATCH_PARENT; return this; } /** * 从底部弹出 * @param isAnimation 是否添加动画 * @return */ public Builder formBottom(boolean isAnimation){ if (isAnimation){ P.mAnimations = R.style.dialog_from_bottom_anim; } P.mGravity = Gravity.BOTTOM; return this; } /** * 设置宽高 * @param width * @param height * @return */ public Builder setWidthAndHeight(int width, int height){ P.mWidth = width; P.mHeight = height; return this; } /** * 添加默认动画 * @return */ public Builder addDefaultAnimation(){ P.mAnimations = R.style.dialog_scale_anim; return this; } /** * 添加动画 * @param styleAnimation * @return */ public Builder setAnimations(int styleAnimation){ P.mAnimations = styleAnimation; return this; } /** * Creates an {@link AlertDialog} with the arguments supplied to this * builder. * <p> * Calling this method does not display the dialog. If no additional * processing is needed, {@link #show()} may be called instead to both * create and display the dialog. */ public AlertDialog create() { // Context has already been wrapped with the appropriate theme. final AlertDialog dialog = new AlertDialog(P.mContext, P.mThemeResId); P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); if (P.mCancelable) { dialog.setCanceledOnTouchOutside(true); } dialog.setOnCancelListener(P.mOnCancelListener); dialog.setOnDismissListener(P.mOnDismissListener); if (P.mOnKeyListener != null) { dialog.setOnKeyListener(P.mOnKeyListener); } return dialog; } /** * Creates an {@link AlertDialog} with the arguments supplied to this * builder and immediately displays the dialog. * <p> * Calling this method is functionally identical to: * <pre> * AlertDialog dialog = builder.create(); * dialog.show(); * </pre> */ public AlertDialog show() { final AlertDialog dialog = create(); dialog.show(); return dialog; } } }
示例的弹窗布局:detail_comment_dialog.xml
<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root_view" android:layout_width="fill_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@color/comment_dialog_bg" android:orientation="vertical" android:padding="10.0dip"> <EditText android:id="@+id/comment_editor" android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/bg_detail_comment_editor" android:hint="@string/ss_share_hint" android:maxHeight="120.0dip" android:padding="9.0dip" android:textColor="@color/comment_dialog_content_text" android:textSize="16.0sp" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10.0dip" android:gravity="center_vertical"> <TextView android:id="@+id/share_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="10.0dip" android:text="@string/comment_dialog_share_label" android:textColor="@color/comment_dialog_share_text" android:textSize="13.0sp" /> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"> <ImageView android:id="@+id/account_icon_weibo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/account_icon_weibo" /> <ImageView android:id="@+id/account_icon_tencent" android:layout_width="wrap_content" android:layout_marginLeft="10dp" android:layout_height="wrap_content" android:src="@mipmap/account_icon_tencent" /> </LinearLayout> <LinearLayout android:id="@+id/platform_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/share_label" android:orientation="horizontal" /> <TextView android:id="@+id/submit_btn" android:layout_width="50.0dip" android:layout_height="25.0dip" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:background="@drawable/bg_comment_submit" android:gravity="center" android:text="@string/comment_dialog_send" android:textColor="@color/comment_dialog_submit_text" android:textSize="13.0sp" /> <TextView android:id="@+id/text_limit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginRight="10.0dip" android:layout_toLeftOf="@id/submit_btn" android:textSize="13.0sp" android:visibility="invisible" /> </LinearLayout> </LinearLayout></FrameLayout>12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
MainActivity.java
public class MainActivity extends BaseActivity { @ViewById(R.id.test) private Button mTest; @Override protected void initData() { mTest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 一行代码就可以搞掂! AlertDialog dialog = new AlertDialog.Builder(MainActivity.this) .setContentView(R.layout.detail_comment_dialog) .setText(R.id.submit_btn, "发送") .setOnClickListener(R.id.account_icon_weibo, new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "微博分享", Toast.LENGTH_SHORT).show(); } }).show(); } }); } @Override protected void initView() { } @Override protected void initTitle() { } @Override protected void setContentView() { setContentView(R.layout.activity_main); } }
代码简单的就不贴了,注释写得很详细,也不作讲解。失恋了,很伤心,决定好好学习,天天向上~~刷一大波代码安慰一下自己!