这两天看Android源码的时候,涉及到了比较常用的一个封装类:AlertDialog,众所周知这个Dialog封装了常用的Dialog使用的方法和样式,十分方便,也可以自定义很多想要的东西。这些不重要,里面比较精妙的我想就是那个Builder了,我们经常会在代码中这样优雅地构建一个AlertDialog。
public void showDialog()
{
AlertDialog dialog = new AlertDialog.Builder(this).
setTitle("Title").
setMessage("Message").
setPositiveButton("positive", null).
setNegativeButton("negative", null).
create();
dialog.show();
}
所以说命名规范重要呢,Builder很明显的暴露了,这是个建造者模式,先看下标准的建造者模式吧,比较晚了,我就百度找张图贴一下了。
网上有很多讲解AlertDialog源码的文章,我觉得对于初学者来说作用不大,因为源码里有很多细枝末节的东西,很容易让初学者分心而难以窥探到其本质。所以今天我换一个思路来说,仿照源码的思想来写一个同样调用方式的demo,把那些没用的细节都撇开,只看模式的思想是怎么运用的。好,先上代码。
package com.amuro.designPattern.builder.alert_dialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
/**
* Created by Amuro on 15/11/8.
*/
public class MyAlertDialog extends Dialog implements DialogInterface
{
private AlertController alertController;
public MyAlertDialog(Context context)
{
this(context, 0);
}
public MyAlertDialog(Context context, int themeResId)
{
super(context, themeResId);
alertController = new AlertController(context, this);
}
public static class Builder
{
private AlertParams params;
public Builder(Context context)
{
params = new AlertParams(context);
}
public Builder setTitle(String title)
{
params.setTitle(title);
return this;
}
public Builder setMessage(String message)
{
params.setMessage(message);
return this;
}
public Builder setLeftString(String leftString)
{
params.setLeftString(leftString);
return this;
}
public Builder setCenterString(String centerString)
{
params.setCenterString(centerString);
return this;
}
public Builder setRightString(String rightString)
{
params.setRightString(rightString);
return this;
}
public MyAlertDialog create()
{
MyAlertDialog dialog = new MyAlertDialog(params.getContext());
params.apply(dialog.alertController);
return dialog;
}
}
}
这个是我们自定义的AlertDialog,为了清晰我把里面所有的没用的代码全踢了,就只剩我们的Builder和AlertController了,这两个类也是AlertDialog原理的核心。Builder的产品是一个AlertParams,注意看每一个set方法都返回了一个Builder自己的指针,这也是我们的代码可以优雅的构建一个Dialog的基础,算是一个小技巧吧。在create方法中,我们会new一个自己的dialog,然后把dialog的成员AlertController传给了Params的apply方法(话说安卓源码里特别喜欢apply这个方法,不知是哪位大神的癖好)。下面来看AlertParams类。
package com.amuro.designPattern.builder.alert_dialog;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import com.amuro.R;
public class AlertParams
{
private Context context;
private String title;
private String message;
private String leftString;
private String centerString;
private String rightString;
public AlertParams(Context context)
{
this.context = context;
}
public void apply(AlertController alert)
{
alert.init();
if(title != null)
{
alert.setTitle(title);
}
if(message != null)
{
alert.setMessage(message);
}
if(leftString != null)
{
alert.setLeftButtonText(leftString);
}
if(centerString != null)
{
alert.setCenterButtonText(centerString);
}
if(rightString != null)
{
alert.setRightButtonText(rightString);
}
}
public Context getContext()
{
return context;
}
public void setContext(Context context)
{
this.context = context;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
public String getLeftString()
{
return leftString;
}
public void setLeftString(String leftString)
{
this.leftString = leftString;
}
public String getCenterString()
{
return centerString;
}
public void setCenterString(String centerString)
{
this.centerString = centerString;
}
public String getRightString()
{
return rightString;
}
public void setRightString(String rightString)
{
this.rightString = rightString;
}
}
get和set都不用看了,核心就是那个apply方法,那个方法里调用了AlertController的init方法完成了它的初始化,我们知道,我们show一个AlertDialog的时候并没有传入任何布局文件,那界面是从哪里来的呢?说到这里我想各位已经猜到了,没错,就是AlertController在做这件事,上代码。
package com.amuro.designPattern.builder.alert_dialog;
import android.app.Dialog;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.TextView;
import com.amuro.R;
public class AlertController
{
private Context context;
private Dialog dialog;
private LayoutInflater layoutInflater;
private View contentView;
private TextView textViewTitle;
private TextView textViewMessage;
private Button buttonLeft;
private Button buttonCenter;
private Button buttonRight;
public AlertController(Context context, Dialog dialog)
{
this.context = context;
this.dialog = dialog;
}
public void init()
{
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
layoutInflater = LayoutInflater.from(context);
contentView = layoutInflater.inflate(R.layout.my_alert_dialog, null);
textViewTitle = (TextView)contentView.findViewById(R.id.tv_my_dialog_title);
textViewMessage = (TextView)contentView.findViewById(R.id.tv_my_dialog_message);
buttonLeft = (Button)contentView.findViewById(R.id.bt_my_dialog_left);
buttonCenter = (Button)contentView.findViewById(R.id.bt_my_dialog_center);
buttonRight = (Button)contentView.findViewById(R.id.bt_my_dialog_right);
dialog.setContentView(contentView);
}
public void setTitle(String title)
{
textViewTitle.setText(title);
}
public void setMessage(String message)
{
textViewMessage.setText(message);
}
public void setLeftButtonText(String left)
{
buttonLeft.setText(left);
}
public void setCenterButtonText(String center)
{
if(!TextUtils.isEmpty(center))
{
buttonCenter.setVisibility(View.VISIBLE);
buttonCenter.setText(center);
}
}
public void setRightButtonText(String right)
{
buttonRight.setText(right);
}
}
看到这里是不是恍然大悟了,没错,就是AlertController完成了界面的初始化和构建工作,这里的demo很简单而实际上真正AlertController是非常复杂的。Android内置的AlertDialog对应的布局文件就是android.R.layoug.alert_dialog,而构建工作则对应于AlertController的installContent方法,具体的,请参考安卓源码,不再赘述。然后让我们看看我们自己的代码怎么运行吧。
public void showMyDialog()
{
MyAlertDialog dialog =
new MyAlertDialog.Builder(this).
setTitle("我的标题").
setMessage("我的信息").
setLeftString("取消").
setCenterString("酱油").
setRightString("确定").
create();
dialog.show();
}
山寨度100%,perfect~可以看到,这个Builder模式并没有标准模版里面的Director类,因为大部分时候,Director其实是调用者本身,会有很多细节化的设置,用了Director反而限制了外部调用时的灵活性。所以,我们做事要基于理论,但也不能拘泥于理论,基于实际情况灵活掌握才是正道。通过这样的封装,成功的向外界隐藏了大量复杂的代码细节,让调用者可以轻松优雅的实现想要的功能。我想,这应该是每一个优秀的程序员在实现功能后对自己的要求吧。最后上一下效果图吧。