Android弹窗探究之AlertDialog(三)—— Dialog的基本封装

在对AlertDialog进行封装之前,我们有必要先了解一下AlertDialog的源码实现。一般情况下一行代码我们就可以创建一个弹框,如下所示:

new AlertDialog.Builder(this).create().show();

下面我们就一段段来分析,首先点击进去看下AlertDialog

public class AlertDialog extends AppCompatDialog implements DialogInterface {
   
...
}

public class AppCompatDialog extends Dialog implements AppCompatCallback {
   
...
}

也就是说我们的AlertDialog是继承自AppCompatDialog,而AppCompatDialog是继承自我们的Dialog,这个关系知道是怎么一回事就可以了,接下来进入到Builder里面。

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>
     * To preserve attributes such as primary and accent colors, the
     * {@code themeResId} may instead be specified as an overlay theme such
     * as {@link R.style#ThemeOverlay_AppCompat_Dialog}. This will
     * override only the window attributes necessary to style the alert
     * window as a dialog.
     * <p>
     * Alternatively, the {@code themeResId} may be specified as {@code 0}
     * to use the parent {@code context}'s resolved value for
     * {@link android.R.attr#alertDialogTheme}.
     *
     * @param context the parent context
     * @param themeResId the resource ID of the theme against which to inflate
     *                   this dialog, or {@code 0} to use the parent
     *                   {@code context}'s default alert dialog theme
     */
    public Builder(@NonNull Context context, @StyleRes int themeResId) {
   
        P = new AlertController.AlertParams(new ContextThemeWrapper(
                context, resolveDialogTheme(context, themeResId)));
        mTheme = themeResId;
    }
...
}

可以看到,我们的Builder并不是直接在AlertDialog里面去创建方法,而是创建了一个静态内部类Builder,为什么会这样设计,了解设计模式的朋友可能会明白,这是用了建造者设计模式,便于为最后生成的AlertDialog进行个性化定制。在Builder里面有两个构造方法:

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;
}

注意看,第一个构造方法最终会执行到第二个构造方法里面,那么两个构造方法的区别是什么呢?注意看第二个构造方法的第二个参数themeResId,意思很明显AlertDialog的设置的主题,所以这里我们就很好去理解了,即:

在创建AlertDialog的Builder的时候,如果我们不给AlertDialog指定主题,则系统会默认使用系统自带的主题样式。

继续研究构造函数,里面有一个很重要的参数:P。这个P是在Builder里面声明的一个静态类:

public static class Builder {
   
    private final AlertController.AlertParams P;
    private final int mTheme;
    ...
}

在调用构造函数的时候会实例化P。那么这个P又是什么呢?点进去看看:

class AlertController {
   
      public static class AlertParams {
   
              public final Context mContext;
              public final LayoutInflater mInflater;
      }
}

可以很明显的看到,P是AlertController类里面的一个静态内部类,我们先知道这个概念,后面再详细介绍。

接下来我们进入到create方法

public AlertDialog create() {
   
    // We can't use Dialog's 3-arg constructor with the createThemeContextWrapper param,
    // so we always have to re-set the theme
    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;
}

我们一行一行来看:
首先创建了一个AlertDialog的实例

protected AlertDialog(@NonNull Context context, @StyleRes int themeResId) {
   
    super(context, resolveDialogTheme(context, themeResId));
    mAlert = new AlertController(getContext(), this, getWindow());
}

在这里创建了AlertController这个类,在AlertController这个构造方法里面主要是去设置一些属性

public AlertController(Context context, AppCompatDialog di, Window window) {
   
    mContext = context;
    mDialog = di;
    mWindow = window;
    mHandler = new ButtonHandler(di);

    final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,
            R.attr.alertDialogStyle, 0);

    mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);
    mButtonPanelSideLayout = a.getResourceId(R.styleable.AlertDialog_buttonPanelSideLayout, 0);

    mListLayout = a.getResourceId(R.styleable.AlertDialog_listLayout, 0);
    mMultiChoiceItemLayout = a.getResourceId(R.styleable.AlertDialog_multiChoiceItemLayout, 0);
    mSingleChoiceItemLayout = a
            .getResourceId(R.styleable.AlertDialog_singleChoiceItemLayout, 0);
    mListItemLayout = a.getResourceId(R.styleable.AlertDialog_listItemLayout, 0);
    mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);
    mButtonIconDimen = a.getDimensionPixelSize(R.styleable.AlertDialog_buttonIconDimen, 0);

    a.recycle();

    /* We use a custom title so never request a window title */
    di.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值