之前实现了一个Demo
底部弹出框,是用DialogFragment
实现的一个Dialog
,虽然实现了链式调用,但是没有使用Builder
模式,所以想试试Builder
模式,写博客记录一下。
这篇博客谈论的Android
中的Builder
模式没有Java
传统的使用那么复杂,只是一种编程思路,使需要复杂的参数构建的对象变得简单,而且内部封装构建过程可以方便变换和复用,更是降低了耦合性。
分析 AlertDialog
AlertDialog
是谷歌原生的对话框,在V7包中提供了Material Design
设计风格的Dialog
,使用非常广泛。但是,在这篇博客里,我们只讨论分析它的编程实现模式,它使用的就是Builder
模式。
1.先来看看 AlertDialog 源码
从AlertDialog
源码中可以发现成员变量中有一个AlertController
,从它的命名
可以了解它大概是个组织类,即是所谓的”控制层“;再从AlertDialog
的构造方法中发现,它没做什么太多的操作,主要就是初始化了AlertController
,那我们可以进一步了解到
AlertController
可能是担任组织数据逻辑的作用。
那我们就进一步的看看
往下阅读它的源码,AlertDialog
在onCreate
中与AlertController
建立了联系,从它的方法命名installContent()
可以了解它是初始化AlertDialog
内容实体的,那我们可以确定刚刚的推测,AlertController
是负责AlertDialog
组织内容逻辑的,而AlertDialog
只是简单的”UI层“。
再往下就是AlertDialog
中静态内部类,也是我们要说的重点Builder
从Builder
类中发现了一个眼熟的东西——AlertController.AlertParams
,从它的命名(命名果然好重要)可以发现它可能是逻辑类,也就是”模型层“(是不是有点像MVC = =),负责处理数据逻辑的。
再看一看源码
跟推测的一样,直接把数据赋予给了AlertController.AlertParams
,而且都是return Builder 对象,保证了链式调用;源码里面大部分都是类似的代码,这里只贴出部分。
在源码的最后,发现了重点
在create()
方法中的apply()
是将AlertDialog
中的AlertController
与AlertController.AlertParams
建立联系,其实就是控制层与逻辑层相通,最后会由
AlertController
控制要显示的视图内容。
AlertDialog
的源码基本上我们过了一遍,了解它的模式思路,那我们再从apply()
进去,看看AlertController
与AlertController.AlertParams
是怎么建立联系的。
2.AlertController.AlertParams源码
从代码中可以看见,AlertController
获得了AlertController.AlertParams
中保存的数据,其他代码不在详述,无非就是转换数据,最后还是要赋予AlertController
。
3.AlertController 源码
在构造方法中获得相对应的视图。
这是之前介绍过的初始化实体内容的方法,显然是负责构建视图的。
构建视图的过程,通过获得的数据构建相应的视图内容。
所以说AlertController
是整个模式中负责组织建造的,这也是Builder
模式的核心。
通过分析AlertDialog
的源码,我们了解谷歌原生组件的Builder
的模式,通过分层将逻辑简化,代码简洁。
Builder模式 实践
根据以上,我使用Builder模式重构了之前的小项目BottomPopUpDialog
。
public class BottomPopUpDialog extends DialogFragment {
private TextView mCancel;
private LinearLayout mContentLayout;
private Builder mBuilder;
private static BottomPopUpDialog getInstance(Builder builder) {
BottomPopUpDialog dialog = new BottomPopUpDialog();
dialog.mBuilder = builder;
return dialog;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//该方法需要放在onViewCreated比较合适, 若在 onStart 在部分机型(如:小米3)会出现闪烁的情况
getDialog().getWindow().setBackgroundDrawableResource(mBuilder.mBackgroundShadowColor);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Holo_Light_NoActionBar);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.bottom_pop_up_dialog, null);
initView(view);
registerListener(view);
setCancelable(true);
return view;
}
private void initView(View view) {
mContentLayout = (LinearLayout) view.findViewById(R.id.pop_dialog_content_layout);
mCancel = (TextView) view.findViewById(R.id.cancel);
initItemView();
}
private void registerListener(View view) {
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
dismiss();
}
return false;
}
});
mCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
}
@Override
public void show(FragmentManager manager, String tag) {
try {
super.show(manager, tag);
} catch (Exception e) {
e.printStackTrace();
}
}
private void initItemView() {
//循环添加item
for (int i = 0; i < mBuilder.mDataArray.length; i++) {
final PopupDialogItem dialogItem = new PopupDialogItem(getContext());
dialogItem.refreshData(mBuilder.mDataArray[i]);
//最后一项隐藏分割线
if (i == mBuilder.mDataArray.length - 1) {
dialogItem.hideLine();
}
//设置字体颜色
if (mBuilder.mColorArray.size() != 0 && mBuilder.mColorArray.get(i) != 0) {
dialogItem.setTextColor(mBuilder.mColorArray.get(i));
}
if (mBuilder.mLineColor != 0) {
dialogItem.setLineColor(mBuilder.mLineColor);
}
mContentLayout.addView(dialogItem);
dialogItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBuilder.mListener.onDialogClick(dialogItem.getItemContent());
if (mBuilder.mIsCallBackDismiss) dismiss();
}
});
}
}
public static class Builder {
private String[] mDataArray;
private SparseIntArray mColorArray = new SparseIntArray();
private BottomPopDialogOnClickListener mListener;
private int mLineColor;
private boolean mIsCallBackDismiss = false;
private int mBackgroundShadowColor = R.color.transparent_70;
/**
* 设置item数据
*/
public Builder setDialogData(String[] dataArray) {
mDataArray = dataArray;
return this;
}
/**
* 设置监听item监听器
*/
public Builder setItemOnListener(BottomPopDialogOnClickListener listener) {
mListener = listener;
return this;
}
/**
* 设置字体颜色
*
* @param index item的索引
* @param color res color
*/
public Builder setItemTextColor(int index, int color) {
mColorArray.put(index, color);
return this;
}
/**
* 设置item分隔线颜色
*/
public Builder setItemLineColor(int color) {
mLineColor = color;
return this;
}
/**
* 设置是否点击回调取消dialog
*/
public Builder setCallBackDismiss(boolean dismiss) {
mIsCallBackDismiss = dismiss;
return this;
}
/**
* 设置dialog背景阴影颜色
*/
public Builder setBackgroundShadowColor(int color) {
mBackgroundShadowColor = color;
return this;
}
public BottomPopUpDialog create() {
return BottomPopUpDialog.getInstance(this);
}
public BottomPopUpDialog show(FragmentManager manager, String tag) {
BottomPopUpDialog dialog = create();
dialog.show(manager, tag);
return dialog;
}
}
public interface BottomPopDialogOnClickListener {
/**
* item点击事件回调
*
* @param tag item字符串 用于识别item
*/
void onDialogClick(String tag);
}
}
这是我结合前面的Builder
模式写的BottomPopUpDialog
。Buildr
模式是将一个对象的构建与显示分离,将不同的参数一个一个添加进去,也是对于外部隐藏实现细节,更是降低了耦合度,方便以后的自由扩展。还有,我的实现省去了Controller
层代码,我把控制层和UI层放在一起,这样实现是为了简单,我觉得Controller
层是在可以复用的场景下,使用起来更有价值,而小组件可以更简单的使用Builder
模式。编程是简单实用。
重构之后的调用
new BottomPopUpDialog.Builder()
.setDialogData(getResources().getStringArray(R.array.popup_array))
.setItemTextColor(2, R.color.colorAccent)
.setItemTextColor(4, R.color.colorAccent)
.setCallBackDismiss(true)
.setItemLineColor(R.color.line_color)
.setItemOnListener(new BottomPopUpDialog.BottomPopDialogOnClickListener() {
@Override
public void onDialogClick(String tag) {
Snackbar.make(view, tag, Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
})
.show(getSupportFragmentManager(), "tag");
最后
这是一个简单的编程模式,记录一下思路。