Android编程之DialogFragment源码详解(一)

DialogFragment是Fragment家族成员之一,如果你把它简单的理解成Dialog,那就错了。它的确可以做作dialog显示,还可以显示出自己定义的Dialog或者AlertDialog,但它同时也是一个Fragment。

按照官方的话来理解就是,你既可以把它当成一个dialog显示出来,也可以让它作为一个Fragment嵌套在Activity中,这样更方便开发。

为什么这么说呢?试想一下,当产品需求最开始把它作为一个界面显示的时候,你可能已经把它作为Fragment已经写好了,但中途产品又把它设计成一个dialog,那你该怎么办?重新去一个dialog或者activity吗?以前的传参怎么办?过两天,产品又将它改回去,你还要再重写一遍吗?等等一系列的问题就来了。

所以,这个时候,你只需要把当前的Fragment继承类改为DialogFragment,再添加几行代码就可以了。其他的,基本都不用动。即使过两天再改回来,或者别的界面也需要它的时候,你就可以直接把它当做Fragment,继续使用,代码都不用改。

这一点,不得不赞一下Fragment这个的出现,大大方便了开发,再也不怕产品设计调整界面布局了!


这里多啰嗦一句,如果产品已经定义好了作为dialog,或者之前就是dialog的,就不要将它们改成DialogFragment的了,还是那句话:只做有意义的代码改动!


DialogFragment的使用用例,官方文档已经写得很清楚了,Demo例子也有,在androidSDK的sample文件夹中,有需要的请自行查阅。

今天之所以翻出源码来,主要还是希望通过对代码的了解,更好使用DialogFragment。而且有些细节部分,不看源码的话,可能就真在方法传值时传错了。比如:style的设置!

在源码最开始部分,就定义了style的常量:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static final int STYLE_NORMAL = 0;  
  2. public static final int STYLE_NO_TITLE = 1;  
  3. public static final int STYLE_NO_FRAME = 2;  
  4. public static final int STYLE_NO_INPUT = 3;  

STYLE_NORMAL:会显示一个普通的dialog

STYLE_NO_TITLE:不带标题的dialog

STYLE_NO_FRAME:无框的dialog

STYLE_NO_INPUT:无法输入内容的dialog,即不接收输入的焦点,而且触摸无效。

说起来,android很多参数的设置,都有用到“|”的方法,表示支持两种或两种以上。最常见的,就是“Top|Left”,所以,在这里有很多人会想用吧:STYLE_NO_TITLE|STYLE_NO_INPUT。那你可就错了,这么用的结果就是把style设置成了:STYLE_NO_INPUT ( 因为:1 | 3 = 3 )


接下来,再看一下它的内部变量:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. int mStyle = STYLE_NORMAL;  
  2. int mTheme = 0;  
  3. boolean mCancelable = true;  
  4. boolean mShowsDialog = true;  
  5. int mBackStackId = -1;  
  6.   
  7. Dialog mDialog;  
  8. boolean mViewDestroyed;  
  9. boolean mDismissed;  
  10. boolean mShownByMe;  

mStyle:默认的样式为STYLE_NORMAL;

mTheme:主题,默认没有设置,而是在setStyle(int style, int theme) 方法中,对其初始化设置,下面会讲到;

mCancelable:是否可以取消,默认是ture,实际上就是设置给Dialog的mDialog.setCancelable(mCancelable);

mShowsDialog:这个参数我放到下面去讲,默认也为true

mBackStackId:后退栈的ID,也就是说,当DialogFragment不显示时,会清空栈里的数据。(关于后退栈的问题,请留心我后面关于FragmentManager详解的文章);

mDialog:DialogFragment之所以会以窗口方式显示,实际上就是其内部有一个Dialog,也就是它,上面的样式、主题、可取消都是设置给它的;

后面三个boolean的变量,就是标志位:

mViewDestroyed:标志位,在Dialog不显示时,处理Fragment的移除操作;

mDismissed:标志位,Dialog是否不显示了;

mShownByMe:标志位,在Dialog是否显示,和mDismissed基本是一对;


DialogFragment的构造方法是空的,什么也没有:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public DialogFragment() {  
  2. }  

然后就是setStyle方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void setStyle(int style, int theme) {  
  2.     mStyle = style;  
  3.     if (mStyle == STYLE_NO_FRAME || mStyle == STYLE_NO_INPUT) {  
  4.         mTheme = android.R.style.Theme_Panel;  
  5.     }  
  6.     if (theme != 0) {  
  7.         mTheme = theme;  
  8.     }  
  9. }  

开头已经讲过了,style使用注意事项。通过这个方法,可以看到,在不设置theme,即为0的情况下,theme会被设置为android.R.style.Theme_Panel。

这还可以根据自己定义的Dialog样式设置进来。


下面就是show方法,共有两个:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void show(FragmentManager manager, String tag) {  
  2.     mDismissed = false;  
  3.     mShownByMe = true;  
  4.     FragmentTransaction ft = manager.beginTransaction();  
  5.     ft.add(this, tag);  
  6.     ft.commit();  
  7. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public int show(FragmentTransaction transaction, String tag) {  
  2.     mDismissed = false;  
  3.     mShownByMe = true;  
  4.     transaction.add(this, tag);  
  5.     mViewDestroyed = false;  
  6.     mBackStackId = transaction.commit();  
  7.     return mBackStackId;  
  8. }  

虽然是两种写法,但实际上还是将自己添加到FragmentManager中而已。


有显示,就是得有不显示的代码:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void dismiss() {  
  2.     dismissInternal(false);  
  3. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void dismissAllowingStateLoss() {  
  2.     dismissInternal(true);  
  3. }  

两个方法都是调用了void dismissInternal(boolean allowStateLoss),只是传参不一样而已,看一下dismissInternal这个方法:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void dismissInternal(boolean allowStateLoss) {  
  2.     if (mDismissed) {  
  3.         return;  
  4.     }  
  5.     mDismissed = true;  
  6.     mShownByMe = false;  
  7.     if (mDialog != null) {  
  8.         mDialog.dismiss();  
  9.         mDialog = null;  
  10.     }  
  11.     mViewDestroyed = true;  
  12.     if (mBackStackId >= 0) {  
  13.         getFragmentManager().popBackStack(mBackStackId,  
  14.                 FragmentManager.POP_BACK_STACK_INCLUSIVE);  
  15.         mBackStackId = -1;  
  16.     } else {  
  17.         FragmentTransaction ft = getFragmentManager().beginTransaction();  
  18.         ft.remove(this);  
  19.         if (allowStateLoss) {  
  20.             ft.commitAllowingStateLoss();  
  21.         } else {  
  22.             ft.commit();  
  23.         }  
  24.     }  
  25. }  

它做了几件事:

1、调用dialog的dismiss方法

2、如果自己在后退栈中,就将自己从后退栈中移除掉(弹出)

3、如果自己不在后退栈中,就将自己从FragmentManager中移除掉。

关于commitAllowingStateLoss与commit的区别,网上有很多讲解,这里也不细说了。以后的FragmentManager中,会对它有更详细的解释。



今天就先写到这里,后面部分下次发布。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值