Android中使用DialogFragment编写对话框的实例教程

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。

使用DialogFragment来管理对话框,当旋转屏幕和按下后退键时可以更好的管理其声明周期,它和Fragment有着基本一致的声明周期。且DialogFragment也允许开发者把Dialog作为内嵌的组件进行重用,类似Fragment(可以在大屏幕和小屏幕显示出不同的效果)。上面会通过例子展示这些好处~
使用DialogFragment至少需要实现onCreateView或者onCreateDIalog方法。onCreateView即使用定义的xml布局文件展示Dialog。onCreateDialog即利用AlertDialog或者Dialog创建出Dialog。

下面我们就来看一个使用DialogFragment编写对话框的例子:
1.重写onCreateView创建Dialog
a)布局文件,我们创建一个设置名称的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content" > 

  <TextView
    android:id="@+id/id_label_your_name"
    android:layout_width="wrap_content"
    android:layout_height="32dp"
    android:gravity="center_vertical"
    android:text="Your name:" /> 

  <EditText
    android:id="@+id/id_txt_your_name"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_toRightOf="@id/id_label_your_name"
    android:imeOptions="actionDone"
    android:inputType="text" /> 

  <Button
    android:id="@+id/id_sure_edit_name"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_below="@id/id_txt_your_name"
    android:text="ok" /> 

</RelativeLayout>

b)继承DialogFragment,重写onCreateView方法

package com.example.zhy_dialogfragment; 

import android.app.DialogFragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; 

public class EditNameDialogFragment extends DialogFragment
{ 

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState)
  {
    View view = inflater.inflate(R.layout.fragment_edit_name, container);
    return view;
  } 

}

c)测试运行:
Main方法中调用:

public void showEditDialog(View view)
  {
    EditNameDialogFragment editNameDialog = new EditNameDialogFragment();
    editNameDialog.show(getFragmentManager(), "EditNameDialog");
  }

效果图:

可以看到,对话框成功创建并显示出来,不过默认对话框有个讨厌的标题,我们怎么去掉呢:可以在onCreateView中调用getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);即可去掉。即:

public class EditNameDialogFragment extends DialogFragment
{ 

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState)
  {
    getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE);
    View view = inflater.inflate(R.layout.fragment_edit_name, container);
    return view;
  } 

}

效果图:

很完美的去掉了讨厌的标题。

2.重写onCreateDialog创建Dialog
在onCreateDialog中一般可以使用AlertDialog或者Dialog创建对话框,不过既然google不推荐直接使用Dialog,我们就使用AlertDialog来创建一个登录的对话框。
a)布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:orientation="vertical" > 

  <ImageView
    android:layout_width="match_parent"
    android:layout_height="64dp"
    android:background="#FFFFBB33"
    android:contentDescription="@string/app_name"
    android:scaleType="center"
    android:src="@drawable/title" /> 

  <EditText
    android:id="@+id/id_txt_username"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="4dp"
    android:layout_marginLeft="4dp"
    android:layout_marginRight="4dp"
    android:layout_marginTop="16dp"
    android:hint="input username"
    android:inputType="textEmailAddress" /> 

  <EditText
    android:id="@+id/id_txt_password"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="16dp"
    android:layout_marginLeft="4dp"
    android:layout_marginRight="4dp"
    android:layout_marginTop="4dp"
    android:fontFamily="sans-serif"
    android:hint="input password"
    android:inputType="textPassword" /> 

</LinearLayout>

b)继承DialogFragment重写onCreateDialog方法

package com.example.zhy_dialogfragment; 

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText; 

public class LoginDialogFragment extends DialogFragment
{ 

  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState)
  {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // Get the layout inflater
    LayoutInflater inflater = getActivity().getLayoutInflater();
    View view = inflater.inflate(R.layout.fragment_login_dialog, null);
    // Inflate and set the layout for the dialog
    // Pass null as the parent view because its going in the dialog layout
    builder.setView(view)
        // Add action buttons
        .setPositiveButton("Sign in",
            new DialogInterface.OnClickListener()
            {
              @Override
              public void onClick(DialogInterface dialog, int id)
              {
              }
            }).setNegativeButton("Cancel", null);
    return builder.create();
  }
}

c)调用

public void showLoginDialog(View view)
  {
    LoginDialogFragment dialog = new LoginDialogFragment();
    dialog.show(getFragmentManager(), "loginDialog");
  }

效果图:

可以看到通过重写onCreateDialog同样可以实现创建对话框,效果还是很nice的。

3.传递数据给Activity
从dialog传递数据给Activity,可以使用“fragment interface pattern”的方式,下面通过一个改造上面的登录框来展示这种模式。
改动比较小,直接贴代码了:

package com.example.zhy_dialogfragment; 

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText; 

public class LoginDialogFragment extends DialogFragment
{
  private EditText mUsername;
  private EditText mPassword; 

  public interface LoginInputListener
  {
    void onLoginInputComplete(String username, String password);
  } 

  @Override
  public Dialog onCreateDialog(Bundle savedInstanceState)
  {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // Get the layout inflater
    LayoutInflater inflater = getActivity().getLayoutInflater();
    View view = inflater.inflate(R.layout.fragment_login_dialog, null);
    mUsername = (EditText) view.findViewById(R.id.id_txt_username);
    mPassword = (EditText) view.findViewById(R.id.id_txt_password);
    // Inflate and set the layout for the dialog
    // Pass null as the parent view because its going in the dialog layout
    builder.setView(view)
        // Add action buttons
        .setPositiveButton("Sign in",
            new DialogInterface.OnClickListener()
            {
              @Override
              public void onClick(DialogInterface dialog, int id)
              {
                LoginInputListener listener = (LoginInputListener) getActivity();
                listener.onLoginInputComplete(mUsername
                    .getText().toString(), mPassword
                    .getText().toString());
              }
            }).setNegativeButton("Cancel", null);
    return builder.create();
  }
}

拿到username和password的引用,在点击登录的时候,把activity强转为我们自定义的接口:LoginInputListener,然后将用户输入的数据返回。
MainActivity中需要实现我们的接口LoginInputListener,实现我们的方法,就可以实现当用户点击登陆时,获得我们的帐号密码了:

c)  MainActivity

package com.example.zhy_dialogfragment; 

import com.example.zhy_dialogfragment.LoginDialogFragment.LoginInputListener; 

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast; 

public class MainActivity extends Activity implements LoginInputListener
{ 

  @Override
  protected void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  } 

  public void showLoginDialog(View view)
  {
    LoginDialogFragment dialog = new LoginDialogFragment();
    dialog.show(getFragmentManager(), "loginDialog"); 

  } 

  @Override
  public void onLoginInputComplete(String username, String password)
  {
    Toast.makeText(this, "帐号:" + username + ", 密码 :" + password,
        Toast.LENGTH_SHORT).show();
  } 

}

效果:

4.DialogFragment做屏幕适配
我们希望,一个对话框在大屏幕上以对话框的形式展示,而小屏幕上则直接嵌入当前的Actvity中。这种效果的对话框,只能通过重写onCreateView实现。下面我们利用上面的EditNameDialogFragment来显示。
EditNameDialogFragment我们已经编写好了,直接在MainActivity中写调用

public void showDialogInDifferentScreen(View view)
  {
    FragmentManager fragmentManager = getFragmentManager();
    EditNameDialogFragment newFragment = new EditNameDialogFragment(); 

    boolean mIsLargeLayout = getResources().getBoolean(R.bool.large_layout) ;
    Log.e("TAG", mIsLargeLayout+"");
    if (mIsLargeLayout )
    {
      // The device is using a large layout, so show the fragment as a
      // dialog
      newFragment.show(fragmentManager, "dialog");
    } else
    {
      // The device is smaller, so show the fragment fullscreen
      FragmentTransaction transaction = fragmentManager
          .beginTransaction();
      // For a little polish, specify a transition animation
      transaction
          .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
      // To make it fullscreen, use the 'content' root view as the
      // container
      // for the fragment, which is always the root view for the activity
      transaction.replace(R.id.id_ly, newFragment)
          .commit();
    }
  }

可以看到,我们通过读取R.bool.large_layout,然后根据得到的布尔值,如果是大屏幕则直接以对话框显示,如果是小屏幕则嵌入我们的Activity布局中
这个R.bool.large_layout是我们定义的资源文件:
在默认的values下新建一个bools.xml

<?xml version="1.0" encoding="utf-8"?>
<resources> 

  <bool name="large_layout">false</bool> 

</resources>

然后在res下新建一个values-large,在values-large下再新建一个bools.xml

<?xml version="1.0" encoding="utf-8"?>
<resources> 

  <bool name="large_layout">true</bool> 

</resources>

最后测试:

左边为模拟器,右边为我的手机~~~~~

5.屏幕旋转
当用户输入帐号密码时,忽然旋转了一下屏幕,帐号密码不见了~~~是不是会抓狂
传统的new AlertDialog在屏幕旋转时,第一不会保存用户输入的值,第二还会报异常,因为Activity销毁前不允许对话框未关闭。而通过DialogFragment实现的对话框则可以完全不必考虑旋转的问题。
我们直接把上面登录使用AlertDialog创建的登录框,拷贝到MainActivity中直接调用:

public void showLoginDialogWithoutFragment(View view)
  {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    // Get the layout inflater
    LayoutInflater inflater = this.getLayoutInflater(); 

    // Inflate and set the layout for the dialog
    // Pass null as the parent view because its going in the dialog layout
    builder.setView(inflater.inflate(R.layout.fragment_login_dialog, null))
        // Add action buttons
        .setPositiveButton("Sign in",
            new DialogInterface.OnClickListener()
            {
              @Override
              public void onClick(DialogInterface dialog, int id)
              {
                // sign in the user ...
              }
            }).setNegativeButton("Cancel", null).show();
  }

下面我分别点击两种方式创建的登录框,看效果图:

可以看到,传统的Dialog旋转屏幕时就消失了,且后台log会报异常~~~使用DialogFragment则不受影响。

PS:再谈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之间的过多进行交叉调用,不利于程序的管控。

(0)

相关推荐

  • Android中DialogFragment自定义背景与宽高的方法

    介绍 DialogFragment在android 3.0时被引入.是一种特殊的Fragment,用于在Activity的内容之上展示一个模态的对话框.典型的用于:展示警告框,输入框,确认框等等. 在DialogFragment产生之前,我们创建对话框:一般采用AlertDialog和Dialog.注:官方不推荐直接使用Dialog创建对话框. 本文主要给大家介绍了关于Android中DialogFragment自定义背景与宽高的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的

  • 详解Android应用中DialogFragment的基本用法

    DialogFragment的基本用法 1. 创建DialogFragment public class DialogA extends DialogFragment implements DialogInterface.OnClickListener { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder

  • Android开发之基于DialogFragment创建对话框的方法示例

    本文实例讲述了Android基于DialogFragment创建对话框的方法.分享给大家供大家参考,具体如下: /** * 使用DialogFragment创建对话框 * @description: * @author ldm * @date 2016-5-12 下午2:00:01 */ public class FragmentAlertDialog extends Activity { private Button button; @Override protected void onCre

  • Android中使用DialogFragment编写对话框的实例教程

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

  • Android中制作自定义dialog对话框的实例分享

    自定义dialog基础版 很多时候,我们在使用android sdk提供的alerdialog的时候,会因为你的系统的不同而产生不同的效果,就好比如你刷的是MIUI的系统,弹出框都会在顶部显示!这里简单的介绍自定义弹出框的应用. 首先创建布局文件dialog: 代码: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.and

  • Android中AlertDialog四种对话框的最科学编写用法(实例代码)

    首先我们上图: xml的代码如下,用于编写按钮: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match

  • 解决Android中自定义DialogFragment解决宽度和高度问题

    关于详解Android应用中DialogFragment的基本用法,大家可以参考下. 1. 概述 DialogFragment在android 3.0时被引入.是一种特殊的Fragment,用于在Activity的内容之上展示一个模态的对话框.典型的用于:展示警告框,输入框,确认框等等. 在DialogFragment产生之前,我们创建对话框:一般采用AlertDialog和Dialog.注:官方不推荐直接使用Dialog创建对话框. 2. 好处与用法 使用DialogFragment来管理对话

  • Android中的指纹识别demo开发实例

    指纹识别是在Android 6.0之后新增的功能,因此在使用的时候需要先判断用户手机的系统版本是否支持指纹识别.另外,实际开发场景中,使用指纹的主要场景有两种: 纯本地使用.即用户在本地完成指纹识别后,不需要将指纹的相关信息给后台. 与后台交互.用户在本地完成指纹识别后,需要将指纹相关的信息传给后台. 由于使用指纹识别功能需要一个加密对象(CryptoObject)该对象一般是由对称加密或者非对称加密获得.上述两种开发场景的实现大同小异,主要区别在于加密过程中密钥的创建和使用,一般来说,纯本地的

  • Android 中隐藏虚拟按键的方法实例代码

    下面通过一段代码给大家讲解android 隐藏虚拟按键的方法,废话不多说了,大家多多看看代码和注释吧,具体代码如下所示: /** * 隐藏虚拟按键,并且全屏 */ protected void hideBottomUIMenu() { //隐藏虚拟按键,并且全屏 if (Build.VERSION.SDK_INT > 11 && Build.VERSION.SDK_INT < 19) { // lower api View v = this.getWindow().getDec

  • Android 中Seekbar详解及简单实例

    Android 中Seekbar详解及简单实例 做到音频播放和音乐播放时,大多数都要用到Seekbar.现在我先简单介绍下Seekbar的几个重要属性. android:max 设置值的大小 . android:thumb="@drawable/" 显示的那个可拖动图标,如果没有设置该参数则为系统默认,如果自己需要重新定义,则将自己需要的图标存放在资源目录 /res/drawable下,然后调用即可. android:thumbOffset 拖动图标的偏量值,可以让拖动图标超过bar的

  • Android 中ViewPager重排序与更新实例详解

    Android 中ViewPager重排序与更新实例详解 最近的项目中有栏目订阅功能,在更改栏目顺序以后需要更新ViewPager.类似于网易新闻的频道管理. 在重新排序之后调用了PagerAdapter的notifyDataSetChanged方法,发现ViewPager并没有更新,于是我开始跟踪源码,在调用PagerAdapter的notifyDataSetChanged方法后,会触发Viewpager的dataSetChanged方法. void dataSetChanged() { //

  • 详解Android中图片的三级缓存及实例

    详解Android中图片的三级缓存及实例 为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响 特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知 所以提出三级缓存策略,通过网络.本地.内存三级缓存图片,来减少不必要的网络交互,避免浪费流量

  • Android中Handler与Message的简单实例

    Android中Handler与Message的简单实例 前言: 虽然笔者已经学习了Android的AsyncTask来实现一部消息的处理.但是在android的学习中,经常会在一些demo中看到Handler与Message的一些使用,所以Handler与Message的学习也是有必要了.至于学多少,笔者还是比较坚持自己的看法,"用多少,学多少",毕竟已经有了AsyncTask如此方便的东西,Handler与Message也不是那么必不可缺了.(如此文的简单了解一下还是不需要花太多时

随机推荐