Android彈窗探究之AlertDialog(三)—— Dialog的基本封裝

在對AlertDialog進(jìn)行封裝之前痒玩,我們有必要先了解一下AlertDialog的源碼實(shí)現(xiàn)昆庇。一般情況下一行代碼我們就可以創(chuàng)建一個彈框固歪,如下所示:

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

下面我們就一段段來分析汹胃,首先點(diǎn)擊進(jìn)去看下AlertDialog

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

也就是說我們的AlertDialog是繼承自AppCompatDialog上沐,而AppCompatDialog是繼承自我們的Dialog皮服,這個關(guān)系知道是怎么一回事就可以了,接下來進(jìn)入到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里面去創(chuàng)建方法,而是創(chuàng)建了一個靜態(tài)內(nèi)部類Builder蕴侧,為什么會這樣設(shè)計择同,了解設(shè)計模式的朋友可能會明白,這是用了建造者設(shè)計模式净宵,便于為最后生成的AlertDialog進(jìn)行個性化定制敲才。在Builder里面有兩個構(gòu)造方法:

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

注意看裹纳,第一個構(gòu)造方法最終會執(zhí)行到第二個構(gòu)造方法里面,那么兩個構(gòu)造方法的區(qū)別是什么呢紧武?注意看第二個構(gòu)造方法的第二個參數(shù)themeResId剃氧,意思很明顯AlertDialog的設(shè)置的主題,所以這里我們就很好去理解了阻星,即:

在創(chuàng)建AlertDialog的Builder的時候朋鞍,如果我們不給AlertDialog指定主題,則系統(tǒng)會默認(rèn)使用系統(tǒng)自帶的主題樣式妥箕。

繼續(xù)研究構(gòu)造函數(shù)滥酥,里面有一個很重要的參數(shù):P。這個P是在Builder里面聲明的一個靜態(tài)類:

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

在調(diào)用構(gòu)造函數(shù)的時候會實(shí)例化P矾踱。那么這個P又是什么呢恨狈?點(diǎn)進(jìn)去看看:

class AlertController {

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

可以很明顯的看到疏哗,P是AlertController類里面的一個靜態(tài)內(nèi)部類呛讲,我們先知道這個概念,后面再詳細(xì)介紹返奉。

接下來我們進(jìn)入到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;
}

我們一行一行來看:
首先創(chuàng)建了一個AlertDialog的實(shí)例

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

在這里創(chuàng)建了AlertController這個類贝搁,在AlertController這個構(gòu)造方法里面主要是去設(shè)置一些屬性

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

繼續(xù)向下看:
P.apply(dialog.mAlert);

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

    /*
    dialog.setCancelable(mCancelable);
    dialog.setOnCancelListener(mOnCancelListener);
    if (mOnKeyListener != null) {
        dialog.setOnKeyListener(mOnKeyListener);
    }
    */
}

首先在AlertController類里面的靜態(tài)內(nèi)部類AlertParams里調(diào)用了apply方法,而在這個方法里面其實(shí)不難看出是給我們的AlertController里面的屬性賦值芽偏,而值是來自于AlertParams聲明的屬性雷逆。其他的幾行代碼很好理解,就是給我們的AlertDialog設(shè)置監(jiān)聽污尉,最后返回AlertDialog實(shí)例膀哲,最后調(diào)用show方法進(jìn)行展示即可。

到這里被碗,AlertDialog的源碼我們大致走了一遍某宪,我們可以大致梳理一下其中幾個關(guān)鍵類的關(guān)系。


1.png

AlertController:顧名思義AlertDialog的控制器類锐朴,主要是用于處理AlertDialog的各種系統(tǒng)內(nèi)置屬性兴喂,持有AlertDialog的實(shí)例。

AlertParams:AlertController的內(nèi)部類焚志,是一個static的類衣迷,聲明各種通用的屬性,比如文本酱酬,圖片壶谒,視圖等等。在執(zhí)行apply方法的時候膳沽,將獲取的值傳遞給AlertController汗菜。

Builder:AlertDialog的內(nèi)部類泼差,是一個static的類,用于構(gòu)建AlertDialog呵俏。同時持有AlertController.AlertParams的實(shí)例堆缘。

了解了這幾個類的主要作用,接下來我們就通過一個具體的實(shí)例來看看AlertDialog到底是怎么運(yùn)行的普碎,簡單例子:

val dialog = AlertDialog.Builder(this)
dialog.setIcon(R.mipmap.ic_launcher)
dialog.setTitle("對話框")
dialog.setMessage("這是一個普通的對話框")
dialog.show()

一步一步分析:

val dialog = AlertDialog.Builder(this)

調(diào)用AlertDialog內(nèi)部類Builder類的構(gòu)造方法吼肥,實(shí)例化AlertController.AlertParams類

dialog.setIcon(R.mipmap.ic_launcher)
dialog.setTitle("對話框")
dialog.setMessage("這是一個普通的對話框")

給AlertController.AlertParams類里面的圖標(biāo)、標(biāo)題麻车、信息設(shè)置具體的值缀皱。

dialog.show()

這個會是分析的重點(diǎn):
(1)首先會去執(zhí)行AlertDialog.Builder類里面的show()方法

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

(2)進(jìn)入create方法

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

首先創(chuàng)建AlertDialog的實(shí)例,然后最終將這個實(shí)例返回动猬,P是在Builder的時候就會被實(shí)例化的AlertController.AlertParams實(shí)例啤斗。接著執(zhí)行AlertController.AlertParams的apply方法,在這里將我們賦值的title赁咙、message和icon賦值給AlertController钮莲,也可以理解為對AlertController進(jìn)行封裝,接下來繼續(xù)對AlertDialog的各種回調(diào)進(jìn)行監(jiān)聽彼水。
(2)繼續(xù)執(zhí)行到show()方法

public void show() {
    if (mShowing) {
        if (mDecor != null) {
            if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
            }
            mDecor.setVisibility(View.VISIBLE);
        }
        return;
    }

    mCanceled = false;

    if (!mCreated) {
        dispatchOnCreate(null);
    } else {
        // Fill the DecorView in on any configuration changes that
        // may have occured while it was removed from the WindowManager.
        final Configuration config = mContext.getResources().getConfiguration();
        mWindow.getDecorView().dispatchConfigurationChanged(config);
    }

    onStart();
    mDecor = mWindow.getDecorView();

    if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
        final ApplicationInfo info = mContext.getApplicationInfo();
        mWindow.setDefaultIcon(info.icon);
        mWindow.setDefaultLogo(info.logo);
        mActionBar = new WindowDecorActionBar(this);
    }

    WindowManager.LayoutParams l = mWindow.getAttributes();
    boolean restoreSoftInputMode = false;
    if ((l.softInputMode
            & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
        l.softInputMode |=
                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
        restoreSoftInputMode = true;
    }

    mWindowManager.addView(mDecor, l);
    if (restoreSoftInputMode) {
        l.softInputMode &=
                ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
    }

    mShowing = true;

    sendShowMessage();
}

首先判斷有沒有被創(chuàng)建過崔拥,也可以理解為AlertDialog所對應(yīng)的視圖部分,如果沒有凤覆,執(zhí)行
dispatchOnCreate(null)方法

void dispatchOnCreate(Bundle savedInstanceState) {
    if (!mCreated) {
        onCreate(savedInstanceState);
        mCreated = true;
    }
}

在這個方法里面會去執(zhí)行onCreate(savedInstanceState)方法链瓦,所以我們找到AlertDialog的onCreate(savedInstanceState)方法

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mAlert.installContent();
}

在這個方法里面最終會執(zhí)行到AlertController的installContent方法

public void installContent() {
    final int contentView = selectContentView();
    mDialog.setContentView(contentView);
    setupView();
}

這個方法比較重要,首先是通過selectContentView()獲取到我們的View盯桦,然后將View添加到mDialog里面去慈俯,最終執(zhí)行setupView()方法對這個視圖賦值,最后調(diào)用

mWindowManager.addView(mDecor, l);

將頁面進(jìn)行展示出來拥峦。大概的時序圖為:


2.png

了解了原理之后贴膘,我們就開始進(jìn)入實(shí)戰(zhàn)部分了

首先類的結(jié)構(gòu)如下所示:


3.png

首先創(chuàng)建AlertController類,主要的功能有:
(1)持有AlertDialog的實(shí)例
(2)創(chuàng)建內(nèi)部類AlertParams

class AlertController {

    private AlertDialog mDialog;
    private Window mWindow;
    private DialogViewHelper mViewHelper;

    public AlertController(AlertDialog dialog, Window window) {
        this.mDialog = dialog;
        this.mWindow = window;
    }

    public AlertDialog getDialog() {
        return mDialog;
    }

    public Window getWindow() {
        return mWindow;
    }

    // 設(shè)置TextView
    public void setText(int viewId, CharSequence text) {
        mViewHelper.setText(viewId, text);
    }

    public void setViewHelper(DialogViewHelper viewHelper) {
        this.mViewHelper = viewHelper;
    }


    // 設(shè)置點(diǎn)擊事件
    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        mViewHelper.setOnClickListener(viewId, listener);
    }

    public <T extends View> T getView(int viewId) {
        return mViewHelper.getView(viewId);
    }

    public static class AlertParams {

        public Context mContext;
        public int mThemeResId;
        // 點(diǎn)擊空白區(qū)是否可以取消
        public boolean mCancelable = true;
        // dialog cancel監(jiān)聽
        public DialogInterface.OnCancelListener mOnCancelListener;
        // dialog dismiss 監(jiān)聽
        public DialogInterface.OnDismissListener mOnDismissListener;
        // dialog 按鍵監(jiān)聽
        public DialogInterface.OnKeyListener mOnKeyListener;
        // 布局View
        public View mView;
        // 布局LayoutId
        public int mViewLayoutResId;
        // 存放所有的文本
        public SparseArray<CharSequence> mTextArray = new SparseArray<>();
        // 存放所有的點(diǎn)擊事件
        public SparseArray<View.OnClickListener> mClickArray = new SparseArray<>();
        // 設(shè)置寬度
        public int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
        // 設(shè)置動畫
        public int mAnimations = 0;
        // 設(shè)置從底部彈出
        public int mGravity = Gravity.CENTER;
        // 設(shè)置高度
        public int mHeight = ViewGroup.LayoutParams.WRAP_CONTENT;

        public AlertParams(Context context, int themeResId) {
            this.mContext = context;
            this.mThemeResId = themeResId;
        }

        // 綁定和設(shè)置參數(shù)
        public void apply(AlertController mAlert) {
            DialogViewHelper viewHelper = null;
            // 設(shè)置布局
            if (mViewLayoutResId != 0) {
                viewHelper = new DialogViewHelper(mContext, mViewLayoutResId);
            }

            if (mView != null) {
                viewHelper = new DialogViewHelper();
                viewHelper.setContentView(mView);
            }

            mAlert.setViewHelper(viewHelper);

            if (viewHelper == null) {
                throw new IllegalArgumentException("請設(shè)置布局setContentView()");
            }

            // 給Dialog設(shè)置布局
            mAlert.getDialog().setContentView(viewHelper.getContentView());

            // 設(shè)置文本
            int textArraySize = mTextArray.size();
            for (int i = 0; i < textArraySize; i++) {
                viewHelper.setText(mTextArray.keyAt(i), mTextArray.valueAt(i));
            }

            // 設(shè)置點(diǎn)擊事件
            int textClickSize = mClickArray.size();
            for (int i = 0; i < textClickSize; i++) {
                viewHelper.setOnClickListener(mClickArray.keyAt(i), mClickArray.valueAt(i));
            }


            Window window = mAlert.getWindow();
            window.setGravity(mGravity);
            if (mAnimations != 0)
                window.setWindowAnimations(mAnimations);
            WindowManager.LayoutParams params = window.getAttributes();
            params.width = mWidth;
            params.height = mHeight;
            window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
            window.setAttributes(params);

        }
    }
}

創(chuàng)建AlertDialog繼承自Dialog事镣,其包含的主要內(nèi)容有:
(1)持有AlertController的實(shí)例
(2)創(chuàng)建內(nèi)部類Builder

public class AlertDialog extends Dialog {

    final AlertController mAlert;

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

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


    // 設(shè)置TextView
    public void setText(int viewId, CharSequence text) {
        mAlert.setText(viewId, text);
    }


    // 設(shè)置點(diǎn)擊事件
    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        mAlert.setOnClickListener(viewId, listener);
    }

    public <T extends View> T getView(int viewId) {
        return mAlert.getView(viewId);
    }


    public static class Builder {

        private final AlertController.AlertParams P;

        public Builder(@NonNull Context context) {
            this(context, R.style.MyDialog);
        }

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

        // 設(shè)置View
        public Builder setContentView(int layoutResId) {
            P.mView = null;
            P.mViewLayoutResId = layoutResId;
            return this;
        }

        // 設(shè)置View
        public Builder setContentView(View view) {
            P.mView = view;
            P.mViewLayoutResId = 0;
            return this;
        }

        // 設(shè)置文本
        public Builder setText(int viewId, CharSequence text) {
            P.mTextArray.put(viewId, text);
            return this;
        }

        // 設(shè)置全屏
        public Builder fullWith() {
            P.mWidth = ViewGroup.LayoutParams.MATCH_PARENT;
            return this;
        }

        // 設(shè)置動畫
        public Builder fromBottom(boolean isAnimation) {
            if (isAnimation) {
                P.mAnimations = R.style.dialog_from_bottom_anim;
            }
            P.mGravity = Gravity.BOTTOM;
            return this;
        }

        // 設(shè)置Dialog的寬高
        public Builder setWidthAndHeight(int width, int height) {
            P.mWidth = width;
            P.mHeight = height;
            return this;
        }

        // 設(shè)置默認(rèn)動畫
        public Builder addDefaultAnimation() {
            P.mAnimations = R.style.dialog_scale_anim;
            return this;
        }

        // 添加動畫
        public Builder setAnimations(int styleAnimation) {
            P.mAnimations = styleAnimation;
            return this;
        }

        // 設(shè)置點(diǎn)擊事件
        public Builder setOnClickListener(int viewId, View.OnClickListener listener) {
            P.mClickArray.put(viewId, listener);
            return this;
        }


        public Builder setCancelable(boolean cancelable) {
            P.mCancelable = cancelable;
            return this;
        }


        public Builder setOnCancelListener(OnCancelListener onCancelListener) {
            P.mOnCancelListener = onCancelListener;
            return this;
        }


        public Builder setOnDismissListener(OnDismissListener onDismissListener) {
            P.mOnDismissListener = onDismissListener;
            return this;
        }


        public Builder setOnKeyListener(OnKeyListener onKeyListener) {
            P.mOnKeyListener = onKeyListener;
            return this;
        }

        public AlertDialog create() {
            final AlertDialog dialog = new AlertDialog(P.mContext, P.mThemeResId);
            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;
        }
    }
}

最后附上我們的DialogViewHelper處理類

public class DialogViewHelper {

    private View mContentView = null;
    private SparseArray<WeakReference<View>> mViews;

    public DialogViewHelper(Context mContext, int mViewLayoutResId) {
        this();
        mContentView = LayoutInflater.from(mContext).inflate(mViewLayoutResId, null);
    }

    public DialogViewHelper() {
        mViews = new SparseArray<>();
    }

    // 設(shè)置布局
    public void setContentView(View mView) {
        this.mContentView = mView;
    }

    // 設(shè)置TextView
    public void setText(int viewId, CharSequence text) {
        TextView tv = getView(viewId);
        if (tv != null) {
            tv.setText(text);
        }
    }


    // 設(shè)置點(diǎn)擊事件
    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        View tv = getView(viewId);
        if (tv != null) {
            tv.setOnClickListener(listener);
        }
    }

    public <T extends View> T getView(int viewId) {
        WeakReference<View> viewReference = mViews.get(viewId);
        View view = null;
        if (viewReference != null) {
            view = viewReference.get();
        }
        if (view == null) {
            view = mContentView.findViewById(viewId);
            if (view != null)
                mViews.put(viewId, new WeakReference<>(view));
        }
        return (T) view;
    }

    // 獲取Content內(nèi)容的View
    public View getContentView() {
        return mContentView;
    }
}

到這里步鉴,一個根據(jù)AlertDialog源碼搭建的通用型AlertDialog就搭建完成啦。

最后附上一個使用例子:
new AlertDialog.Builder(this)
                .setContentView(R.layout.item_permission)
                .setText(R.id.item_delete_content, getString(R.string.sure_refruse_permission))
                .setOnClickListener(R.id.item_delete_cancel, new NoDoubleClickListener() {
                    @Override
                    public void onNoDoubleClick(View v) {
                        if (mXXPermissions != null && mXXPermissions.isShowing()) {
                            mXXPermissions.dismiss();
                        }
                        Util.checkPermission(MainActivity.this);
                    }
                })
                .setOnClickListener(R.id.item_delete_sure, new NoDoubleClickListener() {
                    @Override
                    public void onNoDoubleClick(View v) {
                        if (mXXPermissions != null && mXXPermissions.isShowing()) {
                            mXXPermissions.dismiss();
                        }
                        requestPermission();
                    }
                })
                .setCancelable(false)
                .fullWith()
                .create();
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末璃哟,一起剝皮案震驚了整個濱河市氛琢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌随闪,老刑警劉巖阳似,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異铐伴,居然都是意外死亡撮奏,警方通過查閱死者的電腦和手機(jī)俏讹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來畜吊,“玉大人泽疆,你說我怎么就攤上這事×嵯祝” “怎么了殉疼?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長捌年。 經(jīng)常有香客問我瓢娜,道長,這世上最難降的妖魔是什么礼预? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任眠砾,我火速辦了婚禮,結(jié)果婚禮上托酸,老公的妹妹穿的比我還像新娘褒颈。我一直安慰自己,他們只是感情好获高,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布哈肖。 她就那樣靜靜地躺著,像睡著了一般念秧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上布疼,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天摊趾,我揣著相機(jī)與錄音,去河邊找鬼游两。 笑死砾层,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的贱案。 我是一名探鬼主播肛炮,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宝踪!你這毒婦竟也來了侨糟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤瘩燥,失蹤者是張志新(化名)和其女友劉穎秕重,沒想到半個月后纺讲,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婿着,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杭跪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年鼻忠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凳兵。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡百新,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出庐扫,到底是詐尸還是另有隱情吟孙,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布聚蝶,位于F島的核電站杰妓,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏碘勉。R本人自食惡果不足惜巷挥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望验靡。 院中可真熱鬧倍宾,春花似錦、人聲如沸胜嗓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辞州。三九已至怔锌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間变过,已是汗流浹背埃元。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留媚狰,地道東北人岛杀。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像崭孤,于是被迫代替她去往敵國和親类嗤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容