Android 撸起袖子,自己封装 DialogFragment

前言

具体的代码以及示例我都放上 Github 了,有需要的朋友可以去看一下 DialogFragmentDemos,欢迎 star 和 fork.

本文的主要内容

  • DialogFragment 是什么
  • 创建通用的 CommonDialogFragment
  • 实现各种类型的 DialogFragment

在写正文之前,先来一波效果展示吧
DialogFragmentDemos.gif

一、DialogFragment 是什么

DialogFragment 在 Android 3.0 时被引入,是一种特殊的 Fragment,用于在 Activity 的内容之上显示一个静态的对话框。例如:警告框、输入框、确认框等。

1、DialogFragment 的优点

其实在 Android 中显示对话框有两种类型可供使用,一种是 DialogFragment,而另一种则是 Dialog。在 DialogFragment 产生之前,我们创建对话框一般采用 Dialog,而且从代码的编写角度来看,Dialog 使用起来其实更加简单,但是 Google 却是推荐尽量使用 DialogFragment,是不是感觉很奇怪,其实原因也很简单, DialogFragment 有着 Dialog 所没有的非常好的特性

  • DialogFragment 本身是 Fragment 的子类,有着和 Fragment 基本一样的生命周期,使用 DialogFragment 来管理对话框,当旋转屏幕和按下后退键的时候可以更好的管理其生命周期
  • 在手机配置变化导致 Activity 需要重新创建时,例如旋转屏幕,基于 DialogFragment 的对话框将会由 FragmentManager 自动重建,然而基于 Dialog 实现的对话框却没有这样的能力

2、DialogFragment 的使用

使用 DialogFragment 至少需要实现 onCreateView() 或者 onCreateDialog() 方法,onCreateView() 即使用自定义的 xml 布局文件来展示 Dialog,而 onCreateDialog() 即使用 AlertDialog 或者 Dialog 创建出 我们想要的 Dialog,因为这篇文章主要是讲 DialogFragment 的封装,至于 DialogFragment 具体的使用,可以参考下洋神的这篇文章 Android 官方推荐 : DialogFragment 创建对话框

二、创建通用的 CommonDialogFragment

这个类是 DialogFragment 的子类,对 DialogFragment 进行封装,依赖外部传入的 AlertDialog 来构建,同时也处理了 DialogFragment 中 AlertDialog 不能设置外部取消的问题

public class CommonDialogFragment extends DialogFragment {/*** 监听弹出窗是否被取消*/private OnDialogCancelListener mCancelListener;/*** 回调获得需要显示的dialog*/private OnCallDialog mOnCallDialog;public interface OnDialogCancelListener {void onCancel();}public interface OnCallDialog {Dialog getDialog(Context context);}public static CommonDialogFragment newInstance(OnCallDialog call, boolean cancelable) {return newInstance(call, cancelable, null);}public static CommonDialogFragment newInstance(OnCallDialog call, boolean cancelable, OnDialogCancelListener cancelListener) {CommonDialogFragment instance = new CommonDialogFragment();instance.setCancelable(cancelable);instance.mCancelListener = cancelListener;instance.mOnCallDialog = call;return instance;}@Overridepublic Dialog onCreateDialog(Bundle savedInstanceState) {if (null == mOnCallDialog) {super.onCreateDialog(savedInstanceState);}return mOnCallDialog.getDialog(getActivity());}@Overridepublic void onStart() {super.onStart();Dialog dialog = getDialog();if (dialog != null) {//在5.0以下的版本会出现白色背景边框,若在5.0以上设置则会造成文字部分的背景也变成透明if(Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {//目前只有这两个dialog会出现边框if(dialog instanceof ProgressDialog || dialog instanceof DatePickerDialog) {getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));}}Window window = getDialog().getWindow();WindowManager.LayoutParams windowParams = window.getAttributes();windowParams.dimAmount = 0.0f;window.setAttributes(windowParams);}}@Overridepublic void onCancel(DialogInterface dialog) {super.onCancel(dialog);if (mCancelListener != null) {mCancelListener.onCancel();}}}

可以看到这个类的代码量也是很少的,先定义了两个接口 OnDialogCancelListener,OnCallDialog,前者用于监听弹出窗是否被取消,后者则可以让我们回调获得想要显示的 Dialog,可以看到在 onCreateDialog() 中我们返回的 是 mOnCallDialog.getDialog(getActivity);,当我们在传入 Dialog 的时候,便会回调到此处,让 onCreateDialog() 返回我们传入的 Dialog,对接口回调不是很清楚的朋友,可以看下这篇文章 一个经典例子让你彻彻底底理解java回调机制

接着在 onStart() 中进行了一些特殊性的处理,因为在 5.0 以下的版本,ProgressDialog 和 DatePickerDialog 会出现白色的边框,这使得用户体验非常不好,所以我们要在此处进行相应的处理

最后便是封装我们的构造函数
newInstance(OnCallDialog call, boolean cancelable, OnDialogCancelListener cancelListener),当我们要使用这个 CommonDialogFragment 的时候,先 new 一个 OnCallDialog,将我们想要显示的 Dialog 传进去,cancelable,用于设置对话框是否能被取消,可以看到在 onCancel() 有这样一段代码

if(mCancelListener != null){mCancelListener.onCancel();
}

这便是我们在构造函数中传入 OnCancelListener 的原因,当我们想要做一些取消对话框后的处理时,只要在构造函数中传入 OnCancelListener,实现 onCancel() 方法就行了

三、实现各种类型的 DialogFragment

既然前面我们创建了 CommonFragment 作为所有 DialogFragment 的基类,那么接下来我们当然要好好地来实现各种类型的 DialogFragment 了,我的思路是创建一个 DialogFragmentHelper 作为实现提示框的帮助类,帮我们把代码都封装起来,使用的时候只需要关注与 AlertDialog 的交互,Helper 会帮助我们用 DialogFragment 来进行显示,这样既能统一整个应用的 Dialog 风格,又能让我们实现各种各样的对话框变得相当的简单

在实现 DialogFragmentHelper 之前我们有两件事先要做一下

1、在 styles 文件中定义我们定义我们对话框的风格样式
<style name="Base_AlertDialog" parent="Base.Theme.AppCompat.Light.Dialog"><!--不设置在6.0以上会出现,宽度不充满屏幕的情况--><item name="windowMinWidthMinor">90%</item><!-- 取消标题栏,如果在代码中settitle的话会无效 --><item name="android:windowNoTitle">true</item><!-- 标题的和Message的文字颜色 --><!--<item name="android:textColorPrimary">@color/black</item>--><!-- 修改顶部标题背景颜色,具体颜色自己定,可以是图片 --><item name="android:topDark">@color/app_main_color_deep</item><!--<item name="android:background">@color/white</item>--><!-- 在某些系统上面设置背景颜色之后出现奇怪的背景,处这里设置背景为透明,为了隐藏边框 --><!--<item name="android:windowBackground">@android:color/transparent</item>--><!--<item name="android:windowFrame">@null</item>--><!-- 进入和退出动画,左进右出(系统自带) --><!--<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>--><!-- 按钮字体颜色,全部一起改,单个改需要在Java代码中修改 --><item name="colorAccent">@color/app_main_color</item></style>

我已经打上了详细的注释,相信应该很容易理解

2、写一个接口,用于 DialogFragmentHelper 与逻辑层之间进行数据监听
public interface IDialogResultListener<T> {void onDataResult(T result);
}

准备工作做完了,就让我们开工撸 DialogFragmentHelper 吧,因为篇幅有限,我只是代表性的选了其中的一些效果来讲,具体的代码,可以参考下 DialogFragmentDemos

public class DialogFragmentHelper {private static final String TAG_HEAD = DialogFragmentHelper.class.getSimpleName();/*** 加载中的弹出窗*/private static final int PROGRESS_THEME = R.style.Base_AlertDialog;private static final String PROGRESS_TAG = TAG_HEAD + ":progress";public static CommonDialogFragment showProgress(FragmentManager fragmentManager, String message){return showProgress(fragmentManager, message, true, null);}public static CommonDialogFragment showProgress(FragmentManager fragmentManager, String message, boolean cancelable){return showProgress(fragmentManager, message, cancelable, null);}public static CommonDialogFragment showProgress(FragmentManager fragmentManager, final String message, boolean cancelable, CommonDialogFragment.OnDialogCancelListener cancelListener){CommonDialogFragment dialogFragment = CommonDialogFragment.newInstance(new CommonDialogFragment.OnCallDialog() {@Overridepublic Dialog getDialog(Context context) {ProgressDialog progressDialog = new ProgressDialog(context, PROGRESS_THEME);progressDialog.setMessage(message);return progressDialog;}}, cancelable, cancelListener);dialogFragment.show(fragmentManager, PROGRESS_TAG);return dialogFragment;}/*** 带输入框的弹出窗*/private static final int INSERT_THEME = R.style.Base_AlertDialog;private static final String INSERT_TAG  = TAG_HEAD + ":insert";public static void showInsertDialog(FragmentManager manager, final String title, final IDialogResultListener<String> resultListener, final boolean cancelable){CommonDialogFragment dialogFragment = CommonDialogFragment.newInstance(new CommonDialogFragment.OnCallDialog() {@Overridepublic Dialog getDialog(Context context) {// ...AlertDialog.Builder builder = new AlertDialog.Builder(context, INSERT_THEME);builder.setPositiveButton(DIALOG_POSITIVE, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {if(resultListener != null){resultListener.onDataResult(editText.getText().toString());}}});builder.setNegativeButton(DIALOG_NEGATIVE, null);return builder.create();}}, cancelable, null);dialogFragment.show(manager, INSERT_TAG);}
}

可以看到因为我们实现封装了 CommonFragment,所有这些效果的实现都变得相当的简单吗,这便是封装给我们带来的便利和好处。

就以 加载中的弹出窗 为例,来看看我们是怎么实现的

    public static CommonDialogFragment showProgress(FragmentManager fragmentManager, final String message, boolean cancelable, CommonDialogFragment.OnDialogCancelListener cancelListener){CommonDialogFragment dialogFragment = CommonDialogFragment.newInstance(new CommonDialogFragment.OnCallDialog() {@Overridepublic Dialog getDialog(Context context) {ProgressDialog progressDialog = new ProgressDialog(context, PROGRESS_THEME);progressDialog.setMessage(message);return progressDialog;}}, cancelable, cancelListener);dialogFragment.show(fragmentManager, PROGRESS_TAG);return dialogFragment;}

我们先调用了 CommonDialogFragment 的构造函数,将一个 ProgressDialog 传进去,然后依次传入 cancelable 和 cancelListener,最后调用 show() 函数,将DialogFragment 显示出来,因为我们使用了构造函数的重载,可以看到最简单的构造函数只需要传入两个参数就行了,是不是相当的简洁啊。

应该还没忘了我们上面创建了一个 IDialogResultListener&lt;T&gt; 用于 DialogFragment 与逻辑层之间进行数据监听吧,为了能传入各种各样类型的数据,这里我使用了 泛型 来进行处理,就以 带输入框的弹出窗 为例来看看究竟要怎么使用吧

    public static void showInsertDialog(FragmentManager manager, final String title, final IDialogResultListener<String> resultListener, final boolean cancelable){CommonDialogFragment dialogFragment = CommonDialogFragment.newInstance(new CommonDialogFragment.OnCallDialog() {@Overridepublic Dialog getDialog(Context context) {// ... 这里省略一部分代码AlertDialog.Builder builder = new AlertDialog.Builder(context, INSERT_THEME);builder.setPositiveButton(DIALOG_POSITIVE, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {if(resultListener != null){resultListener.onDataResult(editText.getText().toString());}}});builder.setNegativeButton(DIALOG_NEGATIVE, null);return builder.create();}}, cancelable, null);dialogFragment.show(manager, INSERT_TAG);}

可以看到我们在 showInsertDialog() 方法中传入了IDialogResultListener&lt;String&gt; resultListener,当我们想要处理输入的内容的时候,只要在外部调用的时候,new 一个IDialogResultListener 传进去,然后实现 onDataResult() 方法就行了

以上便是全文的内容,具体的代码以及示例我都放上 Github 了,有需要的朋友可以去看一下 DialogFragmentDemos,如果觉得对你有所帮助的话,就赏个 star 吧!


猜你喜欢

  • 手把手教你从零开始做一个好看的 APP
  • Android 能让你少走弯路的干货整理
  • Android 一款十分简洁、优雅的日记 APP

转载于:https://blog.51cto.com/11365063/2072445

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/539390.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

as模拟器文件夹路径_EGG Switch手机模拟器怎么用?中文教程来了......

大家好&#xff0c;小鸡妹我又来啦。自从昨天曝光了美国NX工作室的EGG模拟器之后&#xff0c;推文底下就多了一千多条留言&#xff0c;说啥的都有。不过小鸡妹大概总结了一下&#xff0c;发现下面这几条问题&#xff0c;出现的频率最高&#xff1a;① 找不到网址&#xff1b;②…

xml文件c语言读取函数,读写xml文件的2个小函数

#region 读写xml文件的2个小函数&#xff0c;2005 4 2 by hycpublic void SetXmlFileValue(string xmlPath,string AppKey,string AppValue)//写xmlPath是文件路径文件名&#xff0c;AppKey是 Key Name&#xff0c;AppValue是Value{XmlDocument xDoc new XmlDocument();xDoc.L…

Java导入导出Excel工具类ExcelUtil

前段时间做的分布式集成平台项目中&#xff0c;许多模块都用到了导入导出Excel的功能&#xff0c;于是决定封装一个ExcelUtil类&#xff0c;专门用来处理Excel的导入和导出 本项目的持久化层用的是JPA&#xff08;底层用hibernate实现&#xff09;&#xff0c;所以导入和导出也…

郁金香汇编代码注入怎么写看雪_世界黑客编程大赛冠军的汇编代码 你见过吗?...

前几天发布了一篇“雷军22年前写的汇编代码”的文章&#xff0c;引起网友的热议。有人说汇编是最牛逼的编程语言&#xff0c;没有之一。汇编语言确实厉害&#xff0c;不知道你有没有见过世界黑客编程大赛冠军的作品?雷军编写的的汇编代码有 网友分享了97年Mekka ’97 4K Intro…

变位齿轮重合度计算公式_齿轮“模数”是如何计算的?

模数是决定齿轮大小的因素。齿轮模数被定义为模数制轮齿的一个基本参数&#xff0c;是人为抽象出来用以度量轮齿规模的数。目的是标准化齿轮刀具&#xff0c;减少成本。直齿、斜齿和圆锥齿齿轮的模数皆可参考标准模数系列表。工业定义&#xff1a;齿轮的分度圆是设计、计算齿轮…

c语言改错和填空能运行吗,C语言改错填空编程

改错题1、在考生文件夹下&#xff0c;给定程序MODI.C的功能是&#xff1a;从低位开始取出长整型变量s中奇数位上的数&#xff0c;依次构成一个新数放在t中。例如&#xff0c;当s中的数为&#xff1a;7654321时&#xff0c;t中的数为&#xff1a;7531。请修改并运行该程序&#…

@Autowired注解实现原理

在讨论代码细节之前&#xff0c;我们再来了解下基础知识。Spring管理可用于整个应用程序的Java对象bean。他们所在的Spring容器&#xff0c;被称为应用程序上下文。这意味着我们不需要处理他们的生命周期(初始化&#xff0c;销毁)。该任务由此容器来完成。另外&#xff0c;该上…

获取freemarker处理后的内容

相信很多人都用过freemarker&#xff0c;或做视图&#xff0c;或模板&#xff0c;或生成静态文件等,但是有多少人做过这样的应用&#xff0c;通过模板后&#xff0c;不是要输出静态的内容&#xff0c;而是直接在代码中获取处理模板后的内容&#xff0c;研究了下API,freemarker里…

c4.5算法python实现_算法:用Python实现—最优化算法

今天给大家分享一下算法&#xff0c;用python来实现最优化算法。废话不多说&#xff0c;直接上代码&#xff1a;一、二分法函数详见rres&#xff0c;此代码使该算法运行了两次def asdf(x): rres8*x**3-2*x**2-7*x3 return rresi2left0right1while i>0 : i i-1 …

comsol临时文件夹中有不支持的字符_文件名中不能包含的字符

文件名是为了方便人们区分计算机中的不同文件&#xff0c;而给每个文件设定一个指定的名称。由文件主名和扩展名组成。DOS操作系统规定文件名由文件主名和扩展名组成&#xff0c;文件主名由1~8个字符组成&#xff0c;扩展名由1~3个字符组成&#xff0c;主名和扩展名之间由一个小…

linux 星号 通配符,如何在bash中转义通配符/星号字符?

简短的回答像其他人所说的那样 - 你应该总是引用变量来防止奇怪的行为。所以使用echo“$ foo”代替echo $ foo。长期回答我确实认为这个例子值得进一步解释&#xff0c;因为它的表面看起来比它看起来更多。我可以看到你的困惑在哪里&#xff0c;因为在你运行你的第一个例子后&a…

PYTHON面试

大部分的面试问题&#xff0c;有最近要找事的老铁吗&#xff1f;python语法以及其他基础部分可变与不可变类型&#xff1b; 浅拷贝与深拷贝的实现方式、区别&#xff1b;deepcopy如果你来设计&#xff0c;如何实现&#xff1b; __new__() 与 __init__()的区别&#xff1b; 你知…

vs怎么更改编译的堆空间_再见吧 buildSrc, 拥抱 Composing builds 提升 Android 编译速度...

前言长期以来困扰我们的一个问题就是构建速度&#xff0c;AndroidStudio 的构建速度严重影响 Android 开发者的工作效率&#xff0c;尤其是更新一个版本号&#xff0c;导致整个项目重新构建&#xff0c;在网络慢的情况下&#xff0c;这是无法忍受的。buildSrc 这种方式&#xf…

java map的遍历

转载地址&#xff1a;http://www.cnblogs.com/shenliang123/archive/2012/08/28/2660705.html -------------------------------------------------------------------------------------------------------------------- java中的遍历 import java.util.Collection; import j…

python循环for...in_python循环while和forin实例

python 循环while和for in简单实例#!/uer/bin/env python# _*_ coding: utf-8 _*_lucknumber 5b 0while b <3:print(guss count:,b)a int(input(you guse number))if a > lucknumber:print (youaerbiger)elif a lucknumber:print (youare righet)break #跳出这个层级…

android悬浮功能实现,Android实现系统级悬浮按钮

本文实例为大家分享了Android系统级悬浮按钮的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下具体的需求1、就是做一个系统级的悬浮按钮&#xff0c;就像iPhone 桌面的那个悬浮按钮效果一样&#xff0c;能随意拖动&#xff0c;并且手一放开&#xff0c;悬浮按钮就自动…

oracle decode_错过血亏!一文搞懂Oracle锁相关视图及相关操作

本文主要研究锁的相关视图&#xff0c;以及锁的相关操作&#xff0c;通过视图查锁的问题。 一、v$transaction视图第一个视图是v$transaction&#xff0c;就是Oracle数据库所有活动的事务数&#xff0c;所有活动的事务每一个活动的事务在这里有一行。v$transactionXIDUSN表示当…

Linux文件系统与命令行

什么是命令行? 接收键盘命令并将其传给操作系统执行的程序(用于输入和管理命令的程序),统称命令行,也叫: Shell&#xff0c;几乎所有Linux发行版都提供了一个 Shell 程序,叫做: Bash (Bourne-Again Shell, 因为最初的 Shell 是由 Steve Bourne 编写的原始 Unix 程序, Again 表…

freeMarker 遍历 list,map,listmap

List List<String> clientSourceDatanew ArrayList<String>();clientSourceData.add("field字段");clientSourceData.add("title标题");ftl&#xff1a; <#if clientSourceData?exists><#list clientSourceData as key> <tr&g…

qtableview不选中_如何选中/取消选中QTableView并触发setData()

我有一个自定义的QTableModel&#xff0c;我在PyQt中使用QTableView显示它。我有一些字段设置为可检查&#xff0c;我想添加“全部检查”和“不检查”按钮。我觉得应该有一种方法可以使setData()从代码中被调用&#xff0c;这样检查状态就会改变&#xff0c;就像我已经用setDat…