本平台的文章更新会有延迟,大家可以关注微信公众号-顾林海,包括年底前会更新kotlin由浅入深系列教程,目前计划在微信公众号进行首发,如果大家想获取最新教程,请关注微信公众号,谢谢
建造者模式的定义是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。也就是说将构建过程和部件的表示隔离开,用户可以在不知道内部构建细节的情况下,对对象的构造流程进行相应的控制,比如在Android中典型的Builder模式就是AlerDialog.Builder类。
先看下UML图:
模型图上的四个角色介绍如下:
-
builder:为创建一个产品对象的各个部件指定抽象接口。
-
ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并 提供一个检索产品的接口。
-
Director:构造一个使用Builder接口的对象。
-
Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
package com.apk.administrator.loadapk;
/**
* Product
* 具体的产品
*/
public class Person {
private String head;
private String body;
private String foot;
public String getHead() {
return head;
}
public void setHead(String head) {
this.head = head;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getFoot() {
return foot;
}
public void setFoot(String foot) {
this.foot = foot;
}
}
package com.apk.administrator.loadapk;
/**
* Builder
* 产品构建的抽象接口
*/
public interface PersonBuilder {
void buildHead();
void buildBody();
void buildFoot();
Person buildPerson();
}
/**
* Director
* 建造者
*/
public class PersonDirector {
public Person constructPerson(PersonBuilder pb) {
pb.buildHead();
pb.buildBody();
pb.buildFoot();
return pb.buildPerson();
}
}
package com.apk.administrator.loadapk;
/**
* ConcreteBuilder
* 具体建造工具(创建一个男人)
*/
public class ManBuilder implements PersonBuilder {
Person person;
public ManBuilder() {
person = new Person();
}
public void buildBody() {
person.setBody("建造男人的身体");
}
public void buildFoot() {
person.setFoot("建造男人的脚");
}
public void buildHead() {
person.setHead("建造男人的头");
}
public Person buildPerson() {
return person;
}
}
package com.apk.administrator.loadapk;
public class Test {
public static void main(String[] args) {
PersonDirector pd = new PersonDirector();
Person person = pd.constructPerson(new ManBuilder());
System.out.println(person.getBody());
System.out.println(person.getFoot());
System.out.println(person.getHead());
}
}
上面代码中最终的目的是要创建一个男人的对象,具体怎么创建使用者不关心,只需要通过建造者PersonDirector创建一个对象,什么对象?ManBuilder对象,这样男人对象就创建出来了。
案例:Dialog的封装
在封装Dialog时,会使用静态内部类Builder对Diloag的标题、内容、按钮以及事件监听进行配置,并通过CommonDialog类展示。
可以看出这是很典型的Builder模式,在配置参数时返回的是Builder本身,这样的话可以通过链式调用,使代码的可读性大大提高。由于封装的是Dialog,因此,Builder类会去继承Dialog来自定义Dialog。下面贴出完整的Dialog封装类:
package com.glh.getproject.view;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.TextView;
import com.glh.getproject.R;
public class CommonDialog {
private Builder mBuilder;
private CommonDialog(Builder builder) {
this.mBuilder = builder;
}
public void show() {
mBuilder.showDialog();
}
public static class Builder extends Dialog implements OnClickListener {
private TextView tv_title;
private TextView tv_left;
private TextView tv_right;
private TextView tv_info;
private DialogListener mDialogListener;
private String title;
private String info;
private String strLeftBtn;
private String strRightBtn;
public interface DialogListener {
void ok();
void cancel();
}
void showDialog() {
show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialogl_info_show);
initUI();
initEvent();
initViewStatus();
}
private void initUI() {
tv_title = findViewById(R.id.tv_title);
tv_left = findViewById(R.id.tv_left);
tv_right = findViewById(R.id.tv_right);
tv_info = findViewById(R.id.tv_info);
}
private void initViewStatus() {
if (!TextUtils.isEmpty(title)) {
tv_title.setText(title);
}
if (!TextUtils.isEmpty(info)) {
tv_info.setText(info);
}
if (!TextUtils.isEmpty(strLeftBtn)) {
tv_left.setText(strLeftBtn);
} else {
tv_left.setText("确定");
}
if (!TextUtils.isEmpty(strRightBtn)) {
tv_right.setText(strRightBtn);
} else {
tv_right.setText("取消");
}
}
private void initEvent() {
tv_left.setOnClickListener(this);
tv_right.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tv_left:
if (mDialogListener != null) {
dismiss();
mDialogListener.ok();
}
break;
case R.id.tv_right:
if (mDialogListener != null) {
dismiss();
mDialogListener.cancel();
}
break;
default:
break;
}
}
public Builder(Context context) {
super(context, R.style.MessageBox);
setCanceledOnTouchOutside(false);
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
WindowManager.LayoutParams wl = getWindow().getAttributes();
wl.gravity = Gravity.CENTER;
getWindow().setAttributes(wl);
}
public Builder setListener(DialogListener listener) {
this.mDialogListener = listener;
return this;
}
public Builder setTitle(String title) {
this.title = title;
return this;
}
public Builder setInfo(String info) {
this.info = info;
return this;
}
public Builder setLeftButtonTitle(String title) {
this.strLeftBtn = title;
return this;
}
public Builder setRightButtonTitle(String title) {
this.strRightBtn = title;
return this;
}
public CommonDialog build() {
return new CommonDialog(this);
}
}
}
相关的Style:
<style name="MessageBox">
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:layout_gravity">center</item>
</style>
layout文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@drawable/dialog_background_shape"
android:paddingLeft="16dp"
android:paddingRight="16dp">
<LinearLayout
android:id="@+id/ll_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:lineSpacingExtra="2dp"
android:textColor="@color/title"
android:textSize="17sp"
android:visibility="visible" />
<TextView
android:id="@+id/tv_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:lineSpacingExtra="2dp"
android:paddingTop="20dp"
android:textColor="@color/title"
android:textSize="17sp"
android:visibility="visible" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ll_title"
android:layout_centerHorizontal="true"
android:layout_marginTop="25dp"
android:gravity="center"
android:orientation="horizontal"
android:paddingBottom="20dp"
android:visibility="visible">
<TextView
android:id="@+id/tv_left"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="5dp"
android:layout_weight="1"
android:background="@drawable/red_button_shape"
android:gravity="center"
android:paddingBottom="7dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="7dp"
android:textColor="@color/white"
android:textSize="14sp"
android:visibility="visible" />
<TextView
android:id="@+id/tv_right"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:background="@drawable/gray_button_shape"
android:gravity="center"
android:paddingBottom="7dp"
android:paddingLeft="30dp"
android:paddingRight="30dp"
android:paddingTop="7dp"
android:textColor="@color/white"
android:textSize="14sp"
android:visibility="visible" />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
整个Dialog的布局背景和按钮都是通过自定义Shape来实现的。
dialog_background_shape:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white" />
<corners android:radius="10dp" />
</shape>
red_button_shape:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp" />
<solid android:color="#FA001F" />
</shape>
gray_button_shape:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="5dp" />
<solid android:color="@color/gray" />
</shape>
在Activity中使用:
public void showDialog() {
CommonDialog commonDialog = new CommonDialog.Builder(this)
.setListener(new CommonDialog.Builder.DialogListener() {
@Override
public void ok() {
Toast.makeText(MainActivity.this, "正在更新...", Toast.LENGTH_SHORT).show();
}
@Override
public void cancel() {
}
}).setTitle("更新通知")
.setInfo("当前版本暂不可用,请下载最新版本,以便享受优质内容。")
.setLeftButtonTitle("知道了")
.setRightButtonTitle("不需要")
.build();
commonDialog.show();
}
案例二:PopupWindow的封装
package com.glh.getproject.view;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupWindow;
import android.widget.TextView;
import com.glh.getproject.R;
/**
* 更新弹框
* Created by glh on 2017-05-09.
*/
public class UpdateDialog {
private PopupWindow mPopupWindow;
private Builder mBuilder;
private UpdateDialog(Builder builder) {
this.mBuilder = builder;
View mPopupLayout = builder.mPopupLayout;
this.mPopupWindow = new PopupWindow(mPopupLayout, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, true);
this.mPopupWindow.setContentView(mPopupLayout);
this.mPopupWindow.setOutsideTouchable(true);
this.mPopupWindow.setFocusable(true);
this.mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
this.mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
if (mBuilder.mCloseListener != null) {
mBuilder.mCloseListener.onClose();
}
}
});
this.mPopupWindow.update();
}
public void dismiss() {
if (!mPopupWindow.isShowing()) {
return;
}
mPopupWindow.dismiss();
}
public void showAtLocation(View parent, int gravity, int x, int y) {
mPopupWindow.showAtLocation(parent, gravity, x, y);
}
public static class Builder {
private View mPopupLayout;
private TextView tv_title;
private TextView tv_left;
private TextView tv_right;
private TextView tv_info;
private UpgradeListener mUpgradeListener;
private CloseListener mCloseListener;
private void initEvent() {
tv_left.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mUpgradeListener != null) {
mUpgradeListener.upgrade(true);
}
}
});
tv_right.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (mUpgradeListener != null) {
mUpgradeListener.upgrade(false);
}
}
});
}
public interface UpgradeListener {
void upgrade(boolean upgrade);
}
public interface CloseListener {
void onClose();
}
public Builder(Context context) {
mPopupLayout = LayoutInflater.from(context).inflate(R.layout.dialogl_info_show, null, true);
tv_title = mPopupLayout.findViewById(R.id.tv_title);
tv_left = mPopupLayout.findViewById(R.id.tv_left);
tv_right = mPopupLayout.findViewById(R.id.tv_right);
tv_info = mPopupLayout.findViewById(R.id.tv_info);
initEvent();
}
public Builder setUpgradeListener(UpgradeListener listener) {
this.mUpgradeListener = listener;
return this;
}
public Builder setCloseListener(CloseListener listener) {
this.mCloseListener = listener;
return this;
}
public Builder setTitle(String title) {
this.tv_title.setText(title);
return this;
}
public Builder setInfo(String info) {
this.tv_info.setText(info);
return this;
}
public Builder setLeftButtonTitle(String title) {
this.tv_left.setText(title);
return this;
}
public Builder setRightButtonTitle(String title) {
this.tv_right.setText(title);
return this;
}
public UpdateDialog build() {
return new UpdateDialog(this);
}
}
}
layout文件和Dialog的布局文件一样,这里就不贴出来了。
在Activity的使用:
private UpdateDialog commonDialog;
private void showPopupWindow() {
if (null == commonDialog) {
commonDialog = new UpdateDialog.Builder(this)
.setUpgradeListener(new UpdateDialog.Builder.UpgradeListener() {
@Override
public void upgrade(boolean upgrade) {
commonDialog.dismiss();
if (upgrade) {
Toast.makeText(MainActivity.this, "正在更新...", Toast.LENGTH_SHORT).show();
}
}
}).setCloseListener(new UpdateDialog.Builder.CloseListener() {
@Override
public void onClose() {
Toast.makeText(MainActivity.this, "对话框关闭了", Toast.LENGTH_SHORT).show();
}
}).setTitle("更新通知")
.setInfo("当前版本暂不可用,请下载最新版本,以便享受优质内容。")
.setLeftButtonTitle("知道了")
.setRightButtonTitle("不需要")
.build();
}
commonDialog.showAtLocation(mGroup, Gravity.CENTER, 0, 0);
}
总结
总的来说相同的方法,不同的执行顺序,产生不同的事件结果时,可以考虑采用建造者模式;多个部件或零件,都可以装配到一个对象中,但产生的运行结果又不相同时,可以考虑采用建造者模式;产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,可以考虑采用建造者模式。
![](https://img.haomeiwen.com/i838794/7bbb25d109a09d6a.jpg)
搜索微信“顾林海”公众号,定期推送优质文章。
网友评论