Android小知识-理解设计模式中的建造者模式

建造者模式的定义是:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。也就是说将构建过程和部件的表示隔离开,用户可以在不知道内部构建细节的情况下,对对象的构造流程进行相应的控制,比如在Android中典型的Builder模式就是AlerDialog.Builder类。

先看下UML图:

模型图上的四个角色介绍如下:

  1. builder:为创建一个产品对象的各个部件指定抽象接口。

  2. ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并 提供一个检索产品的接口。

  3. Director:构造一个使用Builder接口的对象。

  4. 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);
}

总结

总的来说相同的方法,不同的执行顺序,产生不同的事件结果时,可以考虑采用建造者模式;多个部件或零件,都可以装配到一个对象中,但产生的运行结果又不相同时,可以考虑采用建造者模式;产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,可以考虑采用建造者模式。

微信小程序-技术共享笔记

©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页