Android(安卓)弹出式布局之Dialog源码分析
文章目录
关于
Dialog
的基本使用,可以看之前写的一片文章 Android 弹出式布局之Dialog的使用 官方不是不建议直接使用Dialog的,而我们工作中一般是使用AlertDialog或者DialogFragment实现弹框功能
这里简单的给出一个用AlertDialog实现的弹框
AlertDialog dialog = new AlertDialog.Builder(MainActivity.this) .setTitle("这是标题") .setMessage("这是具体的消息内容") .setPositiveButton("确认", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //确认按钮 } }) .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //消息按钮 } }) .create();dialog.show();
从上面的代码中可以看到,是使用了构建者模式(Builder模式)
,巧妙的将操作抽离出来方便自定义操作。
首先看一下new AlertDialog,Builder
做了什么操作。
public Builder(Context context, int themeResId) { P = new AlertController.AlertParams(new ContextThemeWrapper( context, resolveDialogTheme(context, themeResId))); }
可以看到创建了一个AlertController.AlertParams
对象,点进去看看这个AlertController
和AlertController.AlertParams
到底是何方神圣。
public CharSequence mTitle;public View mCustomTitleView;public CharSequence mMessage;...public interface OnPrepareListViewListenerpublic void apply(AlertController dialog)private void createListView(final AlertController dialog)
AlertParams
是AlertController
的内部类,只要定义了一些参数和三个方法,暂时先了解这些,后面在详细看有什么用。
public Builder setTitle(CharSequence title) { P.mTitle = title; return this;}public Builder setMessage(@StringRes int messageId) { P.mMessage = P.mContext.getText(messageId); return this;}
这里AlertDialog
设置的信息实际上是传递给了AlertController.AlertParams
。
public AlertDialog create() { // Context has already been wrapped with the appropriate theme. final AlertDialog dialog = new AlertDialog(P.mContext, 0, false); 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;}
首先通过new AlertDialog(P.mContext, 0, false)
创建了一个Dialog
对象。
AlertDialog(Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0, createContextThemeWrapper); mWindow.alwaysReadCloseOnTouchAttr(); mAlert = AlertController.create(getContext(), this, getWindow());}
在看AlertController.create
方法,return new AlertController(context, di, window);
创建出来一个AlertController
对象。我们再看AlertDialog.Builder#create()
中的apply
方法。
public void apply(AlertController dialog) { if (mCustomTitleView != null) { dialog.setCustomTitle(mCustomTitleView); } else { if (mTitle != null) { dialog.setTitle(mTitle); } ....}
这个方法主要是将通过AlertController.AlertParams转贮的参数又设置给了Dialog对象。
接着我们看AlertDialog
对象的创建,可以看到Dialog
的创建主要由父类完成的,这里在翻一翻Dialog
构造方法。
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { // 由于createContextThemeWrapper在AlertDialog中传递进来是false, //所以mContext最终是使用的创建AlertDialog传递过来的context,而 //我们知道创建dialog对象是不能使用系统的ApplicationContext的, //所以这里应该是Activity的Context对象 if (createContextThemeWrapper) { if (themeResId == ResourceId.ID_NULL) { final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true); themeResId = outValue.resourceId; } mContext = new ContextThemeWrapper(context, themeResId); } else { mContext = context; } mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setOnWindowSwipeDismissedCallback(() -> { if (mCancelable) { cancel(); } }); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this);}
之后创建出来了一个PhoneWindow
对象,它是Window
的唯一子类,具体可以看另一篇文章PhoneWindow源码分析
这里重点关注w.setWindowManager(mWindowManager, null, null);
可以看到调用将windowManager对象设置到了window中。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) { mAppToken = appToken; mAppName = appName; mHardwareAccelerated = hardwareAccelerated || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false); if (wm == null) { wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); } mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }
继续看((WindowManagerImpl)wm).createLocalWindowManager(this);
实现。
public WindowManagerImpl createLocalWindowManager(Window parentWindow) { return new WindowManagerImpl(mContext, parentWindow);}
可以看到实际上是实例话出来了一个WindowManagerImpl
对象,而WindowManagerImpl
是WindowManager
的子类实现。WindowManagerImpl
内部很简单,有这么几个主要的方法。
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); } @Override public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.updateViewLayout(view, params); }@Overridepublic void removeView(View view) { mGlobal.removeView(view, false);}
可以看到所有的操作都是使用了mGlobal
, 而mGlobal
是WindowManagerGlobal
的实例(见private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
)。
而WindowManagerGlobal
负责管理ViewRoot
的创建、销毁操作,由于文章篇幅问题,这里只需要知道到这里就把View添加到window上去了。
接下来看一看show()
方法。这个方法是在Dialog
中。
public void show() { ... mWindowManager.addView(mDecor, l); mShowing = true; sendShowMessage();}
主要是调用WindowManager
将布局添加到了Window中。然后设置显示标记为true。sendShowMessage
用于出发添加相应回调处理。
dismiss
方法和show方法类似,主要还是调用了mWindowManager.removeViewImmediate(mDecor);
将布局移除掉。
更多相关文章
- Android(安卓)内置应用截屏方法
- android自动化测试CTS源码分析之一
- 带你一步一步深入Handler源码,拿下面试官不在话下!
- 使用TestProject Python SDK创建移动Appium测试
- Android(安卓)Fragment中监听事件
- Dagger2使用详解
- android基础(1)
- [Android]【安卓】Json数据的快速拼装和解析
- Parcelable的使用