Android设计模式之Builder模式

Builder模式的定义

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

Builder模式的使用场景

1.相同的方法,不同的执行顺序,产生不同的结果。

2.多个部件或零件,都可以装配到一个对象中,但产生的运行结果又是不同时。

3.产品类非常复杂,或者产品类中的调用顺序不同产生了不同的作用,这个时候适合使用构造者模式。

4.当初始化一个对象特别复杂,如参数多,且很多参数都是默认值时。

 

Android源码中的Builder模式

public class AlertDialog extends Dialog implements DialogInterface { }

AlertDialog.Builder 通过该Builder来构建复杂的AlertDialog对象

通过Builder对象组装Dialog的各个部分,如title、buttons、Message等,将Dialog的构造和表示分离。

 

private AlertController mAlert; //接收Builder成员变量P中的各个参数

//构造函数
protected AlertDialog(Context context, @StyleRes int themeResId) {
    this(context, themeResId, true);
}

//构造函数 AlertDialog
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
    super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0,
            createContextThemeWrapper);

    mWindow.alwaysReadCloseOnTouchAttr();
    //构造AlertController
    mAlert = AlertController.create(getContext(), this, getWindow());
}
//实际调用的是mAlert的setTitle方法
@Override
public void setTitle(CharSequence title) {
    super.setTitle(title);
    mAlert.setTitle(title);
}


/**
 * @see Builder#setCustomTitle(View)
 */
//实际上调用的是mAlert的setCustomTitle方法
public void setCustomTitle(View customTitleView) {
    mAlert.setCustomTitle(customTitleView);
}

public void setMessage(CharSequence message) {
    mAlert.setMessage(message);
}

 

 

Builder为AlertDialog的内部类

AlertController.AlertParams P 中存储了AlertDialig的各个参数。

//Builder为AlertDialog的内部类
public static class Builder {
    ... ...
}

//储存AlertDialog的各个参数,如title、message、icon等
private final AlertController.AlertParams P;


public Builder(Context context) {
    this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
}


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

下述代码中,Builder类可以设置AlertDialog中的title、message、button等参数,这些参数都存储在类型为AlertController.AlertParams的成员变量P中AlertController.AlertParams包含了与AlertDialog视图中对应的成员变量。在调用Builder类的create函数时会创建AlertDialog,并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。

 

//设置各种参数
/**
 * Set the title displayed in the {@link Dialog}.
 *
 * @return This Builder object to allow for chaining of calls to set methods
 */
public Builder setTitle(CharSequence title) {
    P.mTitle = title;
    return this;
}


/**
 * Set the message to display.
  *
 * @return This Builder object to allow for chaining of calls to set methods
 */
public Builder setMessage(CharSequence message) {
    P.mMessage = message;
    return this;
}


public Builder setView(View view) {
    P.mView = view;
    P.mViewLayoutResId = 0;
    P.mViewSpacingSpecified = false;
    return this;
}


//构造AlertDialog,传递参数
public AlertDialog create() {
    // Context has already been wrapped with the appropriate theme.
   //调用new AlertDialog构造对象,并且将参数传递给个体AlertDialog。
    final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
  //将P中的参数应用到 dialog 中的mAlert对象中。
    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;
}

上述代码中,Builder类可以设置AlertDialog中的title、message、button等参数,这些参数都存储在类型为AlertController.AlertParams的成员变量P中,AlertController.AlertParams包含了与AlertDialog视图中对应的成员变量。在调用Builder类的create函数时会创建AlertDialog,并且将Builder成员变量P中保存的参数应用到AlertDialog的mAlert对象中,即P.apply(dialog.mAlert)代码段。


#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) {
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
                mPositiveButtonListener, null);
    }
    if (mNegativeButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
                mNegativeButtonListener, null);
    }
    if (mNeutralButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
                mNeutralButtonListener, null);
    }
    if (mForceInverseBackground) {
        dialog.setInverseBackgroundForced(true);
    }
    // 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);
    }
    */
}

 

 

 

AlertParams是AlertController 的静态内部类

#AlertParams

apply函数中,只是将AlertParams参数设置到AlertController中,例如将标题设置到Dialog对应的标题视图中等。当我们获取到AlertDialog对象后,通过show函数就可以显示这个对话框

 

public static class AlertParams {
    ... ...
}


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) {
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
                mPositiveButtonListener, null);
    }
    if (mNegativeButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
                mNegativeButtonListener, null);
    }
    if (mNeutralButtonText != null) {
        dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
                mNeutralButtonListener, null);
    }
    if (mForceInverseBackground) {
        dialog.setInverseBackgroundForced(true);
    }
    // For a list, the client can either supply an array of items or an
    // adapter or a cursor
   //如果设置了mItems时,则表示是单选或者多选列表,此时创建的是一个ListView。
    if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
        createListView(dialog);
    }
    //将mView设置给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);
    }
    */
}

在apply函数中,只是将AlertParams参数设置到AlertController中,例如将标题设置到Dialog对应的标题视图中等。当我们获取到AlertDialog对象后,通过show函数就可以显示这个对话框。
/**
 * Creates an {@link AlertDialog} with the arguments supplied to this
 * builder and immediately displays the dialog.
 * <p>
 * Calling this method is functionally identical to:
 * <pre>
 *     AlertDialog dialog = builder.create();
 *     dialog.show();
 * </pre>
 */
#AlertDialog.Builder
public AlertDialog show() {
    final AlertDialog dialog = create();
    dialog.show();
    return dialog;
}

 

Dialog的show函数如下:

 

 

public class AlertDialog extends Dialog implements DialogInterface {

... ...

}

 

 

/**
 * Start the dialog and display it on screen.  The window is placed in the
 * application layer and opaque.  Note that you should not override this
 * method to do initialization when the dialog is shown, instead implement
 * that in {@link #onStart}.
 */
#Dialog
public void show() {
    //已经是显示状态,则return
    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;
    //1.onCreate调用
    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();
    //获取DecorView
    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();
    if ((l.softInputMode
            & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
        WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
        nl.copyFrom(l);
        nl.softInputMode |=
                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
        l = nl;
    }
   //将mDecor添加到WindowManager中
    mWindowManager.addView(mDecor, l);
    mShowing = true;
   //发送一个显示Dialog的消息
    sendShowMessage();
}

 

在show函数中主要做如下几件事:

1.通过dispatchOnCreate函数来调用AlertDialog的onCreate函数。

2.然后调用AlertDialog的onStart函数。

3.最后将Dialog的DecorView添加到WindowManager中。

这是一系列典型的生命周期函数。按照惯例,AlertDialog的内容视图构建应该在onCreate函数中。

AlertDialog的onCreate函数:
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       // 调用了AlertController的installContent方法
        mAlert.installContent();
    }

在onCreate函数中主要调用了AlertController的installContent方法,Dialog中的onCreate函数只是一个空实现。AlertDialog的内容视图必然在installContent函数中。

#AlertController
public void installContent() {
        //设置窗口没有title
	mWindow.requestFeature(Window.FEATURE_NO_TITLE);
        int contentView = selectContentView();
        //设置窗口的内容视图布局
        mWindow.setContentView(contentView);
        //初始化AlertDialog的其他子视图的内容
        setupView();
    }

 

installContent函数的代码很少,但极为重要,它调用了Window对象的setContentView,这个setContentView就与Activity中的一样,实际上Activity最终也是调用Window对象的setContentView函数。这里是设置AlertDialog的内容布局,这个布局就是mAlertDialogLayout字段值。这个值在AlertController的构造函数中初始化。

#AlertController
  protected AlertController(Context context, DialogInterface di, Window window) {
        mContext = context;
        mDialogInterface = di;
        mWindow = window;
        mHandler = new ButtonHandler(di);

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

        // modify begin by sunliang for AlertDialog 20170721
        useGomeTheme = a.getBoolean(R.styleable.AlertDialog_useGomeTheme, false);
        if(useGomeTheme){
            mAlertDialogLayout = a.getResourceId(
                com.gome.R.styleable.AlertDialog_layout, com.gome.R.layout.gome_alert_dialog);
        } else {
           //AlertDialog的布局id,也就是alert_dialog.xml布局
            mAlertDialogLayout = a.getResourceId(
                R.styleable.AlertDialog_layout, R.layout.alert_dialog);
        }
        if(useGomeTheme) {
            mButtonPanelSideLayout = a.getResourceId(
                com.gome.R.styleable.AlertDialog_buttonPanelSideLayout, 0);
        } else {
            mButtonPanelSideLayout = a.getResourceId(
                R.styleable.AlertDialog_buttonPanelSideLayout, 0);
        }
        if(useGomeTheme) {
            mListLayout = R.layout.select_dialog_gome;
        } else {
            mListLayout = a.getResourceId(
                R.styleable.AlertDialog_listLayout, R.layout.select_dialog);
        }

        if(useGomeTheme) {
            mMultiChoiceItemLayout = a.getResourceId(
                com.gome.R.styleable.AlertDialog_multiChoiceItemLayout,
                com.gome.R.layout.gome_select_dialog_multichoice);
        } else {
            mMultiChoiceItemLayout = a.getResourceId(
                R.styleable.AlertDialog_multiChoiceItemLayout,
                R.layout.select_dialog_multichoice);
        }
        if(useGomeTheme) {
            mSingleChoiceItemLayout = a.getResourceId(
                com.gome.R.styleable.AlertDialog_singleChoiceItemLayout,
                com.gome.R.layout.gome_select_dialog_singlechoice);
        } else {
            mSingleChoiceItemLayout = a.getResourceId(
                R.styleable.AlertDialog_singleChoiceItemLayout,
                    com.gome.internal.R.layout.gome_select_singlechoice);
        }
        if(useGomeTheme) {
            mListItemLayout = a.getResourceId(
                com.gome.R.styleable.AlertDialog_listItemLayout,
                com.gome.R.layout.gome_select_dialog_item);
        } else {
            mListItemLayout = a.getResourceId(
                R.styleable.AlertDialog_listItemLayout,
                R.layout.select_dialog_item);
        }
        //modify end by sunliang for AlertDialog 20170721
        mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);

        a.recycle();

        /* We use a custom title so never request a window title */
        //window.requestFeature(Window.FEATURE_NO_TITLE);
    }

 

当通过Builder对象的setTitle、setMessage等方法设置具体内容时,就是将这些内容填充到对应的视图中。而AlertDialog也允许你通过setView传入内容视图,这个内容视图就是替换掉蓝色区域。AlertDialog预留了一个costomPanel区域用来显示用户自定义的内容视图。

private void setupView() {
        //modify begin by sunliang for AlertDialog 20170721
//      final View parentPanel = mWindow.findViewById(R.id.parentPanel);
//      final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
//      final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
//      final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);

        View parentPanel;
       //获取并初始化title区域
        if(useGomeTheme) {
            parentPanel = mWindow.findViewById(com.gome.R.id.parentPanel);
        } else {
            parentPanel = mWindow.findViewById(R.id.parentPanel);
        }

        View defaultTopPanel;
        if(useGomeTheme) {
            defaultTopPanel = parentPanel.findViewById(com.gome.R.id.topPanel);
        } else {
            defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
        }

        View defaultContentPanel;
        if(useGomeTheme) {
            defaultContentPanel = parentPanel.findViewById(com.gome.R.id.contentPanel);
        } else {
            defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
        }

        View defaultButtonPanel;
       // 获取并初始化内容区域
        if(useGomeTheme) {
            defaultButtonPanel = parentPanel.findViewById(com.gome.R.id.buttonPanel);
        } else {
            defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
        }

        // Install custom content before setting up the title or buttons so
        // that we can handle panel overrides.
//        final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
//        setupCustomContent(customPanel);
//
//        final View customTopPanel = customPanel.findViewById(R.id.topPanel);
//        final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
//        final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
//
//        // Resolve the correct panels and remove the defaults, if needed.
//        final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
//        final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
//        final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
        ViewGroup customPanel;
        if(useGomeTheme) {
            customPanel = (ViewGroup) parentPanel.findViewById(com.gome.R.id.customPanel);
        } else {
            customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
        }
        setupCustomContent(customPanel);

        View customTopPanel;
        if(useGomeTheme) {
            customTopPanel = customPanel.findViewById(com.gome.R.id.topPanel);
        } else {
            customTopPanel = customPanel.findViewById(R.id.topPanel);
        }

        View customContentPanel;
        if(useGomeTheme) {
            customContentPanel = customPanel.findViewById(com.gome.R.id.contentPanel);
        } else {
            customContentPanel = customPanel.findViewById(R.id.contentPanel);
        }

        View customButtonPanel;
        if(useGomeTheme) {
            customButtonPanel = customPanel.findViewById(com.gome.R.id.buttonPanel);
        } else {
            customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
        }
        //modify end by sunliang for AlertDialog 20170721

        // Resolve the correct panels and remove the defaults, if needed.
        final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
        final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
        final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);

        setupContent(contentPanel);
       //初始化按钮
        setupButtons(buttonPanel);
        setupTitle(topPanel);

        final boolean hasCustomPanel = customPanel != null
                && customPanel.getVisibility() != View.GONE;
        final boolean hasTopPanel = topPanel != null
                && topPanel.getVisibility() != View.GONE;
        final boolean hasButtonPanel = buttonPanel != null
                && buttonPanel.getVisibility() != View.GONE;

        // Only display the text spacer if we don't have buttons.
        if (!hasButtonPanel) {
            if (contentPanel != null) {
                final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
                if (spacer != null) {
                    spacer.setVisibility(View.VISIBLE);
                }
            }
            mWindow.setCloseOnTouchOutsideIfNotSet(true);
        }

        if (hasTopPanel) {
            // Only clip scrolling content to padding if we have a title.
            if (mScrollView != null) {
                mScrollView.setClipToPadding(true);
            }

            // Only show the divider if we have a title.
            View divider = null;
            if (mMessage != null || mListView != null || hasCustomPanel) {
                if (!hasCustomPanel) {
                    divider = topPanel.findViewById(R.id.titleDividerNoCustom);
                }
                if (divider == null) {
                    divider = topPanel.findViewById(R.id.titleDivider);
                }

            } else {
                divider = topPanel.findViewById(R.id.titleDividerTop);
            }

            if (divider != null) {
                divider.setVisibility(View.VISIBLE);
            }
        } else {
            if (contentPanel != null) {
                final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle);
                if (spacer != null) {
                    spacer.setVisibility(View.VISIBLE);
                }
            }
        }

        if (mListView instanceof RecycleListView) {
            ((RecycleListView) mListView).setHasDecor(hasTopPanel, hasButtonPanel);
        }

        // Update scroll indicators as needed.
        if (!hasCustomPanel) {
            final View content = mListView != null ? mListView : mScrollView;
            if (content != null) {
                final int indicators = (hasTopPanel ? View.SCROLL_INDICATOR_TOP : 0)
                        | (hasButtonPanel ? View.SCROLL_INDICATOR_BOTTOM : 0);
                content.setScrollIndicators(indicators,
                        View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
            }
        }

        final TypedArray a = mContext.obtainStyledAttributes(
                null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
       //设置背景
        setBackground(a, topPanel, contentPanel, customPanel, buttonPanel,
                hasTopPanel, hasCustomPanel, hasButtonPanel);
        a.recycle();
    }
}
private void setupCustomContent(ViewGroup customPanel) {
    final View customView;
如果用户设置了内容视图,那么将它显示在customPanel的custom布局里面
    if (mView != null) {
        customView = mView;
    } else if (mViewLayoutResId != 0) {
        final LayoutInflater inflater = LayoutInflater.from(mContext);
        customView = inflater.inflate(mViewLayoutResId, customPanel, false);
    } else {
        customView = null;
    }

    final boolean hasCustomView = customView != null;
    if (!hasCustomView || !canTextInput(customView)) {
        mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
    }

    if (hasCustomView) {
        //modify begin by sunliang for AlertDialog 20170721
//            final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
        FrameLayout custom;
        if(useGomeTheme) {
            custom = (FrameLayout) mWindow.findViewById(com.gome.R.id.custom);
        } else {
            custom = (FrameLayout) mWindow.findViewById(R.id.custom);
        }
        //modify end by sunliang for AlertDialog 20170721
 //显示用户设置的视图
        custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));

        if (mViewSpacingSpecified) {
            custom.setPadding(
                    mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
        }

        if (mListView != null) {
            ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
        }
    } else {
        customPanel.setVisibility(View.GONE);
    }
}

 

setupView顾名思义就是初始化AlertDialog布局的各个部分,在该函数调用之后,整个Dialog的视图内容全部设置完毕。这些各区域的视图都属于mAlertDialogLayout布局中的子元素,Window对象又关联了mAlertDialogLayout的整个布局树,调用完setupView之后整个视图树的数据填充完毕,当用户调用show函数时,WindowManager将会将Window对象的DecorView添加到用户窗口上。

 

注意AlertDialog的setView方法和setContentView方法:

setView方法需要在show方法之前调用。setView是AlertDialog的方法,在AlertDialog中调用AlertController中的setView。而在AlertController中这个setView则是指的CustomView的部分而不是整个窗体。

注意AlertDialog的setView方法和setContentView方法:

setView方法需要在show方法之前调用。setView是AlertDialog的方法,在AlertDialog中调用AlertController中的setView。而在AlertController中这个setView则是指的CustomView的部分而不是整个窗体。

 

 

setContentView实际调用的是PhonWindow的setContentView方法,其设置的是整个窗口的布局。

 

 

 

 

参考《android源码设计模式》

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值