Dialog与FragmentDialog源代码分析

时间:2023-03-09 07:17:07
Dialog与FragmentDialog源代码分析

《世界守则》UI一片

注形容自己用语言 android学习之路

转载请保留出处 by Qiao

http://blog.****.net/qiaoidea/article/details/46402845

【导航】

- 弹出式对话框各种方案 从仿QQ消息提示框来谈弹出式对话框的实现方式 (Dialog,PopupWind,自己定义View,Activity。FragmentDialog)

- Dialog源代码解析 从源代码上看Dialog与DialogFragment


1.概述

  前一篇写了经常使用的弹出框的几种实现方式,这里通过源代码来简要解析下Dialog的实现原理。后便作为补充会讲下官方提倡的FragmentDialog。


2.源代码解析

  通常创建非堵塞式对话框的方式就是使用dialog了。只是在Android 3.0 之后,google更推荐使用新引入的基于Fragment的DialogFragment。

这里我们从源代码层次来看下具体实现。

2.1 Dialog

1.DialogInterface

  Dialog对话框实现的接口有DialogInterface,Window.Callback, keyEvent.Callback,OnCreateContextMenuListener,后边几个主要的Activity、View等组件都或多或少实现了。这里側重讲下Dialog专有的DialogInterface。

public interface DialogInterface {
public static final int BUTTON_POSITIVE = -1;
public static final int BUTTON_NEGATIVE = -2;
public static final int BUTTON_NEUTRAL = -3; @Deprecated
public static final int BUTTON1 = BUTTON_POSITIVE;
@Deprecated
public static final int BUTTON2 = BUTTON_NEGATIVE;
@Deprecated
public static final int BUTTON3 = BUTTON_NEUTRAL; public void cancel();
public void dismiss(); interface OnCancelListener {
public void onCancel(DialogInterface dialog);
} interface OnDismissListener {
public void onDismiss(DialogInterface dialog);
} interface OnShowListener {
public void onShow(DialogInterface dialog);
} interface OnClickListener {
public void onClick(DialogInterface dialog, int which);
} interface OnMultiChoiceClickListener {
public void onClick(DialogInterface dialog, int which, boolean isChecked);
} interface OnKeyListener {
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event);
}
}

  比較简单易懂。没什么要说的,定义了最主要的接口方法。一目了然。具体设置和使用都在在Dialog中具体实现。

2.Dialog全局变量

  相同比較清晰容易理解,只是多解释。

    private static final String TAG = "Dialog";
private Activity mOwnerActivity;//关联和创建它的activity final Context mContext;
final WindowManager mWindowManager;
Window mWindow;
View mDecor;
private ActionBarImpl mActionBar; protected boolean mCancelable = true; private String mCancelAndDismissTaken;
private Message mCancelMessage;//取消指令
private Message mDismissMessage;//消失指令
private Message mShowMessage;//显示指令 private OnKeyListener mOnKeyListener;//点击事件 private boolean mCreated = false;
private boolean mShowing = false;
private boolean mCanceled = false; private final Handler mHandler = new Handler(); private static final int DISMISS = 0x43;
private static final int CANCEL = 0x44;
private static final int SHOW = 0x45; private Handler mListenersHandler;//消息指令接受处理handler private ActionMode mActionMode;

3.Dialog构造方法

  基本全部的缺省构造方法都是最后调用到这里:

Dialog(Context context, int theme, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (theme == 0) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme,
outValue, true);
theme = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, theme);
} else {
mContext = context;
} mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
Window w = PolicyManager.makeNewWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}

  theme为零,则使用系统定义的主题风格,createContextThemeWrapper表示是否使用自己的主题风格。

  然后初始化窗口管理器和消息指令处理handler。

  贴上它的缺省构造方法:

    public Dialog(Context context) {
this(context, 0, true);
} public Dialog(Context context, int theme) {
this(context, theme, true);
} @Deprecated
protected Dialog(Context context, boolean cancelable,
Message cancelCallback) {
this(context);
mCancelable = cancelable;
mCancelMessage = cancelCallback;
} protected Dialog(Context context, boolean cancelable,
OnCancelListener cancelListener) {
this(context);
mCancelable = cancelable;
setOnCancelListener(cancelListener);
}

4.Dialog显示 show()

  通过加入dialog至根视图并附加到window窗口中去,同一时候发送dialog显示的消息指令。

 public void show() {
/**
* 假设已经显示了,则重绘actionBar并显示根视图View mDecor
*/
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;//设置取消状态为false /**
* 假设Dialog没有被创建和初始化则调用dispatchOnCreate创建操作
*/
if (!mCreated) {
dispatchOnCreate(null);
} /**
* 初始化ActionBar动画效果和 根视图View
*/
onStart();
mDecor = mWindow.getDecorView(); /**
* 初始化ActionBar文本图片
*/
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
final ApplicationInfo info = mContext.getApplicationInfo();
mWindow.setDefaultIcon(info.icon);
mWindow.setDefaultLogo(info.logo);
mActionBar = new ActionBarImpl(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;
} //加入根视图View到窗口,发送显示指令
try {
mWindowManager.addView(mDecor, l);
mShowing = true; sendShowMessage();//显示消息指令
} finally {
}
} //设置同意ActionBar的显示隐藏动画
protected void onStart() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
} //从消息池去一条消息,发送显示指令给目标handler
private void sendShowMessage() {
if (mShowMessage != null) {
Message.obtain(mShowMessage).sendToTarget();
}
}

  说到这里最好还是看一下处理消息指令的handler和其具体逻辑。

5.ListenersHandler

  不了解Hanler的同学能够參考一下前面在线程更新UI时候讲到的 消息处理系统模型,当中有关于hanlder的简要解说。

  这里看ListenersHandler 的具体逻辑,事实上非常easy。就是调用对应的接口监听事件:

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

6.Dialog的hide()

  方法非常easy,仅仅是让根视图不可见:

    public void hide() {
if (mDecor != null) {
mDecor.setVisibility(View.GONE);
}
}

  真正让dialog移除和消失的是dismiss()方法:

6.Dialog的dismiss()

假设当前线程不是mHandler所在线程,则通过发送消息处理。终于都是运行dismissDialog()方法:
    @Override
public void dismiss() {
if (Looper.myLooper() == mHandler.getLooper()) {
dismissDialog();
} else {
mHandler.post(mDismissAction);
}
} void dismissDialog() {
/**
*假设根视图为空或者当前dialog并没有显示,直接返回
*/
if (mDecor == null || !mShowing) {
return;
} //假设当前窗口已经destoryed掉。直接返回
if (mWindow.isDestroyed()) {
Log.e(TAG, "Tried to dismissDialog() but the Dialog's window was already destroyed!");
return;
} /**
*首先移除根视图View,调用mActionMode的finish()方法,然后清空窗口。改变显示状态并发送dialog消失指令
*/
try {
mWindowManager.removeViewImmediate(mDecor);
} finally {
if (mActionMode != null) {
mActionMode.finish();
}
mDecor = null;
mWindow.closeAllPanels();
onStop();
mShowing = false; sendDismissMessage();
}
} //关闭ActionBar的动画效果
protected void onStop() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
} private void sendDismissMessage() {
if (mDismissMessage != null) {
// Obtain a new message so this dialog can be re-used
Message.obtain(mDismissMessage).sendToTarget();
}
}

7.Dialog取消cancel()

改动状态并发送消息,最后调用的是dismiss()方法
  public void cancel() {
if (!mCanceled && mCancelMessage != null) {
mCanceled = true;
Message.obtain(mCancelMessage).sendToTarget();
}
dismiss();
}

8.Dialog状态保存与恢复

 private static final String DIALOG_SHOWING_TAG = "android:dialogShowing";
private static final String DIALOG_HIERARCHY_TAG = "android:dialogHierarchy"; /**
* 保存dialog当前状态
*/
public Bundle onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putBoolean(DIALOG_SHOWING_TAG, mShowing);
if (mCreated) {
bundle.putBundle(DIALOG_HIERARCHY_TAG, mWindow.saveHierarchyState());
}
return bundle;
} /**
* 载入状态并恢复
*/
public void onRestoreInstanceState(Bundle savedInstanceState) {
final Bundle dialogHierarchyState = savedInstanceState.getBundle(DIALOG_HIERARCHY_TAG);
if (dialogHierarchyState == null) {
// dialog has never been shown, or onCreated, nothing to restore.
return;
}
dispatchOnCreate(savedInstanceState);
mWindow.restoreHierarchyState(dialogHierarchyState);
if (savedInstanceState.getBoolean(DIALOG_SHOWING_TAG)) {
show();
}
}

9.Dialog事件监听

  该部分省略,主要定义了返回事件。按键事件和触摸事件以及长按菜单和焦点变化事件等,能够在源代码中查看具体。

  Dialog 和DialogFragment源代码

2.2 DialogFragment

  android 3.0之后引入Fragment,并推荐DialogFragment代替Dialog,一方面更好的碎片化布局能够内签到基本界面,还有一方面更能有效地在屏幕方向切换时保存状态和恢复。

  DialogFragment继承自Fragment并实现了DialogInterface的OnCancelListener和OnDismissListener 两个接口。

其基本样式有:

  • public static final int STYLE_NORMAL = 0;
  • public static final int STYLE_NO_TITLE = 1;
  • public static final int STYLE_NO_FRAME = 2;
  • public static final int STYLE_NO_INPUT = 3;

1.DialogFragment全局变量

加上前面的几种样式和静态常量Tag。其全局变量也非常easy醒目
    private static final String SAVED_DIALOG_STATE_TAG = "android:savedDialogState";
private static final String SAVED_STYLE = "android:style";
private static final String SAVED_THEME = "android:theme";
private static final String SAVED_CANCELABLE = "android:cancelable";
private static final String SAVED_SHOWS_DIALOG = "android:showsDialog";
private static final String SAVED_BACK_STACK_ID = "android:backStackId"; int mStyle = STYLE_NORMAL;//默认样式
int mTheme = 0;
boolean mCancelable = true;
boolean mShowsDialog = true;
int mBackStackId = -1;//回退栈id Dialog mDialog;
boolean mViewDestroyed;
boolean mDismissed;
boolean mShownByMe;

2.DialogFragment继承和重写Fragment

  看下在DialogFragment中重写的Fragment方法

    //空的构造方法
public DialogFragment() {
} //关联绑定activity
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!mShownByMe) {
// 假设不是通过我们的API创建,则该布局将不再被消失(比如作为activity界面碎片的一部分,而不是作为对话框)
mDismissed = false;
}
} //解除绑定
@Override
public void onDetach() {
super.onDetach();
if (!mShownByMe && !mDismissed) {
// 同上。在家畜绑定的时候才消失
mDismissed = true;
}
} //fragment创建
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); mShowsDialog = mContainerId == 0; //状态恢复重建
if (savedInstanceState != null) {
mStyle = savedInstanceState.getInt(SAVED_STYLE, STYLE_NORMAL);
mTheme = savedInstanceState.getInt(SAVED_THEME, 0);
mCancelable = savedInstanceState.getBoolean(SAVED_CANCELABLE, true);
mShowsDialog = savedInstanceState.getBoolean(SAVED_SHOWS_DIALOG, mShowsDialog);
mBackStackId = savedInstanceState.getInt(SAVED_BACK_STACK_ID, -1);
} } //依据主题风格来返回布局载入器LayoutInflater
@Override
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
if (!mShowsDialog) {
return super.getLayoutInflater(savedInstanceState);
} mDialog = onCreateDialog(savedInstanceState);
switch (mStyle) {
case STYLE_NO_INPUT:
mDialog.getWindow().addFlags(
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
// fall through...
case STYLE_NO_FRAME:
case STYLE_NO_TITLE:
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
}
if (mDialog != null) {
return (LayoutInflater) mDialog.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
return (LayoutInflater) mActivity.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
} //当Activity创建时设置绑定Dialog的对应事件
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); if (!mShowsDialog) {
return;
} View view = getView();
if (view != null) {
if (view.getParent() != null) {
throw new IllegalStateException("DialogFragment can not be attached to a container view");
}
mDialog.setContentView(view);
}
mDialog.setOwnerActivity(getActivity());
mDialog.setCancelable(mCancelable);
mDialog.setOnCancelListener(this);
mDialog.setOnDismissListener(this);
if (savedInstanceState != null) {
Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
if (dialogState != null) {
mDialog.onRestoreInstanceState(dialogState);
}
}
} @Override
public void onStart() {
super.onStart();
if (mDialog != null) {
mViewDestroyed = false;
mDialog.show();
}
} @Override
public void onStop() {
super.onStop();
if (mDialog != null) {
mDialog.hide();
}
} //移除Fragment的时候同一时候移除和置空dialog
@Override
public void onDestroyView() {
super.onDestroyView();
if (mDialog != null) {
mViewDestroyed = true;
mDialog.dismiss();
mDialog = null;
}
}

3.DialogFragment的onCreateDialog()

  该方法负责创建我们的创建Dialog,返回一个Dialog实例

     public Dialog onCreateDialog(Bundle savedInstanceState) {
return new Dialog(getActivity(), getTheme());
}

3.DialogFragment的show()

通过获取Fragment的FragmentTransaction 载入并显示当前布局。
    public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
} public int show(FragmentTransaction transaction, String tag) {
mDismissed = false;
mShownByMe = true;
transaction.add(this, tag);
mViewDestroyed = false;
mBackStackId = transaction.commit();
return mBackStackId;
}

4.DialogFragment的dismiss()

  假设回退栈不为空,则返回前一个栈页面,否则直接移除掉当前页。allowStateLoss參数表示是否同意提交的时候丢失保存的状态,false则状态未保存会抛异常。

     public void dismiss() {
dismissInternal(false);
} public void dismissAllowingStateLoss() {
dismissInternal(true);
} void dismissInternal(boolean allowStateLoss) {
if (mDismissed) {
return;
}
mDismissed = true;
mShownByMe = false;
if (mDialog != null) {
mDialog.dismiss();
mDialog = null;
}
mViewDestroyed = true;
if (mBackStackId >= 0) {
getFragmentManager().popBackStack(mBackStackId,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
mBackStackId = -1;
} else {
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.remove(this);
if (allowStateLoss) {
ft.commitAllowingStateLoss();
} else {
ft.commit();
}
}
}

5.DialogFragment的其它方法

  DialogFragment还定义了获取当前Dialog,设置style和取得是否显示的状态等方法,这里省略。不做赘述。具体查看源代码。

  Dialog 和DialogFragment源代码

  

3.综述

  这两个雷相对照较简单和易于理解。重在实现view的加入。显示、隐藏和移除。

补充: Dialog的Builder模式

  在AlertDialog的实现中。有使用到Builder这么一个静态类。事实上实用的设计模式中的Builder模式。

关于建造者模式(Builder Pattern)。也叫生成器模式。它能将一个复杂对象的构建与它的表示分离开。同一时候使相同的构建过程能够创建不同的表示。

 从AlertDialog简单解释来讲,就是我们不用关心这个dialog是怎样创建,怎么实现,仅仅用简单的通过builder来设计我们想要的结果,他的每一个set方法都返回其对象本身,我们仅仅需将想要的效果和属性附加到这个对象上去就好了。想深入了解的同学。能够自行谷歌/百度 Java设计模式–建造者模式(Builder Pattern).

  

  下一篇。我们将通过自己定义来实现IOS一个常见的ActionSheet样式。demo结合自己定义View,到使用builder模式,重写DialogFragment来讲,具体效果前一篇已经展示了,内容详情请关注接下来的新博文。


版权声明:本文博主原创文章。博客,未经同意不得转载。