AlertDialog 源码分析及Bulider 模式打造万能的dialog

来源:互联网 发布:python 自然语言解析 编辑:程序博客网 时间:2024/05/19 16:34

AlertDialog 源码分析及Bulider 模式打造万能的dialog

背景:不管我们在哪个项目中,都会用到dialog,会有不同的各种样式的dialog,Android自带的dialog采用的bulider模式,但是定制很差,往往项目中采用的dialog 都会需要一些动画,和弹出方式以及所用到的背景图片等等。这个时候我们往往采用自定义dialog,但是如果有很多不同样式的dialog,我们就需要创建好多个自定义的dialog类。这个时候就在想可不可以用一个dialog类,当然可以最简单的AlertDialog 就只有一个类,我们只需要传布局便可。

我们先看一下AlertDialog的源码,进行分析

查看源码是我们最好的学习方式


按住Ctrl右键点击查看AlertDialog的源码

public class AlertDialog extends AppCompatDialog implements DialogInterface 

可以看到AlertDialog是继承AppCompatDialog类,我们来看一下这个类

可以看到AppCompatDialog extends Dialog 由此可见Android 的AlertDialog 也是一个自定义成的Dialog,至此我们的猜想是成立的,既然成立那就看一下他是怎么实现的继续看AppCompatDialog的构造函数,至于AppCompatDialog implements AppCompatCallback 这个接口我们先不考虑,官方的是这样注释的:Implemented this in order for AppCompat to be able to callback in certain situations. 有兴趣的可以自己去了解下。

private AppCompatDelegate mDelegate;public AppCompatDialog(Context context) {    this(context, 0);}public AppCompatDialog(Context context, int theme) {    super(context, getThemeResId(context, theme));    // This is a bit weird, but Dialog's are typically created and setup before being shown,    // which means that we can't rely on onCreate() being called before a content view is set.    // To workaround this, we call onCreate(null) in the ctor, and then again as usual in    // onCreate().    getDelegate().onCreate(null);    // Apply AppCompat's DayNight resources if needed    getDelegate().applyDayNight();}protected AppCompatDialog(Context context, boolean cancelable,        OnCancelListener cancelListener) {    super(context, cancelable, cancelListener);}@Overrideprotected void onCreate(Bundle savedInstanceState) {    getDelegate().installViewFactory();    super.onCreate(savedInstanceState);    getDelegate().onCreate(savedInstanceState);}

可以看到和平常的自定义dialog没什么不同,但是多了一个AppCompatDelegate这个类:
AppCompatDelegate-
This class represents a delegate which you can use to extend AppCompat’s support to any
可以看到一段官方的注释,英语不好的话就在线翻译吧–|,这句话的意思是:
此类表示一个委托,可以使用它来将AppCompat的支持扩展。也就是说这是一个委托类

从源码中我们可以看到他是将许多方法都交给了这个委托类去处理,这个类有什么作用呢?如果有谁做过夜间模式的一定知道这个类AppCompatDelegate AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); 这个类有木有很熟悉,如果有不熟悉的可以去看这个链接,http://godcoder.me/2016/07/28/Android%20Material%20Design%E7%B3%BB%E5%88%97%E4%B9%8B%E5%A4%9C%E9%97%B4%E6%A8%A1%E5%BC%8F/

  public ActionBar getSupportActionBar() {    return getDelegate().getSupportActionBar();}@Overridepublic void setContentView(@LayoutRes int layoutResID) {    getDelegate().setContentView(layoutResID);}@Overridepublic void setContentView(View view) {    getDelegate().setContentView(view);}@Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {    getDelegate().setContentView(view, params);}@Nullable@Overridepublic View findViewById(@IdRes int id) {    return getDelegate().findViewById(id);}@Overridepublic void setTitle(CharSequence title) {    super.setTitle(title);    getDelegate().setTitle(title);}@Overridepublic void setTitle(int titleId) {    super.setTitle(titleId);    getDelegate().setTitle(getContext().getString(titleId));}@Overridepublic void addContentView(View view, ViewGroup.LayoutParams params) {    getDelegate().addContentView(view, params);}@Overrideprotected void onStop() {    super.onStop();    getDelegate().onStop();}

由此可以看出,源码写的非常全面,实现了夜间和日间模式的切换,这是一个非常好的思路。

AppCompatDialog 类分析完了,说白就是我们平常自定义的dialog,只是AppCompatDialog 还实现了夜间模式

我们继续返回AlertDialog 类,接下来我们主要看这一段代码 276行

  public static class Builder {    private final AlertController.AlertParams P;    private final int mTheme;    /**     * Creates a builder for an alert dialog that uses the default alert     * dialog theme.     * <p>     * The default alert dialog theme is defined by     * {@link android.R.attr#alertDialogTheme} within the parent     * {@code context}'s theme.     *     * @param context the parent context     */    public Builder(@NonNull Context context) {        this(context, resolveDialogTheme(context, 0));    }    /**     * Creates a builder for an alert dialog that uses an explicit theme     * resource.     * <p>     * The specified theme resource ({@code themeResId}) is applied on top     * of the parent {@code context}'s theme. It may be specified as a     * style resource containing a fully-populated theme, such as     * {@link R.style#Theme_AppCompat_Dialog}, to replace all     * attributes in the parent {@code context}'s theme including primary     * and accent colors.     * <p>

很明显采用了Builder模式,我们来看一下是怎么样实现的:
看一下这个类 AlertController 从字面意思上可以看出是Alert 的控制器,我们挑一段代码查看:346行
public void setIcon(int resId) {
mIcon = null;
mIconId = resId;

    if (mIconView != null) {        if (resId != 0) {            mIconView.setVisibility(View.VISIBLE);            mIconView.setImageResource(mIconId);        } else {            mIconView.setVisibility(View.GONE);        }    }}

可以看出他是给icon赋值,也就是说这个控制器我们也可以写在Builder中,在Builder扩展赋值,也可以向源码一样写一个控制器public static class AlertParams 传参单独控制和扩展。

源码分析完毕,从源码我们可以学习到很多思想,很有益处

学到了技术,打磨我们的工具

下面我只写简单的实现方法

自定义一个Dialog

模式 布局 弹窗样式 弹出动画等逻辑和点击事件交给Builder去处理

public class CommentDialog extends Dialog {private View mView;private Activity context;private WindowManager.LayoutParams lp;//这里我将Builder自定义一个类,分开单独处理public CommentDialog(CommentBuilder builder) {    super(builder.context);    context = (Activity) builder.context;    mView = builder.view;}public CommentDialog(CommentBuilder builder, int resStyle) {    super(builder.context, resStyle);    context = (Activity) builder.context;    mView = builder.view;}@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(mView);    setCanceledOnTouchOutside(false);    Window window = getWindow();    window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);    window.getDecorView().setPadding(0, 0, 0, 0);    lp = window.getAttributes();    lp.gravity = Gravity.CENTER; //弹出位置    window.setAttributes(lp);}

}

Builder 类

public class CommentBuilder {public Context context;public View view;private int resStyle = -1;public boolean cancelTouchout;public CommentBuilder(Context context) {    this.context = context;}/*** 布局** @param resView* @return*/public CommentBuilder view(int resView) {    view = LayoutInflater.from(context).inflate(resView, null);    return this;}/*** dialog样式** @param resStyle* @return*/public CommentBuilder style(int resStyle) {    this.resStyle = resStyle;    return this;}/*** 添加一个点击事件** @param viewRes* @param listener* @return*/public CommentBuilder addViewOnclick(int viewRes, View.OnClickListener listener) {    view.findViewById(viewRes).setOnClickListener(listener);    return this;}/*** 触摸dialog外部是否可以取消** @param val false 不可dismiss* @return*/public CommentBuilder cancelTouchout(boolean val) {    cancelTouchout = val;    return this;}public CommentDialog build() {    if (resStyle != -1) {        return new CommentDialog(this, resStyle);    } else {        return new CommentDialog(this);    }}

}

如何使用呢

 CommentBuilder builder = new CommentBuilder(CropperActivity.this);    builder.view(R.layout.dialog_comment_upload_success_layout)            .style(R.style.shareStyles)            .setReview(R.id.dcus_tv, isReview)            .addViewOnclick(R.id.ll_dialog_ok, CropperActivity.this);    commentDialog = builder.build();    commentDialog.show();

注:对于不同样式的Dialog,我们只需要传不同的布局和样式,赋值和改变值还可以在Builder中扩展增加对应的方法;点击事件我们可以写在当前的Activity 和 Fragment onClick()中实现,若想添加多个点击事件 可以添加多个.addViewOnclick(R.id.XX, CropperActivity.this);我们还可以学习源码一样,写一个控制器进行控制,具体大家可以自己实现。

至此谢幕,技术的提升:千里之行,始于足下