恍然大悟,Java
万物皆是对象的真谛,当然Android
也不列外,其实我们在写程序的时候也是在给写每一个对象; 所以我们在Android Studio中所看到的Java源码也是一个个对象的封装体;
一时如蒙雷击,我们看源码也是如此;
- 带着问题进入
- 如果是我写我该如何写?
- 每一个方法是如何调用?
即然如此我们便能无需太多的教学文档,就能驾驭Android
中的基本用法。还能学到一些设计模式和一些写代码的技巧;
这是我第一次看试着把自己看的东西写成博客。写得不好请观者见谅;
Dialog继承结构
创建
创建 - 构造方法 - 重点
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == 0) {
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);
// 构建显示时所用的Window;
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
解说一下重要的代码
// 1.获取Window管理器
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//2.创建一个 Windows 来供 Dialog 挂载布局
final Window w = new PhoneWindow(mContext);
// 3.设置Window和 窗口管理器关联
w.setWindowManager(mWindowManager, null, null);
// 4.
mListenersHandler = new ListenersHandler(this);
只要简单的3步就完成了Dialog
和布局所显示的关联。
WindowManager
是用来干什么的呢?
其实和我们的Activity
一样,我们需要写XML
布局,然后通过LayoutInflater
转化为View
设置给Acvitity
是一样的。我们都需要一个显示的窗口;在Android
里面就叫window
;
简单来说Window是抽象类,具体实现是PhoneWindow,通过WindowManager就可以创建Window。WindowManager是外界访问Window的入口,但是Window的具体实现是在WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。所有的视图例如Activity、Dialog、Toast都是附加在Window上的。
Window w = new PhoneWindow(mContext);
具体关系如下图:
需要深入了解Window
可以看老罗文章:
Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析
显示
在写码的时候发现一个问题,我自己继承
写了一个dialog
MyDialog dialog = new MyDialog(context, R.style.default_dialog_style);
为什么不会调用生命周期中的OnCreate()
方法?一直以为在构建的时候就应该创建。后来才发现自己理解错误,要调用show()
方法才会显示。如下
public void show() {
// 1.判断是否显示
··· 部分代码
mCanceled = false;
// 2.调用OnCreate();
if (!mCreated) {
dispatchOnCreate(null);
}
// 3.启动
onStart();
// 4. 获取到Window跟布局
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);
}
··· 部分代码 -- 设置布局参数
try {
// 5. 添加 布局到 布局管理器 中
mWindowManager.addView(mDecor, l);
mShowing = true;
// 调用 show()示监听回调
sendShowMessage();
} finally {
}
}
可以看到第2条中如果如果 mCreated=false
才会回调dispatchOnCreate()
继续深入:
void dispatchOnCreate(Bundle savedInstanceState) {
if (!mCreated) {
onCreate(savedInstanceState);
mCreated = true;
}
}
// 空实现
protected void onCreate(Bundle savedInstanceState) {
}
由上代码可以看到onCreate
是空实现,我们在自己定义Dialog
时复写onCreate()
才会有实际的作用;
接着看OnStart()
; 也无太多实际作用。设置显示mActionBar
动画;
/**
* Called when the dialog is starting.
*/
protected void onStart() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
}
那么我们是在哪里设置布局的? 如下;布局是设置在mWindow
中的;
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
一般我们是复写在OnCreat()
中。或者直接dialog.setContentView(R.layout.layout_view)
来设置布局;
接着把拿到mDecor
设置个布局管理器中。就是源码中的第5条。那么我们写得布局就显示在我们的程序上面了。由于我们直接加载window
上。因而我们可以直接显示在所以布局的顶部;
关闭对话框
所有对话框都实现了一个接口 DialogInterface
public interface DialogInterface{
... 省略 ...
public void cancel();
public void dismiss();
... 省略 ...
}
Dialog
中还有一个方法hide()
;所以让一个对话框消失有三种方法;我们来看看有什么不同:
hide() ---- 只是让布局看不到,但是没有关闭它;并没有移除屏幕;
public void hide() {
if (mDecor != null) {
mDecor.setVisibility(View.GONE);
}
}
dismiss
@Override
public void dismiss() {
// 判断是否在同一线程
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
//不在同一线程。发送关闭的mHandler来关闭对话框
mHandler.post(mDismissAction);
}
}
// 销毁对话框
void dismissDialog() {
if (mDecor == null || !mShowing) {
return;
}
if (mWindow.isDestroyed()) {
return;
}
// 1. 移除 WindowManger 中 Decor
try {
mWindowManager.removeViewImmediate(mDecor);
} finally {
//2. 销毁必要组件,
if (mActionMode != null) {
mActionMode.finish();
}
mDecor = null;
mWindow.closeAllPanels();
//3. 回调 onStop方法
onStop();
mShowing = false;
// 4. 发送 销毁 通知的监听 最后介绍:
sendDismissMessage();
}
}
注意:官方文档上同时提到了一点注意事项:如果你要进行一些清理工作的话,不要在重写dismiss函数,而应该在onStop函数中进行这些清理工作
cancel 和 dismiss 区别
这样看下来。除了hide()
和其他两个方法有点区别,其他两个好像就没有太大区别了:先说结论吧:
Hide()只是隐藏对话框;dismiss()就是关闭并结束对话框的方法;
cancel()如果没有设置setOnCancelListener()
才会和dismiss()有所不同;
在Dialog
内部有这样一段代码
// 设置打开关闭监听
private static final class ListenersHandler extends Handler {
private WeakReference<DialogInterface> mDialog;
public ListenersHandler(Dialog dialog) {
mDialog = new WeakReference<DialogInterface>(dialog);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case DISMISS:
((OnDismissListener) msg.obj).onDismiss(mDialog.get());
break;
case CANCEL:
((OnCancelListener) msg.obj).onCancel(mDialog.get());
break;
case SHOW:
((OnShowListener) msg.obj).onShow(mDialog.get());
break;
}
}
}
如上代码所理解,如果你在使用Dialog
设置了相应的回调监听,会回调相应的监听方法;
我们在来看看cancel
方法:
public void cancel() {
if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
// Obtain a new message so this dialog can be re-used
Message.obtain(mCancelMessage).sendToTarget();
}
//回调dismis方法;
dismiss();
}
如果你设置了回调监听
public void setOnCancelListener(final OnCancelListener listener)
上面可以得出的结论cancel()
包含dismiss()
。cancel()
会发送关闭的消息通知ListenersHandler
去回调相应的监听;
Dialog的源码大概就是如此了,其实仔细回想下来还是非常简单的,就是对源码的包装,window
的加载而已。难的是加载和显示的过程。这些我就不去深入研究了。
下一篇准备些Dialog
用到的设计模式;里面包含message
和handle
的用法,所以还是有许多可以学习的。
推荐:
网友评论