对话框

qiang.zhang大约 8 分钟

对话框

Window

App界面附着在窗口Window上。大至整个活动页面,小至Toast的提示窗,还有对话框Dialog,都建立在窗口上。如果想熟练掌握对话框,就必须先了解窗口。下面从Window的5个常用方法开始介绍。

setContentView:设置内容视图。我们每天打交道的Activity第一句就是setContentView,查看源码后发现内部原来调用了同名方法getWindow().setContentView。
setLayout:设置内容视图的宽、高尺寸。
setGravity:设置内容视图的对齐方式。
setBackgroundDrawable:设置内容视图的背景。
findViewById:根据资源ID获取该视图的对象。这个方法每个Activity代码都要用许多遍。查看Activity源码后可以发现该方法也是调用Window的同名方法getWindow().findViewById。

AlertDialog

对话框是提示用户做出决定或输入更多信息的小窗口。对话框不会占据整个屏幕,通常适用于需要用户进行操作才能继续执行的模态框事件。

使用 DialogFragment 来管理对话框可确保对话框能正确处理各种生命周期事件,如用户按“返回”按钮或旋转屏幕时。此外,DialogFragment 类还以可嵌入组件的形式在较大界面中重复使用对话框的界面,类似于传统的 Fragment。

下面介绍将 DialogFragment 与 AlertDialog 对象结合使用

创建对话框 fragment

通过扩展 DialogFragment 并在 onCreateDialog() 回调方法中创建 AlertDialog,可以完成各种对话框设计,包括自定义布局以及对话框设计指南中描述的布局。

示例代码:

public class StartGameDialogFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.dialog_start_game)
               .setPositiveButton(R.string.start, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // START THE GAME!
                   }
               })
               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // User cancelled the dialog
                   }
               });
        // Create the AlertDialog object and return it
        return builder.create();
    }
}

  • 调用Show 的时候即可显示在界面上

构建提醒对话框

使用 AlertDialog 类来构建各种对话框设计。通常情况下,该类是您完成构建所需的唯一对话框类。如图所示,提醒对话框有三个区域:

对话框的布局
对话框的布局
标题
此为可选项,仅当内容区域被详细消息、列表或自定义布局占据时才应使用标题。如果需要陈述的是一条简单消息或问题(如图 1 中的对话框),则不需要标题。

内容区域
内容区域可显示消息、列表或其他自定义布局。

操作按钮
对话框中的操作按钮不应超过三个。

示例代码:

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(R.string.dialog_message)
       .setTitle(R.string.dialog_title);
AlertDialog dialog = builder.create();

添加按钮

代码示例:

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Add the buttons
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // User clicked OK button
           }
       });
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // User cancelled the dialog
           }
       });
// Set other dialog properties
...

// Create the AlertDialog
AlertDialog dialog = builder.create();

添加列表

示例图
示例图

代码示例:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle(R.string.pick_color)
           .setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int which) {
               // The 'which' argument contains the index position
               // of the selected item
           }
    });
    return builder.create();
}

创建自定义布局

可以在builder 构建的时候调用 setView 方法,代码示例:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // Get the layout inflater
    LayoutInflater inflater = requireActivity().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.dialog_signin, null))
    // Add action buttons
           .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
               @Override
               public void onClick(DialogInterface dialog, int id) {
                   // sign in the user ...
               }
           })
           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                   LoginDialogFragment.this.getDialog().cancel();
               }
           });
    return builder.create();
}
  • tip: 以对话的形式展示Dialog
<activity android:theme="@android:style/Theme.Holo.Dialog" >

将事件传递回对话框的宿主

定义一个接口,让宿主实现这个接口即可 代码示例:

public class NoticeDialogFragment extends DialogFragment {

    /* The activity that creates an instance of this dialog fragment must
     * implement this interface in order to receive event callbacks.
     * Each method passes the DialogFragment in case the host needs to query it. */
    public interface NoticeDialogListener {
        public void onDialogPositiveClick(DialogFragment dialog);
        public void onDialogNegativeClick(DialogFragment dialog);
    }

    // Use this instance of the interface to deliver action events
    NoticeDialogListener listener;

    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Verify that the host activity implements the callback interface
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            listener = (NoticeDialogListener) context;
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            throw new ClassCastException(activity.toString()
                    + " must implement NoticeDialogListener");
        }
    }
    ...
}

显示对话框

创建一个 DialogFragment 实例并调用 show(),以传递对话框 Fragment 的 FragmentManager 和标记名称。

可以从 FragmentActivity 调用 getSupportFragmentManager()

或从 Fragment 调用 getFragmentManager(),从而获取 FragmentManager。

举例:

public void confirmStartGame() {
    DialogFragment newFragment = new StartGameDialogFragment();
    newFragment.show(getSupportFragmentManager(), "game");
}

第二个参数 "game" 是系统用于保存 Fragment 状态并在必要时进行恢复的唯一标记名称。该标记通过调用 findFragmentByTag() 来获取 Fragment 的句柄 意思是可以通过这个唯一标记找到这个Fragment。

关闭对话框

通过对 DialogFragment 调用 dismiss() 来手动关闭对话框。在 DialogFragment 中实现 onDismiss() 方法。

全屏显示对话框或将其显示为嵌入式 Fragment

让一部分界面在某些情况下显示为对话框,但在其他情况下全屏显示或显示为嵌入式 Fragment(可能取决于设备使用大屏幕还是小屏幕)。

代码示例:

public class CustomDialogFragment extends DialogFragment {
    /** The system calls this to get the DialogFragment's layout, regardless
        of whether it's being displayed as a dialog or an embedded fragment. */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the layout to use as dialog or embedded fragment
        return inflater.inflate(R.layout.purchase_items, container, false);
    }

    /** The system calls this only when creating the layout in a dialog. */
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // The only reason you might override this method when using onCreateView() is
        // to modify any dialog characteristics. For example, the dialog includes a
        // title by default, but your custom layout might not need it. So here you can
        // remove the dialog title, but you must call the superclass to get the Dialog.
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        return dialog;
    }
}

然后,根据屏幕尺寸确定将 Fragment 显示为对话框或全屏界面:



boolean isLargeLayout;

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

    isLargeLayout = getResources().getBoolean(R.bool.large_layout);
}

public void showDialog() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    CustomDialogFragment newFragment = new CustomDialogFragment();

    if (isLargeLayout) {
        // 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.add(android.R.id.content, newFragment)
                   .addToBackStack(null).commit();
    }
}

mIsLargeLayout 布尔值 可以定义在资源里面,一个在 res/values/bools.xml,另一个在res/values-large/bools.xml 这样就能兼容不同屏幕大小的设备了

DatePickerDialog

Android 以可直接使用的对话框形式提供可供用户选择时间或日期的控件。 每个选择器都提供一些控件,以用于选择时间的各个部分(小时、分钟、上午/下午)或日期的各个部分(月、日、年)。使用这些选择器有助于确保用户可以选择格式正确且已根据用户所在的语言区域进行调整的有效时间或日期。

创建日期选择器

与上面类似,过于简单,直接展示代码示例:

public static class DatePickerFragment extends DialogFragment
                            implements DatePickerDialog.OnDateSetListener {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current date as the default date in the picker
        final Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);
        int day = c.get(Calendar.DAY_OF_MONTH);

        // Create a new instance of DatePickerDialog and return it
        return new DatePickerDialog(getActivity(), this, year, month, day);
    }

    public void onDateSet(DatePicker view, int year, int month, int day) {
        // Do something with the date chosen by the user
    }
}

TimePickerDialog

创建时间选择器

使用 DialogFragment 显示 TimePickerDialog,代码示例:

public static class TimePickerFragment extends DialogFragment
                            implements TimePickerDialog.OnTimeSetListener {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);

        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), this, hour, minute,
                DateFormat.is24HourFormat(getActivity()));
    }

    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time chosen by the user
    }
}

ProgressDialog

该类可显示带有进度条的对话框。此 widget 已弃用,因为它会在显示进度时阻止用户与应用进行互动。如果需要指示加载进度或不确定的进度,应遵循进度和 Activity 的设计准则,并在布局中使用 ProgressBar,而非 ProgressDialog。

自动填充框架

这里不做解读,后续看是否需要再另行开章写,简要提一些步骤。

创建 EditText 的一个子类,该子类可以处理 AUTOFILL_TYPE_DATE 类型的值:

  1. 创建一个继承自 EditText 的类。
  2. 实现 getAutofillType() 方法,该方法应返回 AUTOFILL_TYPE_DATE。
  3. 实现 getAutofillValue() 方法,该方法应返回表示日期(以毫秒为单位)的 AutofillValue 对象。如需创建返回对象,请使用 forDate() 方法生成 AutofillValue 对象。
  4. 实现 autofill() 方法。此方法提供了用于处理 AutofillValue 参数(类型为 AUTOFILL_TYPE_DATE)的逻辑。如需处理该参数,请为其创建适当的字符串表示形式,如 mm/yyyy。使用字符串表示形式来设置视图的 text 属性。
  5. 实现以下功能:当用户想要在 EditText 的自定义子类中修改日期时,显示选择器。该视图应根据用户在选择器中选择的值的字符串表示形式来更新 text 属性。
Loading...