考拉Android统一弹框
作者:钱成杰
背景
在快速开发的背景下,经历了n个版本后的考拉Android App中已经存在了各种各样看似相同却各有差别的弹框样式。其中包括系统弹框和自定义弹框,并且在线上时常会出现IllegalArgumentException的异常,而现有的解决方法是通过工具类来保护调用show和dismiss方法,这种方式效率不高,而且覆盖不全,开发过程中容易遗漏。另外现有的Builder方式的弹框构造工具虽然功能强大,能构造各种弹框,但是使用复杂,样式逻辑耦合,使用成本太高。于是,便需要一款样式统一、show和dismiss安全、调用简单、构造方便的统一弹框工具。
目标
统一弹框的交互样式
show和dismiss方法安全
样式与逻辑解耦,使用者不必关心弹框样式,只需要完成自己的续逻辑即可
使用简单,一个接口即可获得所需样式
扩展性,可支持展示特别样式弹框
怎么做?
既然有了特定的目标,那么就要开始弹框的设计了。弹框通过工厂模式设计生产,使用者通过CommonDialogFactory提供的接口,可以直接生产CommonDialog类、KaolaCommonDialog类、KaolaBottomCloseDialog类三种弹框,其中CommonDialog是为了兼容旧逻辑保留的旧的弹框样式,KaolaCommonDialog是新设计统一了样式的弹框类,但是两者在样式上没有很大的区别,KaolaBottomCloseDialog是一种底部关闭的弹框样式。
0. 保留CommonDialog的旧的构建方式。
考虑到旧的弹框中有不少弹框的业务逻辑与旧的Builder工具样式有耦合,为了保证业务逻辑不受影响,我保留了以前的Builder方式构造弹框的设计,并且对其进行了接口化封装。这样可以降低构造弹框的成本。
1. 整理目前项目中用到的弹框的样式。
由于不熟悉别的模块中弹框到底有哪些样式,所以需要整理目前项目中所有用到的弹框样式。统计结果发现大部分弹框使用的是系统弹框,而自定义弹框中不少样式已经不再使用,但是在旧的构造工具中依然存在,由于逻辑耦合,后期维护相对麻烦,这也验证了我们统一弹框组件的必要性。
2. 慢慢从DialogManager里面把样式抽出来,形成基本样式。
从旧的弹框管理类DialogManager中,将现在正在使用的弹框抽离出来,在弹框工厂CommonDialogFactory中封装成单独的接口以提供使用。也可以根据整理的接口,形成一套基本的弹框样式,这样在后面做样式统一的时候就可以参考着现有的样式去做了。
3. 具体弹框样式形成文档维护在wiki上(提供使用帮助)。
那么为了更方便地使用和维护统一弹框组件,使用文档是必不可少的。把现有的弹框样式总结在wiki文档中,对于现有可用的弹框样式也是一目了然;还可以根据文档中提供的案例进行调用开发,降低成本。
4. 样式全部抽出来之后,那就按照统一的交互进行修正。
由于目前没有视觉提供的统一样式,所以我们基于第2点钟抽离出来的基本样式,按照这些样式来做构建的统一。然后再按照后续设计提供的统一交互规范进行进一步的修正,慢慢做到样式也统一,最后真正实现弹框的完全统一。
样式
下图中可以看到弹框类的继承关系:
KaolaBaseDialog中实现了安全的show和dismiss方法:
@Overridepublic void show() { if (!checkAllow()) { return;
} // 防止check无效
try { super.show();
} catch (Exception e) {
e.printStackTrace();
}
}@Overridepublic void dismiss() { if (mOnDismissListener != null) {
mOnDismissListener.onDismiss(mDismissType);
} if (!checkAllow()) { return;
} try { super.dismiss();
} catch (Exception e) {
e.printStackTrace();
}
}/**
* 检查环境是否允许
*
* @return
*/private boolean checkAllow() {
Context context = this.getContext(); if (context instanceof Lifeful) {
Lifeful lifeful = (Lifeful) context; if (!lifeful.isAlive()) { return false;
}
} else if (context instanceof Activity) { if (!ActivityUtils.activityIsAlive(context)) { return false;
}
} return true;
}
KaolaCommonDialog是通用样式类:
KaolaBottomDialog是底部弹出浮层:
KaolaBottomConfirmDialog是底部确认浮层:
KaolaBottomCloseDialog是底部带关闭弹框:
ExpectPickUpTimeDialog是时间选择器浮层:
通用弹框的文案也支持SpannableString的多样展示;另外可以看到KaolaBaseDialog可以使用在各种情况下,不仅仅是通用弹框,还可以是一些通用组件比如ExpectPickUpTimeDialog时间选择器(不过这里做成了与取件业务相关的组件)。
怎么用?
KaolaCommonDialog的使用
直接调用CommonDialogFactory提供的createOneOrTwoButtonsCustomView接口,该接口可以提供标题、文案、自定义view、通用按钮的展示,使用者可以通过wiki文档或者直接查看接口注释了解接口内容。
/**
* 一个标题,一个文案,一个view,一个白底红字negative(left)按钮,一个红底白字positive(right)按钮
*
* @param context
* @param title 标题,传空不带标题
* @param message 提示文案
* @param view 自定义区域需要添加的view
* @param leftBtn 左边按钮的内容(传空不显示按钮)
* @param rightBtn 右边按钮的内容(传空不显示按钮)
* @return KaolaCommonDialog
*/public KaolaCommonDialog createOneOrTwoButtonsWithCustomView(Context context, String title, CharSequence message,
View view, String leftBtn, String rightBtn) {
...
} KaolaCommonDialog dialog = CommonDialogFactory.getInstance()
.createOneOrTwoButtonsWithCustomView(this, "标题", "提示文案", getCustomView(), "取消", "确定")
.setOnLeftButtonClickListener(() -> ToastUtils.show("leftClick... negative"))
.setOnRightButtonClickListener(() -> ToastUtils.show("rightClick... positive"))
.setCancelableOutside(true);
dialog.setOnDialogDismissListener(dismissType -> { // dismiss回调
ThreadCore.getInstance().postOnMainLooper(new LifefulRunnable(new Runnable() { @Override
public void run() { if (dismissType == KaolaBaseDialog.DISMISS_TYPE_POSITIVE) {
ToastUtils.show("右侧按钮点击消失");
} else if (dismissType == KaolaBaseDialog.DISMISS_TYPE_NEGATIVE) {
ToastUtils.show("左侧按钮点击消失");
} else {
ToastUtils.show("其他消失");
}
}
}, DialogTestActivity.this), 1000);
});// 不展示顶部分割线dialog.dividerTop.setVisibility(View.GONE);
dialog.show();
考虑到一些特殊情况的需求,默认的样式无法满足视觉要求的时候,就需要定制化一些弹框中的内容了。所以弹框类中的各个成员以public的形式开发给使用者,以适应各种定制化要求,比如:不希望展示title下面的分割线,可以直接获取dividerTop对象进行设置。另外自定义view参数可以满足对弹框内容的特殊化定制。在统一了弹框调用之后,依然具有很强的扩展性。
KaolaBottomDialog的使用
考虑到底部浮层的多样性,没有将KaolaBottomDialog的构建放入CommonDialogFactory中,而是使用通用的构建方式构建。
private KaolaBottomDialog chooseDialog;
...
chooseDialog = new KaolaBottomDialog(this);// 展示浮层右上角关闭按钮chooseDialog.showRightClose(true);
...// 设置标题(String)和内容(ListView)chooseDialog.setContent(getString(R.string.refund_delivery), dialogListView) // 限制浮层最大屏高比(最大为屏幕高度的2/3)
.setDialogHeight(2f / 3);
chooseDialog.show();
思考
统一交互既能够给产品带来更好体验,又可以减少开发者不必要的开发工作,降低代码耦合,提高工作效率,是一个建议并值得去做的事情。目前的统一弹框方案还是有很多不足,等着我们去优化,比如将所有Dialog统一到CommonDialogFactory中去构建,再使用等等。
网易云产品免费体验馆,无套路试用,零成本体验云计算价值。
本文来自网易实践者社区,经作者钱成杰授权发布
更多网易研发、产品、运营经验分享请访问网易云社区。
相关文章:
【推荐】 消息推送平台高可用实践(上)
【推荐】 为何要在网站上设置的验证码
【推荐】 搜索凑单页大促显示延迟方案设计
考拉Android统一弹框的更多相关文章
- 网易考拉Android客户端网络模块设计
本文来自网易云社区 作者:王鲁才 客户端开发中不可避免的需要接触到访问网络的需求,如何把访问网络模块设计的更具有扩展性是每一个移动开发者不得不面对的事情.现在有很多主流的网络请求处理框架,如Squar ...
- Android Dialog弹框提示
public void showUpdateDialog(String content) { //普通的AlertDialog对话框 AlertDialog.Builder builder = new ...
- Android填坑系列:在小米系列等机型上放开定位权限后的定位请求弹框
背景: 近期因实际项目需要,在特定操作下触发定位请求,取到用户位置及附近位置. 问题: 经初步选型,最终决定接入百度定位,按照百度定位SDK Android文档,接入过程相对顺利.但随后发现,在小米系 ...
- 2015年网易考拉海淘android面试
经朋友推荐,昨天下午去网易杭州公司参加了考拉海淘android客户端的面试.今天回忆一下面试题目,做个整理进行备案. 1.说说JVM垃圾回收机制. 1.1.画了JVM分代回收的图,大致说了下垃圾分代回 ...
- 弹框在UC浏览器或者Android机器上会被顶上去
弹框在UC浏览器或者Android机器上会被顶上去 可以通过监听resize事件 this.height = $(document).height(); window.addEventListener ...
- 遇到的一个移动端从下往上过渡的弹框,在Android下过渡动画的优化问题。
优化之前: /* 分享弹框样式 */ .popUpDiv { width: 100vw; height: 100vh; transition: all 0.5s ease; position: fix ...
- Android H5混合开发(5):封装Cordova View, 让Fragment、弹框、Activity自由使用Cordova
近期,有同事咨询如何在Fragment中使用Cordova,看了下Cordova源码,官方并没有提供包含Cordova Webview的Fragment,以供我们继承. 上网查询了一下,也有几篇文章讲 ...
- UI自动化之特殊处理二(弹框\下拉框\选项\文件上传)
弹框\下拉框\选项\文件上传也是一些比较特殊的操作 目录 1.弹框 2.下拉框 3.选项 4.文件上传 1.弹框 弹框有三种形式,value为alert.confirm.prompt三种的弹框,第一个 ...
- selenium webdriver从安装到使用(python语言),显示等待和隐性等待用法,切换窗口或者frame,弹框处理,下拉菜单处理,模拟鼠标键盘操作等
selenium的用法 selenium2.0主要包含selenium IDE 和selenium webDriver,IDE有点类似QTP和LoadRunner的录制功能,就是firefox浏览器的 ...
随机推荐
- isee图片专家批量处理图片大小教程
经常用手机.照相机外出拍照片,然后再弄到电脑上面很占硬盘空间了,isee图片专家是一款非常专业的批量压缩图片大小工具,方便储存,给电脑减压,具有一次自动处理N张图片:程序小巧,资源占用低,处理速度快等 ...
- 笨办法学Python(二十五)
习题 25: 更多更多的练习 我们将做一些关于函数和变量的练习,以确认你真正掌握了这些知识.这节练习对你来说可以说是一本道:写程序,逐行研究,弄懂它. 不过这节练习还是有些不同,你不需要运行它,取而代 ...
- Node.js与npm安装(转载)
2009年的JSCOnf大会上,一个叫Ryan Dahl的年轻程序员向人们展示了一个他正在做的项目,一个基于Google V8引擎的JavaScript运行平台,它提供了一套事件循环和低IO的应用程序 ...
- C++学习之虚析构函数
什么样的情况下才需要虚析构函数? 类需要控制自己的对象执行一系列操作时发生什么样的行为,这些操作包括:创建(对象).拷贝.移动.赋值和销毁.在继承体系中,如果一个类(基类或其派生的类)没有定义拷贝控制 ...
- Uva 11997 多路归并
题目链接:https://uva.onlinejudge.org/external/119/11997.pdf 题意: k*k的矩阵,从每一行中选一个元素加起来,可以得到 kk个和,求前 k 个最小值 ...
- vuejs作用域插槽
作用域插槽 <div id='root'> <child> <template slot-scope='props'> <h1>{{props.item ...
- 大数据(1)初始hadoop
1.hadoop模型如下: (上图为Hadoop1.x的布局) (Hadoop2.x较Hadoop1.x,多了YARN) Hadoop框架,是一个庞大的生态系统. 或者我们可以这样理解: 可以把整个体 ...
- window环境下安装node.js
在使用sublime text 3 过程中,node.js装了好几次都没有成功,今天终于成功了,现将安装过程整理一下. 安装过程中主要参考了以下代码: 第一,下载文件 https://nodejs.o ...
- 利用bootstrap实现图片Carousel效果
引入头文件: <link rel="stylesheet" href="bootstrap.min.css"> <link rel=" ...
- 洛谷P4316 绿豆蛙的归宿(期望)
题意翻译 「Poetize3」 题目背景 随着新版百度空间的上线,Blog宠物绿豆蛙完成了它的使命,去寻找它新的归宿. 题目描述 给出一个有向无环图,起点为1终点为N,每条边都有一个长度,并且从起点出 ...