详解一:

Android提供alert、prompt、pick-list,单选、多选,progress、time-picker和date-picker对话框,并提供自定义的dialog。在Android 3.0后,dialog基于fragment,并对之前版本提供兼容支持库,也就是说对于开发者而言,dialog是基于DialogFragment的,但此时需要在应用中加入相关的兼容库。

和Windows或者网页JS的Dialog不同,Android的dialog是异步的,而不是同步的。对于同步的dialog,显示dialog后,下一行代码会等到dialog结束,即下一行代码可以知道dialog的输入以及用户点击的button。而对于异步的dialog,dialog显示后,下一行代码继续执行,而不是等dialog消失,通过callback来处理dialog的事件。异步的dialog也意味着应用的代码也可以关闭dialog。

我们的小例子通过菜单触发分别触发告警框和自定义布局提示框,提示框中有三个button,其中一个Help按钮可以再触发一个帮助内容的对话框。

创建dialog fragment

对话框基于DialogFrame,告警框AlterDialogFrament类如下,如何通过newInstance()创建实例在Fragment的学习中已经学过,不再详述。newInstance()有两个参数,一是告警框的标题,一是告警框的内容。

public class AlterDialogFragment extends DialogFragment{   

   /*【步骤1】:通过newInstance()创建实例,并返回,这里的处理和系统从save状态中re-create相同。

    * 1、通过缺省构造函数创建对象

    * 2、将传递的信息设置为fragment的参数

    * 3、返回对象

    * */


    public static AlterDialogFragment newInstance(String title,String message){

        AlterDialogFragment adf = new AlterDialogFragment();

        Bundle bundle = new Bundle();

        bundle.putString("alert-title", title);

        bundle.putString("alert-message", message);

        adf.setArguments(bundle);

        return adf;

    }

   ...... 略,见后文...... 

}

自定义布局提示框PromptDialogFragment同样是DialogFragment的继承。类似的,代码如下:

public class PromptDialogFragment extends DialogFragment{



    public static PromptDialogFragment newInstance(String prompt){

        PromptDialogFragment pdf = new PromptDialogFragment();

        Bundle b = new Bundle();

        b.putString("prompt-message", prompt);

        pdf.setArguments(b);

        return pdf;

    }

    ......略,见后文......

}

Activity显示对话框

在MyActivity中,通过optionsMenu来分别触发告警框和提示框的显示,代码如下:

public class MainActivity extends Activity{

    //设置告警框、提示框和帮助框的dialog fragment的tag。

    public final static String ALERT_DIALOG_TAG = "ALERT_DIALOG_TAG";

    public final static String PROMPT_DIALOG_TAG = "PROMPT_DIALOG_TAG";

    public final static String HELP_DIALOG_TAG = "HELP_DIALOG_TAG";

   

    …... 略 : 设置UI和创建OptionsMenu ......

  

    @Override

    public boolean onOptionsItemSelected(MenuItem item) { 

        switch(item.getItemId()){

        case R.id.alter_dialog:           

            alterDialogTestCase();

            break;

        case R.id.prompt_dialog:

            promptDialogTestCase();

        default:

            break;

        }

        return false;

    } 

  

   /* 触发告警框:通过dialogFragment.show()触发

    * 我们注意对于FragmentTransaction ft,代码中没有执行ft.commit()。查看DialogFragment的show方法的源代码,如下

              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;

                } 

    * 这里面的操作含有ft.add()和ft.commit(),故不需要在代码中重复commit,否则会异常。 add表示加入到activity,这里没有填容器的ID,即contianerViewID为0,表示不加载在具体容器内,对于dialog,container为null。

    * 这本例中也可以通过adf.show(getFragmentManager(), ALERT_DIALOG_TAG)来实现。对于将fragment transaction作为参数的方式,在调用show()之前,可通过fragment transaction进行控制,如加入到back stack中,这将在按提示框的Help按钮弹帮助框中进行演示。在show()中,同时设置了fragment的tag,可用于索引,可在fragment中可以通过getTag()获取。 */


    private void alterDialogTestCase(){ 

        AlterDialogFragment adf = AlterDialogFragment.newInstance("Alert", "This is the Alter Message for test!");

        FragmentTransaction ft = getFragmentManager().beginTransaction();

        adf.show(ft, ALERT_DIALOG_TAG);

    }

    /* 弹出提示框 */

    private void promptDialogTestCase(){ 

        PromptDialogFragment pdf = PromptDialogFragment.newInstance("This is a Prompt Dialog!");

        FragmentTransaction ft = getFragmentManager().beginTransaction();

        pdf.show(ft, PROMPT_DIALOG_TAG);

    }

   

    /* 此为用户按对话框按键时被调用的方法,通过Toast显示相关信息。*/

    public void onDialogDone(String tag, boolean cancelled, CharSequence message) {

        String s = tag + " responds with: " + message;

        if(cancelled)

            s = tag + " was cancelled by the user";

        //Toast是没有button的信息框,在一定时间后消失,很适合用于debug。

        Toast.makeText(this, s, Toast.LENGTH_LONG).show(); 

    }



}

通过fragment实现dialog的好处是:activity配置改变(例如转向)进行重构的情况下,fragment管理器能够自动重够,恢复原来的状态,无需人工干预。

详解二:

DialogFragment的实例newInstance()已经在上一次学习笔记中实现。我们创建dialog的UI,可以通过重写DialogFragment的两个函数当中的一个来实现,这两个函数是onCreateView()和onCreateDialog(),前者返回view,后者返回dialog,如同通过AlertDialog.Builder构造一样。

重写onCreateView()

重写onCreateView()是fragment的传统方式,适合自定义的对话框,本例适合用于提示框,如下图所示。通过按菜单弹出提示框,提示框由一个TextView,一个EditText和三个Button组成UI。按不同的按钮触发不同的处理。小例子自作范例,按Save和Dismiss按钮,都会调用Activity的onDialogDone()函数,根据用户的实际操作,显示不同的信息。按Help按钮,则弹出一个帮助框。再弹框在稍后学习笔记中实现。

通过onCreateView()设置UI和按键反馈

利用Fragment的onCreateView()来实现对话框的UI和Fragment学习中没有差别,在本例中,我们增加了按钮点击的触发,代码如下:

public class PromptDialogFragment extends DialogFragment implements OnClickListener

    public static PromptDialogFragment newInstance(String prompt){

        ...略...

    }



    @Override //通过重写Fragment的onCreateView()实现dialog的UI

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 

        //1、通过inflate,根据layout XML定义,创建view

        View v = inflater.inflate(R.layout.prompt_dialog, container,false);

        TextView tv = (TextView)v.findViewById(R.id.prompt_message);

        tv.setText(getPrompt());

        //2、注册三个button的按键监听listener

        Button dismissBtn = (Button)v.findViewById(R.id.button_dismiss);

        dismissBtn.setOnClickListener(this);         

        Button saveBtn = (Button)v.findViewById(R.id.button_save);

        saveBtn.setOnClickListener(this);         

        Button helpBtn = (Button)v.findViewById(R.id.button_help);

        helpBtn.setOnClickListener(this);

        return v;

    }

    

    private String getPrompt(){

        Bundle b = getArguments();

        return b.getString("prompt-message");

    } 



    @Override //在onCreate中设置对话框的风格、属性等

    public void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState);

        //如果setCancelable()中参数为true,若点击dialog覆盖不到的activity的空白或者按返回键,则进行cancel,状态检测依次onCancel()和onDismiss()。如参数为false,则按空白处或返回键无反应。缺省为true 

        setCancelable(true);

        //可以设置dialog的显示风格,如style为STYLE_NO_TITLE,将被显示title。遗憾的是,我没有在DialogFragment中找到设置title内容的方法。theme为0,表示由系统选择合适的theme。

        int style = DialogFragment.STYLE_NO_NORMAL, theme = 0;

        setStyle(style,theme); 

    } 



    @Override //仅用于状态跟踪

    public void onCancel(DialogInterface dialog) { 

        showInfo("onCancel() is called");

        super.onCancel(dialog);

    }     



    @Override  //仅用户状态跟踪

    public void onDismiss(DialogInterface dialog) { 

        showInfo("onDismiss() is called");

        super.onDismiss(dialog);

    }



    @Override //Button按键触发的回调函数

    public void onClick(View v) { 

        MainActivity act = (MainActivity)getActivity();

        switch(v.getId()){

        case R.id.button_dismiss:

            act.onDialogDone(getTag(), true, null); 
//调用activity的onDialogDone(),通过Toast显示相关信息

            dismiss();  //关闭对话框,并触发onDismiss()回调函数。

            break;

        case R.id.button_help: 

            … 略:以后实现 …

            break;

        case R.id.button_save:

            TextView tv = (TextView)getView().findViewById(R.id.input_text);

            act.onDialogDone(getTag(), false, "[save]" + tv.getText());
//调用activity的onDialogDone(),通过Toast显示相关信息

            dismiss(); //关闭对话框,并触发onDismiss()回调函数

            break;

        default:

            break;

        }

    }

    

    private void showInfo(String s){

        Log.d("PromptDialogFragment",s);   

    }

}

信息保存

如果用户在输入框中填入text,然后进行屏幕的横屏和竖屏切换,这涉及到填入内容的保存,可以通过onSaveInstanceState(),将之保存到fragment的Bundle savedInstanceState中,并在onCreateView()中将之恢复。但是在Android 4.2版本的测试中,系统已经能够自动保存和恢复,无需加入代码。当然,安全地我们仍建议进行以下处理。

public class PromptDialogFragment extends DialogFragment implements OnClickListener{

    private EditText et = null;

    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        ......    

        et = (EditText)v.findViewById(R.id.input_text);

        if(savedInstanceState != null){

            CharSequence text = savedInstanceState.getCharSequence("input");

            et.setText(text == null ? "" : text);

        }

        ......        

    }   

    @Override

    public void onSaveInstanceState(Bundle outState) {

        outState.putCharSequence("input", et.getText());

        super.onSaveInstanceState(outState);

    }

}

重写onCreateDialog()

对于简单的对话框,可以通过AlterDialog.Builder直接创建对话框的UI,本例用于告警框,如下图。AlertDialog.Builder在Android 3.0版本之前的创建对话框方式,在之后的版本中,可用在DialogFragment中,适用于创建简单对话框。

代码如下。虽然都是OnClickListener接口,但提示框的是View.OnClickListener,这里是DialogInterface.OnClickListener。

public class AlterDialogFragment extends DialogFragment implements DialogInterface.OnClickListener

    /*【步骤1】:通过newInstance()创建实例并返回* */

    public static AlterDialogFragment newInstance(String title,String message){

        … 略 … 

    } 

        

    private String getTitle(){

        return getArguments().getString("alert-title");

    }

   

    private String getMessage(){

        return getArguments().getString("alert-message");

    }

   

    /* 【步骤2】创建view可以通过两个途径,一是fragment中的onCreateView(),二是DialogFragment中的onCreateDialog()。

     * 前者适合对自定义的layout进行设置,具有更大的灵活性

     * 而后者适合对简单dialog进行处理,可以利用Dialog.Builder直接返回Dialog对象

     * 从生命周期的顺序而言,先执行onCreateDialog(),后执行oonCreateView(),我们不应同时使用两者。

     * */


    @Override

    public Dialog onCreateDialog(Bundle savedInstanceState) { 

        AlertDialog.Builder b = new AlertDialog.Builder(getActivity())

                                    .setTitle(getTitle())

                                    .setMessage(getMessage())

                                    .setPositiveButton("OK", this)
 
//设置回调函数

                                    .setNegativeButton("Cancel",this); //设置回调函数

        return b.create();

   }
 



    @Override //按键触发的回调函数

    public void onClick(DialogInterface dialog, int which) { 

        boolean isCancel = false;

        if(which == AlertDialog.BUTTON_NEGATIVE){
//判断用户所按何键

            isCancel = true;

        } 

        MyActivity act = (MyActivity) getActivity();

        act.onDialogDone(getTag(), isCancel, "CLick OK, Alert dismissed");

    }



}

详解三:

提示框的按钮Help,将触发弹出新的帮助提示框。

帮助提示框的实现

帮助提示框的实现很简单,利用重写onCreateView( )的方式,点击按钮是执行dismiss(),关闭对话框即可。

代码不在此重复。dialog fragment的关闭有两种方式,一种是在dialog fragment中直接执行dismiss(),我们来看看DialogFragment的源代码片段:

  1. /**
  2. * Dismiss the fragment and its dialog.  If the fragment was added to the
  3. * back stack, all back stack state up to and including this entry will
  4. * be popped.  Otherwise, a new transaction will be committed to remove
  5. * the fragment.
  6. */
  7. public void dismiss() {
  8. dismissInternal(false);
  9. }
  10. void dismissInternal(boolean allowStateLoss) {
  11. if (mDismissed) {
  12. return;
  13. }
  14. mDismissed = true;
  15. mShownByMe = false;
  16. if (mDialog != null) {
  17. mDialog.dismiss();
  18. mDialog = null;
  19. }
  20. mViewDestroyed = true;
  21. if (mBackStackId >= 0) {
  22. getFragmentManager().popBackStack(mBackStackId,
  23. FragmentManager.POP_BACK_STACK_INCLUSIVE);
  24. mBackStackId = -1;
  25. } else {
  26. FragmentTransaction ft = getFragmentManager().beginTransaction();
  27. ft.remove(this);
  28. if (allowStateLoss) {
  29. ft.commitAllowingStateLoss();
  30. } else {
  31. ft.commit();
  32. }
  33. }
  34. }

如果back stack堆栈有该dialog,将其pop出来,否则ft.remove(this); ft.commit();。估计pop的操作也包含ft.remove()和ft.commit()。调用dismiss()会触发onDismiss()回调函数。跟踪状态,如下。

实现再弹框

在PromptDialogFragment中实现弹框的相关代码如下。这里采用另一种关闭dialog的方法,通过fragment transaction进行控制。

public void onClick(View v) { 

    ... ...

    switch(v.getId()){ 

    case R.id.button_help: 

        FragmentTransaction ft  = getFragmentManager().beginTransaction();

       

        /* 如果不执行remove(),prompt dailog在下层,跟踪状态,系统即不会进入onDismiss()状态。主要考虑美观的问题,如果下面prompt对话框大于帮助框,视觉效果不好。下面左图为执行了remove()的效果,右图为不执行remove()的效果。 

        对于Dialog,container为0或者null。 */

        ft.remove(this);

        /* 将当前的PromptDialogFragment加入到回退堆栈,当用户按返回键,或者通过按帮助框的Close按钮dismiss帮助框是,重新显示提示框。
 

       对于back stack的处理,系统具有一定的智能。例如:执行两次addToStackStack(),实际不会重复压栈。 有例如:注释掉remove()语句,即提示框不消失,而是在帮助框的下面,如右图,由于提示框存在,我们并不需要将提示框键入到back stack,但是在实验中发现是否有addToBackStack()都不会结果有影响,系统能够分析到对象存在,不需要压栈。没有去查源代码,猜测通过mBackStackId比对来进行智能处理。*/

        ft.addToBackStack(null);



        HelpDialogFragment hdf = HelpDialogFragment.newInstance(R.string.help_message); 

        /* 对fragment的处理是通过fragment transaction,与在activity弹框一样,通过show()方式实现。 在此之前,我们已经通过transaction将当前的fragment加入到back stack中。*/

        hdf.show(ft,MainActivity.HELP_DIALOG_TAG);


        break;

    ... ... 

    }

}

通过remove()和addToBackStack()使得fragment从UI中消失,当仍可以通过fragment管理器和回退堆栈获取。

再谈fragment管理器

通过fragment管理器或者fragment transaction,我们可以对dialog fragment进行具体地控制。show()就是在管理器中加入fragment,dismiss()就是从管理器中去掉fragment。我们不能先进行add(),然后在进行show(),因此一个fragment对象只能加入管理器一次。如果fragment被dismiss(),将从管理器中删除,我们不能再通过管理器获取该fragment的信息。因此,如果我们想保留被dismiss的dialog的一些状态或信息,需要在dialog外进行保存,例如利用activity。

总结:

编程思想:封装接口

在小例子中,fragment会调用activity的onDialogDone()来显示Toast等信息。在真正项目中,fragment的编写并不需要了解activity的各类方法,好的编程风格是将fragment所涉及的方法以接口的方式封装起来,如下:

public interface OnMyDialogClickListener {

    public void onDialogDone(String tag, boolean cancelled, CharSequence message);

}

在activity中,增加接口的实现,如下:

public class MainActivity extends Activity implements OnMyDialogClickListener{

    ......     

    public void onDialogDone(String tag, boolean cancelled, CharSequence message) {

        String s = tag + " responds with: " + message;

        if(cancelled)

            s = tag + " was cancelled by the user";

        Toast.makeText(this, s, Toast.LENGTH_LONG).show();

        showInfo(s);

    }

}

相应地,在fragment中,对该方法的调用,可以写为:

OnMyDialogClickListener act = (OnMyDialogClickListener)getActivity();

act.onDialogDone(……);

对于一些大型项目,如果我们无法确定activity是否真的实现了接口,可以在fragment的早期,即刚关联activity的阶段进行检测,如下:

@Override

public void onAttach(Activity activity) {

    //onAttach()是合适的早期阶段进行检查MyActivity是否真的实现了接口。

    //采用接口的方式,dialog无需详细了解MyActivity,只需了解其所需的接口函数,这是真正项目中应采用的方式。


    try{

        OnMyDialogClickListener act = (OnMyDialogClickListener)activity;

    }catch(ClassCastException e){

        …... activity并不真正支持接口的异常处理......

    }

    super.onAttach(activity);

}

fragment和activity以其他fragment之间的通信

小例子演示了通过getActivity()获取接口对象或者直接获取activity的对象,实现两者之间的通信。此外fragment也可以通过fragment管理器,通过tag,获取其他fragment实例,从而进行fragment之间的通信。当然从编程思想的角度看,fragment之间的过多进行交叉调用,不利于程序的管控。

DialogFragment详解的更多相关文章

  1. Android学习笔记-Dialog详解

    1.对话框的使用 1.1AlertDialog的显示 简单对话框以及监听的设置:重点掌握三个按钮(也就是三上单词): PositiveButton(确认按钮);NeutralButton(忽略按钮) ...

  2. Linq之旅:Linq入门详解(Linq to Objects)

    示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...

  3. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  4. EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解

    前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...

  5. Java 字符串格式化详解

    Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...

  6. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  7. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  8. Git初探--笔记整理和Git命令详解

    几个重要的概念 首先先明确几个概念: WorkPlace : 工作区 Index: 暂存区 Repository: 本地仓库/版本库 Remote: 远程仓库 当在Remote(如Github)上面c ...

  9. Drawable实战解析:Android XML shape 标签使用详解(apk瘦身,减少内存好帮手)

    Android XML shape 标签使用详解   一个android开发者肯定懂得使用 xml 定义一个 Drawable,比如定义一个 rect 或者 circle 作为一个 View 的背景. ...

随机推荐

  1. dos与unix系统的格式转化

    unix 只用\n作为行结束符,而在 dos中是以\r和\n作为行结束符, 如果一个文件是在unix系统下创建,然后想在dos下使用,就要用unix2dos,如 unix2dos file 如果一个文 ...

  2. python 筛选

    data=[,,,,,,,-,-] result=[x ]

  3. ubuntu 14.04 server(amd64) 安装ros indigo

    1.添加软件源(添加了正确的软件源,操作系统就知道去哪里下载程序,并根据命令自动安装软件) sudo sh -c 'echo "deb http://packages.ros.org/ros ...

  4. socket编程之accept()函数【转载】

    名称 accept() 接收一个套接字中已建立的连接 使用格式 #include <sys/types.h> #include <sys/socket.h> int accep ...

  5. eclipse开发go语言入门案例

    1.配置eclipse下配置GO语言的插件 点击eclipse的“Help”菜单,找到“Install New Software…”菜单项.如下图: 点击“Install New Software…” ...

  6. Java JDK5新特性-静态导入

    2017-10-31 00:10:50 静态导入格式:import static 包名 ...类名.方法名: 也就说可以直接导入到方法名. 注意: 方法必须是静态的 如果有多个同名的静态方法,容易不知 ...

  7. 使用MyBatis Generator自动生成实体、mapper和dao层

    原文链接 通过MyBatis Generator可以自动生成实体.mapper和dao层,记录一下怎么用的. 主要步骤: 关于mybatis从数据库反向生成实体.DAO.mapper: 参考文章:ht ...

  8. rsync+inotify文件同步

    rsync+inotify文件同步 在服务器中,通常结合计划任务.shell脚本来执行本地备份.为了进一步提高备份的可靠性,使用异地备份也是非常重要的,利用rsync工具,可以实现快速.高效的异地备份 ...

  9. Silverlight自定义控件系列 – TreeView (4) 缩进

    接下来是缩进,没有缩进的Tree怎么看都不顺眼. 首先,定义节点深度Depth(注:回叫方法暂没有代码,以后要用到): 1: /// <summary> 2: /// Using a De ...

  10. week 1

    day1 订正 学习AC自动机 day2 mobius反演 对偶图 codeforces day3 ZR模拟赛 订正 day4 复习AC自动机 题库 https://www.cnblogs.com/c ...