Android应用开发:Dialog使用123
引言
在Android开发中,我们在很多情况下都会使用到Dialog,即弹出框。如弹出一个登录框,又如有操作需要用户二次确认等情况。本片文章就来阐述一下如何在Android开发过程中,正确的使用Dialog。
Dialog的设计哲学
Dialog是一个弹出框,小的窗口,用来提示用户确认下一步的操作(在确认前这个操作并不执行)或展示额外信息(如下一步的必然操作中确实需要用户知道的信息)。既然是弹出框,就不应该充满全屏(一般情况下),并且要尽可能简短精悍的表达想要展示给用户的意思,剔除一切冗余信息。下图展示了一个标准的Dialog:
1. 标题栏。
用来介绍这个Dialog用来干什么,比如形容自己是在请求用户做什么,或是应用所请求的是什么内容。标题的展现形式可以是单一的文字,也可以是图标+文字。
官方的建议是Dialog如果就是用来警示用户,那么完全可以不需要标题,直接显示内容告诉用户即将发生重要的事情。而有些对于用户来说特别重要的事项,比如丢失数据、产生运营商流量、消费动作等则最好是用最直接的话做标题,然后在内容中做清晰表述。打个比方,如果产生了消费动作,标题就可以起“结账”,内容用来详细表明结的什么账,明细是什么。最好不要在起“警告”这样的标题,因为完全可以用代表警告的图标来表达。
2. 内容。
Dialog主要展示的就是内容了,可以展示多种类型的内容,比如一段重要的描述性话语,或如图中展示的一样是一些单选,同时也可以是多选列表,甚至可以是开发者自定义的任何视图。最需要注意的就是对内容详细或简略的程度进行把控,如果内容过少,那么可以考虑通过交互方式的修改来使用Toast进行展示,毕竟Dialog会阻挡用户的操作,内容较少的Dialog可能会让用户感觉不疼不痒,甚至认为这个提示并不重要,从而降低应用本身的用户体验或者是误操作。
顺便提一下题外话,前一阵子网上爆出的Android最可怕病毒(自动发送短信,传播自身)其实就是滥用Android权限的结果。可见,如果在一些重要动作上不对用户进行必要的提醒、提示,那么某种角度上讲这个应用与“病毒”差不多。
3. 动作按钮
最多两个按钮,一个确定、一个取消,像上图中内容为单选列表,那么就可以理解为点击内容即为“确定”,所以“取消”按钮就不需要了,甚至当Dialog允许通过返回按键或点击Dialog外的区域进行取消时,“取消”按钮也不需要。也就是说,动作按钮这里,可以是2,可以是1,也可以是无。具体的设计逻辑根据Dialog的具体作用决定,如果只是单单的提示,叫用户知道某些信息而已,那么完全可以只放一个“我知道了”按钮,其实这个按钮是“取消”的动作。
对于动作按钮区域并没有什么需要格外注意的,唯一能想到的也就是Android在4.0后将“确定”、“取消”的左右位置换为“确定“在右,”取消“在左。如果一意孤行非要跟主流不一样的话,我想也只能用”呵呵“进行回应了。毕竟是有”用户习惯“这么个名词存在的,当大家都已经习惯了一套规则的时候,所谓”悖论创新“可能也只是”顽皮任性“的挡箭牌而已。
Toast
Dialog算是一个重度的弹出框,在描述”内容“的设计哲学时也说过,如果说内容过少,又只是想提示用户而非叫用户做选择、决定时完全可以考虑使用更加轻量级、对用户体验影响偏小的Toast进行提示。在用户操作上,Toast相比Dialog更加友好,因为Toast在一段时间(Android默认长则3.5秒,短则2秒)后会自动消失,而Dialog必须需要用户进行干涉才可以消失。同时,Toast并不会影响用户在界面上的下一步操作,而Dialog属于一种打断行为,不进行处理就甭想进行下一步操作。信不信由你,如果一个应用能够做到连续弹出3个以上Dialog,用户不是摔手机就一定是卸载应用了。
Dialog的初级开发使用
Android官方建议不要直接使用Dialog这个类来构造应用的弹出框,在开发实践中也很容易发觉确实使用Dialog直接构造弹出框并不方便,基本的视图框架都需要自己来进行设计规划,对于一个简简单单的弹出框来说,这样的实现复杂度明显不符合开发预期。而遵循官方的建议,我们通常使用AlertDialog进行弹出框的构造,同时对于开发过程中可能出现的日期、时间的选择窗口,Android官方也早有准备,分别是DatePickerDialog和TimePickerDialog。(推荐一个开源项目,界面更加友好美观https://github.com/flavienlaurent/datetimepicker)
另,不得不在此提及Android所提供的另一个Dialog,即ProgressDialog。这个Dialog类型是Android官方不建议使用的,原话是:避免使用ProgressDialog。从开发角度上讲,ProgressDialog的使用极其简单,并没有很高的门槛。Android官方这样建议更多是出于设计和用户体验方面的考虑。ProgressDialog的弹出在屏幕上占用了非常小的空间,其他区域罩以灰色蒙板。并且通常情况下ProgressDialog还需要阻挡用户下一步操作,直到其所等待的事件完成为止。即使不考虑可能发生的设备旋转情况,ProgressDialog的单一、阻挡性强、视图不友好这几方面就足以导致用户的不舒适感。所以,在设计、开发过程中,遇到需要用户等待的情况,建议以视图布局中嵌入ProgressBar代替,这样的原因有:
一、整个屏幕不会罩以灰色蒙板,相信很多用户并不喜欢这样的东西;
二、ProgressBar并不会阻挡用户在屏幕上的全部操作。即使ProgressBar在显示中,也不会影响例如NavigationDrawer控件的使用。
三、在开发中,ProgressDialog对于旋转的处理要难于ProgressBar。因此容易出现疏忽,造成用户的混淆或误操作。
下边直接介绍关于AlertDialog的使用明细。
AlertDialog使用示例:
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_DARK); builder.setTitle("Dialog title") .setIcon(android.R.drawable.stat_sys_warning) .setMessage("Dialog content") .setPositiveButton("OK", null) .setNegativeButton("Cancel", null); builder.create().show();
效果图:
通常,应用中的弹出框就是这样构造出来的。
1. 需要显示列表
调用AlertDialog.Builder的接口。
setAdapter(ListAdapter adapter, DialogInterface.OnClickListener listener)
adapter参数即为ListView的Adapter, 用来确定列表的视图。
listener参数为列表项的点击监听器。
视觉效果如:
这时并不需要”确认“与”取消“按钮,直接点击列表项即视为选择。
2. 需要显示单选列表
形如:
调用AlertDialog.Builder的setSingleChoiceItems接口,其形式有四种:
第一种:
setSingleChoiceItems(CharSequence[] items, int checkedItem, DialogInterface.OnClickListener listener)items参数为列表中显示的字符;
checkedItem 因为列表是单选,一般是有一个默认就选中的项目,这个参数就用来指定第几个项目是默认被选中的,从0起。若为-1则代表没有默认选择;
listener参数就是列表项点击监听器。
这种方式适合选项只是简单的字符。
第二种:
setSingleChoiceItems(int itemsId, int checkedItem, DialogInterface.OnClickListener listener)itemsId 参数为指定xml中事先定义好的String array。其他参数意义同第一种。
第三种:
setSingleChoiceItems(ListAdapter adapter, int checkedItem, DialogInterface.OnClickListener listener)
adapter参数为列表的Adapter,用来决定每个列表项的视觉效果
checkedItem和listener的意义同第一种方式中一样。
这种方式适合选项的视图复杂的需求。
第四种:
setSingleChoiceItems (Cursor cursor, int checkedItem, String labelColumn, DialogInterface.OnClickListener listener)
cursor为列表中项目数据所在的数据库Cursor
labelColumn参数为需要展示的数据在数据库中的列名是什么
其他参数与另三种方法无异。
再次提一下在单选项弹出框中动作按钮是否需要展现的问题。建议同时展现”确定“和”取消“按钮。
展现”确认“按钮的原因是在实际使用过程中,用户极有可能轮流点击不同的选项,若开发者视为点击选项就是确定,那么这样用户需要做的重复动作就比较多。用户可能有这样的行为是因为:
一、手指的习惯动作;
二、想要通过选择不同选项直接看到结果,如更换主题(想要看到更换后的直接效果,哪一个符合就选择确定)。
而展现“取消”按钮的原因是:
一、因为弹出框可以对本身是否能够通过点击外部区域进行取消做设置,假设用户使用了多款对各自弹出框进行了不同设置的应用,那么极有可能已经混淆了究竟是否可以通过点击外部区域进行弹出框的取消,所以这个时候直接提供一个“取消”按钮就能够解决用户的疑虑,正如Android设计规范中所说,不要让用户去想,而是提供给用户最直接的选项让他选择,这样做才是较好的用户体验。
二、即使手机的返回按键可以取消弹出框,大多数情况下用户手指从弹出框移动到返回键的距离还是不小的,而且如果是单手操作,这样也可能产生手机滑落的情况发生,最糟的用户体验也莫过于此了。所以,提供一个“取消”按钮,大多数情况下,用户直接点击“取消”来取消弹出框显然更方便、更安全。
3. 需要显示多选列表
形如:
调用AlertDIalog.Builder的setMultiChoiceItems方法,有三种形式:
第一种:
setMultiChoiceItems(CharSequence[] items, boolean[] checkedItems, DialogInterface.OnMultiChoiceClickListener listener)
items参数与单选中的意义一样,是所有项目的字符集合;
checkedItems参数为哪些选项是默认选中的。需要注意的是,如果是null就代表没有默认选中的,而如果是非null,那么长度必须要和items一样。
listener参数为表内项目点击监听器。
第二种:
setMultiChoiceItems(int itemsId, boolean[] checkedItems, DialogInterface.OnMultiChoiceClickListener listener)
itemsId参数将需要显示的所有项目指定到事先定义好的String array上。其他参数与第一种无异。
第三种:
setMultiChoiceItems(Cursor cursor, String isCheckedColumn, String labelColumn, DialogInterface.OnMultiChoiceClickListener listener)
cursor参数为需要显示的项目所在数据库Cursor
labalColumn为项目数据所在数据库中的列名
其他参数与其他方法无异。
到这里可以发现,包含多选列表的弹出框中,并没有通过指定Adapter构造的方法,看似是一种缺失。而联系实际使用,不难发现,复杂视图的多选项弹出框鲜有。我本人是想不到究竟有什么场景需要复杂视图的、多选项的——弹出框。当然,如果一定有这种需求要实现,可完全可以通过自定义视图的弹出框实现(稍后做介绍)。
4. 需要自定视图
自定义视图,指的是自定义Dialog的“内容”区域视图,通过API setView(View view)实现。实现过程也非常简单,可以实现设计好一套布局,然后通过LayoutInflater产出一个View,将这个View设置进去。如果在这个View(“内容”自定义视图)中,需要做必要的监听,同样通过findViewById方法得到需要监听的对象做相应处理即可。
PS:不对AlertDialog.Builder设置Title,最终的Dialog就不会有Title视图。
Dialog开发进阶
通过以上的介绍,相信在对Dialog的使用上起码视图的需求都应该没有问题了。接下来还有一些实际开发过程中的问题需要处理。如:设备发生旋转,上面所做的这些Dialog会发生什么?答案是他们会消失。为什么消失?以开发过程中最熟悉的Activity为例,若设备发生旋转,Activity会先销毁onDestroy,再恢复onCreate,同时相关必要数据的恢复可以通过Bundle进行。而上边所做的这些Dialog并不能处理自己的生命周期,一旦设备发生旋转,会立即由系统dismiss掉。通过引入DialogFragment,使其作为AlertDialog的载体可以完美解决这个问题。
DialogFragment继承于Fragment,专门用来做Dialog的承载体,类似设备旋转这样的事件DialogFragment自然就有了能力去处理,其中的Dialog也不至于被系统直接dismiss掉。
实现一个DialogFragment非常简单,只要实现onCreateDialog方法即可,其原型为:
public Dialog onCreateDialog(Bundle savedInstanceState) { return new Dialog(getActivity(), getTheme()); }
通过直接返回一个Dialog对象即可实现,所以上边实现的第一个AlertDialog就可以转换为:
public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_DARK); builder.setTitle("Dialog title") .setIcon(android.R.drawable.stat_sys_warning) .setMessage("Dialog content") .setPositiveButton("OK", null) .setNegativeButton("Cancel", null); return builder.create() }光是这样Dialog还没有显示出来,像普通的Dialog一样,需要一个show方法的触发,不过通过DialogFragment实现的Dialog的show方法有些不同:
public void show(FragmentManager manager, String tag) public int show(FragmentTransaction transaction, String tag)
不要忘了DialogFragment是继承于Fragment的,要显示他必然需要用到FragmentManager或FragmentTransaction。这里提醒一下,如果是Fragment中调用DialogFragment,在show的时候需要注意使用的是getChildFragmentManager,这样能保证正确的Fragment层级关系。
通过上边对DialogFragment的引入,达到了旋转设备Dialog不消失的效果。但是如果Dialog内容中有状态变动,旋转后会被复位,其实这个问题就是要解决Fragment的旋转屏幕问题。通过onSaveInstanceState与onCreateDialog的配合很好做到这一点。这里不再赘述。
最后,强烈提示!!!实现自己的DialogFragment一定要保留默认构造函数,即空的、public的构造函数,否则会产生程序异常。
总结
本篇文章完整、详尽的从设计到开发介绍了Android系统中关于Dialog的使用详情。本质上,Dialog的使用非常简单,更多的则是关于应用、产品质量与用户体验的追求。无论是移动互联网狂潮也好,还是传统企业也好,质量、用户体验都是需要与时俱进的。·
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。