Android建造者模式(okhttp源码Builder设计模式,AlertDialog源码Builder设计模式)

建造者模式

一. 概念及其生活案例

生活案例:顾名思义,重点就是建造的过程。举例:造车,车是由各种各样的零件组装而成,底盘,轮胎,外壳,座椅等等,生成的流程还是非常固定的,可以把这些组成部分看成一系列的复杂的对象,轮胎可能需要各样的材料组成,外壳或许会有颜色的定制或者材料质量要求,座椅也许也会定制要求真皮材质,非常的多元化。但是它们的组装流程是一样不变的,变化的是它们这些配件的一些内部的细节(颜色,材质等等),但是不影响我们组装汽车的流程。

定义:

指一个复杂对象的构造和它的表示分离,使得同样的构建流程可以创建不同的表示,被称为建造者模式,它是将一个复杂的对象分解为多个简单的对象,然后一步步构建而成,它将变与不变相分离,即产品的组成部分是不变的,但是产品的每一个部分是可以灵活选择的。

代码示例

第一步:确定一个产品对象,它是由若干个简单对象部分A,B,C组成,我们看到A,B,C三个简单对象我们都提供了set方法去设置它,但其实根据具体需求product并不是三个对象都需要,所以这是不固定的set方法。什么意思呢,比如我们外部调用这个Product对象的时候,可能会有选择性的去setA,setB,setC。可控性就会比较强,当我们需要什么再去调用什么。

/**
 * 产品  是复杂对象
 */
public class Product {
    
    
    private PartA partA;
    private PartB partB;
    private PartC partC;

    public PartA getPartA() {
    
    
        return partA;
    }

    public void setPartA(PartA partA) {
    
    
        this.partA = partA;
    }

    public PartB getPartB() {
    
    
        return partB;
    }

    public void setPartB(PartB partB) {
    
    
        this.partB = partB;
    }

    public PartC getPartC() {
    
    
        return partC;
    }

    public void setPartC(PartC partC) {
    
    
        this.partC = partC;
    }

    public void show() {
    
    
        Log.e("product", "show: A" + partA.getName());
        Log.e("product", "show: B" + partB.getName());
        Log.e("product", "show: C" + partC.getName());
    }
}
public class PartA {
    
    
    String name;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }
}

public class PartB {
    
    
    String name;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }
}

public class PartC {
    
    
    String name;

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }
}

产品类定义好之后,我们就可以抽象一个Builder类,来执行我们的构建流程。一个复杂对象的流程是始终不变的,变化的是它的成分而已,所以我们可以使用抽象类作为基类。所有的内容都会在我们的build()函数里面进行构建。

abstract public class Builder {
    
    
    Product product;

    public abstract void setPartA(String nameA);

    public abstract void setPartB(String nameB);

    public abstract void setPartC(String nameC);

    public abstract void build();

    public Product getProduct() {
    
    
        return product;
    }
}
//这个是Builder的实现类,我们这里主要看Build方法,模拟建造者的过程
//build方法把三个A,B,C简单对象都实例化了出来,并且组装到product复杂对象上,这就是建造的核心
public class ConcreateBuilder extends Builder {
    
    
    private String nameA;
    private String nameB;
    private String nameC;

    @Override
    public void setPartA(String nameA) {
    
    
        this.nameA = nameA;
    }

    @Override
    public void setPartB(String nameB) {
    
    
        this.nameB = nameB;
    }

    @Override
    public void setPartC(String nameC) {
    
    
        this.nameC = nameC;
    }

    @Override
    public void build() {
    
    
        PartA partA = new PartA();
        if (!TextUtils.isEmpty(nameA)) {
    
    
            partA.setName(nameA);
        }
        PartB partB = new PartB();
        if (!TextUtils.isEmpty(nameB)) {
    
    
            partB.setName(nameB);
        }
        PartC partC = new PartC();
        if (!TextUtils.isEmpty(nameC)) {
    
    
            partC.setName(nameC);
        }
        product = new Product();
        product.setPartA(partA);
        product.setPartB(partB);
        product.setPartC(partC);

    }
}

第三方调用

public class MainActivity extends AppCompatActivity {
    
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
	//这里实例化了抽象类Builder的实现类对象,并且对product里面的简单对象A,B,C赋值
       
        Builder builder = new ConcreateBuilder();
        //这里我们可以看到,我们可以选择性的对A,B,C赋值,根据实际情况而定,即便没有赋值就是默认值。
        //毫不影响Product这个复杂对象的创建,只是Product里面的参数ABC三个值不一样罢了,这就是参数可变性,建造过程不变性。
        builder.setPartA("111");
        builder.setPartB("22222");
        builder.setPartC("333");
        builder.build();
        Product product = builder.getProduct();
        product.show();
    }
}

增强版标题栏代码实例

先看下面的图,我们app常见的标题栏就会用到建造者模式了,因为我们的标题栏可能会多种多样,我们要把握住变化的部分,也就是标题栏上面的内容,不变的部分就是标题栏本身,所以标题栏就是一个复杂的对象,标题栏上面可以有不同的内容,这些不同的内容就是组成复杂对象的简单对象。

在这里插入图片描述

接下来我们看代码模拟这个过程,我们一般是面向接口编程最优,我们首先明确好我们构建过程需要做些什么,在接口里面定义好对应的抽象方法,明确了需要做什么之后我们再写实现类去实现对应的功能。下面是自定义的一个Navigation的接口。

/**
 * 构建过程
 */
public interface INavigation {
    
    
    /**
     * 创建一个NavigationBar
     */
    void createNavigationBar();

    /**
     * 绑定一些参数 控件,以及控件上的信息等等,参数可选可不选
     */
    void attachNavigationParams();

    /**将navigation绑定到父控件
     * @param navigationView
     * @param parent
     */
    void attachParent(View navigationView, ViewGroup parent);
}

然后我们新建一个AbsNavigationBar的Builder内部类,主要是为了统一管理一些东西,抽取出公共的内容。在这个Builder类里面我们写了setText和setOnClickListener两个自定义方法,为的就是调用的时候统一的创建。

//这是基类抽象类
public class AbsNavigationBar<T extends AbsNavigationBar.Builder> implements INavigation {
    
    
    private T mBuilder;
    private View mNavigationView;

    protected AbsNavigationBar(T mBuilder) {
    
    
        this.mBuilder = mBuilder;
        createNavigationBar();
    }

    @Override
    public void createNavigationBar() {
    
    
        mNavigationView = LayoutInflater.from(mBuilder.mContext).inflate(mBuilder.mLayoutId, mBuilder.mParent, false);
        //添加
        attachParent(mNavigationView, mBuilder.mParent);
        //绑定文本和点击事件
        attachNavigationParams();
    }


    @Override
    public void attachNavigationParams() {
    
    
        /**
         * 文本
         */
        Map<Integer, String> textMap = mBuilder.textMap;
        for (Map.Entry<Integer, String> entry : textMap.entrySet()) {
    
    
            //绑定文本内容
            TextView textView = findViewBind(entry.getKey());
            textView.setText(entry.getValue());
        }
        /**
         * 点击事件
         */
        Map<Integer, View.OnClickListener> clickListenerMap = mBuilder.clickListenerMap;
        for (Map.Entry<Integer, View.OnClickListener> entry : clickListenerMap.entrySet()) {
    
    
            View view = findViewBind(entry.getKey());
            view.setOnClickListener(entry.getValue());
        }
    }

    public <T extends View> T findViewBind(int id) {
    
    
        return mNavigationView.findViewById(id);
    }

    @Override
    public void attachParent(View navigationView, ViewGroup parent) {
    
    
        parent.addView(navigationView, 0);
    }

    public static abstract class Builder<T extends Builder> {
    
    
        public Context mContext;
        public int mLayoutId;
        //存储对应id和文本容器
        public Map<Integer, String> textMap;
        //存储对应id和点击事件的容器
        public Map<Integer, View.OnClickListener> clickListenerMap;
        public ViewGroup mParent;
		
        //子类继承该抽象类的时候会通过构造函数对这些参数赋值
        public Builder(Context context, int layoutId) {
    
    
            Activity activity = (Activity) context;
            ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView();
            this.mParent = (ViewGroup) viewGroup.getChildAt(0);
            this.mContext = context;
            this.mLayoutId = layoutId;
            textMap = new HashMap<>();
            clickListenerMap = new HashMap<>();
        }

        //设置文字
        public T setText(int textId, String textString) {
    
    
            textMap.put(textId, textString);
            return (T) this;
        }

        //设置点击事件
        public T setOnClickListener(int viewId, View.OnClickListener listener) {
    
    
            clickListenerMap.put(viewId, listener);
            return (T) this;
        }
        //创建标题栏对象
        public abstract AbsNavigationBar create();
    }
}
/**
主要是用来接受一些参数,比如控件上的一些文字信息,或者对应控件的点击事件,假设我们一个个去写在每个页面
会导致代码很臃肿,所以这里用这个类统一管理。
*/
public static abstract class Builder<T extends Builder> {
    
    
    public Context mContext;
    public int mLayoutId;
    //存储对应id和文本容器
    public Map<Integer, String> textMap;
    //存储对应id和点击事件的容器
    public Map<Integer, View.OnClickListener> clickListenerMap;
    public ViewGroup mParent;

    public Builder(Context context, int layoutId) {
    
    
        Activity activity = (Activity) context;
        ViewGroup viewGroup = (ViewGroup) activity.getWindow().getDecorView();
        this.mParent = (ViewGroup) viewGroup.getChildAt(0);
        this.mContext = context;
        this.mLayoutId = layoutId;
        textMap = new HashMap<>();
        clickListenerMap = new HashMap<>();
    }

    //设置文字
    public T setText(int textId, String textString) {
    
    
        textMap.put(textId, textString);
        return (T) this;
    }

    //设置点击事件
    public T setOnClickListener(int viewId, View.OnClickListener listener) {
    
    
        clickListenerMap.put(viewId, listener);
        return (T) this;
    }

    public abstract AbsNavigationBar create();
}
}
//具体实现类
public class DefaultNavigationBar extends AbsNavigationBar<AbsNavigationBar.Builder> {
    
    
    
    protected DefaultNavigationBar(AbsNavigationBar.Builder mBuilder) {
    
    
        super(mBuilder);
    }
	
    public static class Builder extends AbsNavigationBar.Builder<AbsNavigationBar.Builder> {
    
    

        public Builder(Context context) {
    
    
            super(context, R.layout.navigation_layout);
        }
		
        @Override
        public DefaultNavigationBar create() {
    
    
			
            return new DefaultNavigationBar(this);
        }
		//返回this是为了调用的时候采用链式调用
        public Builder setLeftText(String leftText) {
    
    
            setText(R.id.tv_left, leftText);
            return this;
        }

        public Builder setTitleText(String titleText) {
    
    
            setText(R.id.tv_title, titleText);
            return this;
        }

        public Builder setRightText(String rightText) {
    
    
            setText(R.id.tv_right, rightText);
            return this;
        }

        public Builder setLeftListener(View.OnClickListener leftListener) {
    
    
            setOnClickListener(R.id.tv_left, leftListener);
            return this;
        }

        public Builder setTitleListener(View.OnClickListener titleListener) {
    
    
            setOnClickListener(R.id.tv_title, titleListener);
            return this;
        }

        public Builder setRightListener(View.OnClickListener rightListener) {
    
    
            setOnClickListener(R.id.tv_right, rightListener);
            return this;
        }
    }

}

这里是我们采用了链式调用,很明显调用和我们的实现相分离,极大程度上解耦,调用者不用关系Builder是怎么实现的,

只需要传入对应的参数就可以把想要的东西设置在标题栏上,当我们业务需求增加的时候,直接修改DefaultNavigationBar这个类或者新增实现类即可,抽象类AbsNavigationBar作为基类就不用动它。

public class MainActivity extends AppCompatActivity {
    
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //这里是链式调用
        DefaultNavigationBar navigationBar = new DefaultNavigationBar.Builder(this).setLeftText("返回1").setLeftListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                finish();
            }
        }).setTitleText("标题").setRightText("信息").create();


      
    }
}

标题栏布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="100dp">

    <TextView
        android:id="@+id/tv_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dp"
        android:layout_marginTop="20dp"
        android:text=""
        android:textSize="30sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="MissingConstraints" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="标题"
        android:textSize="30sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="MissingConstraints" />
    <TextView
        android:id="@+id/tv_right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="20dp"
        android:layout_marginTop="20dp"
        android:text=""
        android:textSize="30sp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

主页面布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="12312312"></TextView>

</LinearLayout>

运行效果:对比这两个图的效果,我们的activity的页面就作为内容部分在标题栏的下面,标题栏始终在上面,也就是说标题栏和内容模块是属于同级关系,不属于包含关系,不管我们怎么修改内容activity的内容布局等等,都不会影响到我们的标题栏,相当于两个View都互不影响,分离开了,这样子也算是解耦的一部分。

在这里插入图片描述
在这里插入图片描述

OKhttp源码详解(Builder设计模式)

看下面这段代码,是我们使用okhttp的常用写法,跟我们上面自己写的是一样的,链式调用法则,最后通过一个build的方法创建出来。

public class MainActivity extends AppCompatActivity {
    
    
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
		
        //okhttp用法
        //新建一个OkHttpClient的构造器Builder
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.connectTimeout(100, TimeUnit.SECONDS).addInterceptor(new Interceptor() {
    
    
            @Override
            public Response intercept(Chain chain) throws IOException {
    
    
                return null;
            }
        }).readTimeout(100, TimeUnit.MINUTES);

    }
}

接下来我们分析下它的源码

先再回顾一下建造者模式的定义:

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式将一个复杂对象的创建过程封装起来,允许对象通过多个步骤来创建,并且可以改变过程。尤其是在对象特别复杂,内部参数及其多的时候,建造者模式就能发挥出它的优势。若源代码中有Builder这个词,大概率使用了建造者模式。

注:这里我们用低版本的okhttp依赖分析源码,因为高版本的源码已经是kotlin了,很多人可能还不习惯kotlin,所以暂时先分析java版本的okhttp

/**
OkHttpClient就是我们说的复杂对象,内部包含一系列简单对象超时时间(Timeout),分发器(dispatcher),拦截器(interceptors)等等。
*/
public class OkHttpClient implements Cloneable, Call.Factory, WebSocketCall.Factory {
    
    
 	//我们实例化的构造函数
    public OkHttpClient() {
    
    
        //往下调用
    this(new Builder());
  }
//调用到这个构造函数 
//下面我们看到的这些就是一个个的简单对象通过builder这个对象去赋值的
  OkHttpClient(Builder builder) {
    
    
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
    this.protocols = builder.protocols;
    this.connectionSpecs = builder.connectionSpecs;
    this.interceptors = Util.immutableList(builder.interceptors);
    this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
    this.eventListenerFactory = builder.eventListenerFactory;
    this.proxySelector = builder.proxySelector;
    this.cookieJar = builder.cookieJar;
    this.cache = builder.cache;
    this.internalCache = builder.internalCache;
    this.socketFactory = builder.socketFactory;

    boolean isTLS = false;
    for (ConnectionSpec spec : connectionSpecs) {
    
    
      isTLS = isTLS || spec.isTls();
    }

    if (builder.sslSocketFactory != null || !isTLS) {
    
    
      this.sslSocketFactory = builder.sslSocketFactory;
      this.certificateChainCleaner = builder.certificateChainCleaner;
    } else {
    
    
      X509TrustManager trustManager = systemDefaultTrustManager();
      this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
    }

    this.hostnameVerifier = builder.hostnameVerifier;
    this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
        certificateChainCleaner);
    this.proxyAuthenticator = builder.proxyAuthenticator;
    this.authenticator = builder.authenticator;
    this.connectionPool = builder.connectionPool;
    this.dns = builder.dns;
    this.followSslRedirects = builder.followSslRedirects;
    this.followRedirects = builder.followRedirects;
    this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
    this.connectTimeout = builder.connectTimeout;
    this.readTimeout = builder.readTimeout;
    this.writeTimeout = builder.writeTimeout;
    this.pingInterval = builder.pingInterval;

    if (interceptors.contains(null)) {
    
    
      throw new IllegalStateException("Null interceptor: " + interceptors);
    }
    if (networkInterceptors.contains(null)) {
    
    
      throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
    }
  }
  public Builder newBuilder() {
    
    
    return new Builder(this);
  }
  // Builder类
  public static final class Builder {
    
      
      //OkHttpClient.Builder的默认无参构造方法还设置了默认的参数初始值,我们在调用的时候如果没有传入参数那么就会使用它默认值。
     public Builder() {
    
    
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault();
      hostnameVerifier = OkHostnameVerifier.INSTANCE;
      certificatePinner = CertificatePinner.DEFAULT;
      proxyAuthenticator = Authenticator.NONE;
      authenticator = Authenticator.NONE;
      connectionPool = new ConnectionPool();
      dns = Dns.SYSTEM;
      followSslRedirects = true;
      followRedirects = true;
      retryOnConnectionFailure = true;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }
    Builder(OkHttpClient okHttpClient) {
    
    ...... }    
    //Builder类的OkHttpClient,这里调用Builder.build()方法将Builder对象传递到OkHttpClient的构造方法里完成对象的创建。
    public OkHttpClient build() {
    
    
      return new OkHttpClient(this);
    }  
   }  
}

优点:

  1. 对象的创建和对象的展示分离开了,怎么说这个,OkHttpClient负责对外提供给别人调用,Builder负责对象的创建,符合职责分离。

  2. Builder类默认的成员变量会对OkhttpClient的创建所需的对象进行一些默认创建初始化,对外会提供一些传入参数的方法,让调用者也能灵活控制一些OkhttpClient的创建过程,第三方调用不配置参数的时候就是使用Builder无参构造里面的默认参数值。

AlertDialog源码详解(Builder设计模式)

一般情况我们先看怎么使用它,很明显它也是采用链式调用,所以第一反映用了建造者模式,核心在Builder类里面,我们去看看。

public class MainActivity extends AppCompatActivity {
    
    
    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        AlertDialog alertDialog = builder.create();
        //构造样式,标题,信息,按钮等等
        builder.setTitle("确认").setMessage("确定吗?")
                .setPositiveButton("是", null)
                .setNegativeButton("否", null).show();
    }
}

Builder类,源码很多我们就不列举完了,主要看一些重要部分。

public static class Builder {
    
    
    //这个p就是我们设置set方法,第三方调用的对象的引用
    private final AlertController.AlertParams P;
    private final int mTheme;


    public Builder(@NonNull Context context) {
    
    
        this(context, resolveDialogTheme(context, 0));
    }


    public Builder(@NonNull Context context, @StyleRes int themeResId) {
    
    
        P = new AlertController.AlertParams(new ContextThemeWrapper(
                context, resolveDialogTheme(context, themeResId)));
        mTheme = themeResId;
    }


    @NonNull
    public Context getContext() {
    
    
        return P.mContext;
    }


    public Builder setTitle(@StringRes int titleId) {
    
    
        P.mTitle = P.mContext.getText(titleId);
        return this;
    }


    public Builder setTitle(@Nullable CharSequence title) {
    
    
        P.mTitle = title;
        return this;
    }
    ......
    public Builder setPositiveButton(@StringRes int textId, final OnClickListener listener) {
    
    
         P.mPositiveButtonText = P.mContext.getText(textId);
         P.mPositiveButtonListener = listener;
         return this;
     }
    	//创建了Dialog对象
      @NonNull
        public AlertDialog create() {
    
    
        
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
            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;
        }

		
        public AlertDialog show() {
    
    
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }
    
}

进入到AlertParams里面看看,这里大部分都是set方法,也就是我们调用的时候想要设置自己想要的参数的地方,它这里经过apply函数的处理,都交给了一个AlertController类型的对象,进去看看它是什么。


public void apply(AlertController dialog) {
    
    
    if (mCustomTitleView != null) {
    
    
        dialog.setCustomTitle(mCustomTitleView);
    } else {
    
    
        if (mTitle != null) {
    
    
            dialog.setTitle(mTitle);
        }
        if (mIcon != null) {
    
    
            dialog.setIcon(mIcon);
        }
        if (mIconId != 0) {
    
    
            dialog.setIcon(mIconId);
        }
        if (mIconAttrId != 0) {
    
    
            dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
        }
    }
    if (mMessage != null) {
    
    
        dialog.setMessage(mMessage);
    }
    if (mPositiveButtonText != null || mPositiveButtonIcon != null) {
    
    
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
                mPositiveButtonListener, null, mPositiveButtonIcon);
    }
    if (mNegativeButtonText != null || mNegativeButtonIcon != null) {
    
    
        dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
                mNegativeButtonListener, null, mNegativeButtonIcon);
    }
    if (mNeutralButtonText != null || mNeutralButtonIcon != null) {
    
    
        dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
                mNeutralButtonListener, null, mNeutralButtonIcon);
    }
    // For a list, the client can either supply an array of items or an
    // adapter or a cursor
    if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
    
    
        createListView(dialog);
    }
    if (mView != null) {
    
    
        if (mViewSpacingSpecified) {
    
    
            dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
                    mViewSpacingBottom);
        } else {
    
    
            dialog.setView(mView);
        }
    } else if (mViewLayoutResId != 0) {
    
    
        dialog.setView(mViewLayoutResId);
    }

}

AlertController类,仔细观察我们会发现,AlertParams 是它的静态内部类,AlertParams 的属性和AlertController的成员属性是一样的,在我们第三方应用者调用set方法设置属性参数值的时候,其实我们一开始调用的是private final AlertController.AlertParams P; P这个引用指向的对象,也就是一开始先给AlertParams 的成员属性赋值。

class AlertController {
    
    
    private final Context mContext;
    final AppCompatDialog mDialog;
    private final Window mWindow;
    private final int mButtonIconDimen;

    private CharSequence mTitle;
    private CharSequence mMessage;
    ListView mListView;
    private View mView;

    private int mViewLayoutResId;

    private int mViewSpacingLeft;
    private int mViewSpacingTop;
    private int mViewSpacingRight;
    private int mViewSpacingBottom;
    private boolean mViewSpacingSpecified = false;

    Button mButtonPositive;
    private CharSequence mButtonPositiveText;
    Message mButtonPositiveMessage;
    private Drawable mButtonPositiveIcon;

    Button mButtonNegative;
    private CharSequence mButtonNegativeText;
    Message mButtonNegativeMessage;
    private Drawable mButtonNegativeIcon;

    Button mButtonNeutral;
    private CharSequence mButtonNeutralText;
    Message mButtonNeutralMessage;
    private Drawable mButtonNeutralIcon;

    NestedScrollView mScrollView;

    private int mIconId = 0;
    private Drawable mIcon;

    private ImageView mIconView;
    private TextView mTitleView;
    private TextView mMessageView;
    private View mCustomTitleView;

    ListAdapter mAdapter;

    int mCheckedItem = -1;

    private int mAlertDialogLayout;
    private int mButtonPanelSideLayout;
    int mListLayout;
    int mMultiChoiceItemLayout;
    int mSingleChoiceItemLayout;
    int mListItemLayout;

    private boolean mShowTitle;

    private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;

    Handler mHandler;
    。。。。。。
    }

调用了builder 的show方法之后,进去show方法看看,

//我们在应用上面的调用
AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog alertDialog = builder.create();
builder.setTitle("确认").setMessage("确定吗?").setPositiveButton("是", null).setNegativeButton("否", null).show();
//进入show方法,发现它调用的是dialog.show();再进入到create看看
  public AlertDialog show() {
    
    
            final AlertDialog dialog = create();
            dialog.show();
            return dialog;
        }
	
//这里是实例化了一个AlertDialog,dialog.mAlert,这个就是final AlertController mAlert;是AlertDialog的成员变量,传入到我们上面说的apply方法中去,P就是前面我们说的AlertParams这个静态内部类的引用,在apply中都把AlertParams中的值赋值到AlertController的成员变量中,类似就是在AlertParams中做了一个缓存操作把,这里大家如果忘记了apply可以回上面去看看,主要是做set操作。
  @NonNull
  public AlertDialog create() {
    
    
            final AlertDialog dialog = new AlertDialog(P.mContext, mTheme);
            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;
        }

优点:可以看出来AlertController是一个复杂的产品对象,它包含着许多简单对象,通过Builder进行构建对象,解决了我们Dialog上要展示多种效果的问题,不用每次都书写重复的代码。

猜你喜欢

转载自blog.csdn.net/weixin_46039528/article/details/128167521