通过Demo了解AlertDialog的构造原理

这两天看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很明显的暴露了,这是个建造者模式,先看下标准的建造者模式吧,比较晚了,我就百度找张图贴一下了。
builder pattern
网上有很多讲解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反而限制了外部调用时的灵活性。所以,我们做事要基于理论,但也不能拘泥于理论,基于实际情况灵活掌握才是正道。通过这样的封装,成功的向外界隐藏了大量复杂的代码细节,让调用者可以轻松优雅的实现想要的功能。我想,这应该是每一个优秀的程序员在实现功能后对自己的要求吧。最后上一下效果图吧。
这里写图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值