问题描述

做过Android开发的人都遇到过这样的问题:随着需求的变化,某些入口界面通常会出现 UI的增加、减少、内容变化、以及跳转界面发生变化等问题。每次发生变化都要手动修改代码,而入口界面通常具有未读信息提醒这样的“小红点”逻辑;一旦UI变化,“小红点”逻辑也要重新计算。如果不同的RD来维护这些代码,耦合性非常高,出错概率也很大。本文以自选股的个人页卡为例(界面如下图所示),并给出了一套方案来解决动态更新UI的问题以及更好的解决未读提醒的逻辑。


旧的方案(Phase out)

(1)对于UI动态变化的问题,通常结合远程控制来解决。以上图的“资产管理”为例,旧的解决方案会在XML写死全部的item,如:“港股交易”、“基金交易”和“精品理财”这三个item。然后根据后台传递过来的json解析出需要隐藏哪些item。点击不同的item会跳转到不同的activity(如下图所示),这部分跳转操作也是写死在代码中的。

这解决了一部分问题,但是如果需求新增了item,比如新增了“沪深交易”、“美股交易”,那就需要改动现有代码了。

(2)对于未读指示(小红点)功能,它的作用是,有未读信息来了,需要在UI上面显示一个小红点提醒用户。比如下图的,股友动态的头像提醒,资产管理的“NEW”提醒,系统设置的新版本提醒等。

旧的方案是定义了一个int型(32位),用它的每一位代表一个UI上的Item。比如好友动态是第1位,未读提醒是第2位... 小红点思想是哪个item有未读信息,则该int型对应的那一位就置1,否则为0。一旦某个item有未读提醒的改变,则将这个int型对应的位改变,异步写入SharedPreference中,同时利用观察者模式通知UI做更新,如下图所示:

上述做法总体来说最大的缺陷就是没有做到“开放-封闭”原则。面对扩展的时候,即添加一个item则不得不修改现有代码,需要在该int型中添加一位标志位,观察者模式也要注册新item。所以下面我会介绍另一种方案可以更好的解决该问题。


新的解决方法

(1) 数据抽象

首先进行数据的抽象,并将UI进行分组,如下图所示:

按照组合模式,将数据以树形结构组织起来,表现“整体/部分”层次结构,如下图所示。这样做的好处是,可以以一致的方式来处理个别对象以及对象组合。蓝色的表示节点,而绿色的表示叶节点。

组合模式的类图,如下所示:

对UI进行的数据抽象。无论是ListItem列表项,还是GridView Item的项,都采用了PersonalItem对象来表示,如下所示:

对于PersonalItem来说,有些没有意义的方法(如add()、remove()、getChild())就不实现,调用它们会抛出UnsupportedOperationException()异常。

(2) 完美解决未读提醒(小红点)的问题

关于计算小红点,PersonalGroup类利用组合+迭代器的模式,代码如下:

这里使用了迭代器,用它遍历所有PersonalComponent组件。遍历过程中可能遇到PersonalItem也可能遇到PersonalGroup。由于它们都是PersonalComponent,且实现了getUnreadIndicator()方法,那我们只需调用getUnreadIndicator()即可。

如上图所示,PersonalGroup中加载的是PersonalComponent,可能是PersonalItem也可能是PersonalGroup。组合模式的优点就是无视具体类型 -- 获取出来的都是PersonalComponent,然后利用多态,调用具体类的getUnredIndicatorCount()方法。如果是PersonalGroup,则继续调用它的这个方法(与此方法一样,会开始另一个遍历);如果为PersonalItem,则说明遍历到了树形结构的末端(即叶节点),则进行如下处理:

如果getUnreadIndicator为true,则表示该PersonalComponent需要显示小红点。因此,利用上述组合+迭代方式,运用递归在根节点处进行一次调用即可。如下图所示,当计算出叶节点“A股大赛”有未读提醒,则它上级的groups也有未读提醒,一直统计到根节点。

getUnredIndicatorCount()是每一个item自己来决定自己是否需要展示小红点的方法。这就是将局部与整体解耦了。整体上面,需要计算小红点,至于如何计算则委托给具体类来实现。即面向对象中的将 "做什么" 与 "怎么做"分开。RD可以从中解放出来,不必关注整体实现,只需关注自己的实现即可。比如,需要在“资产管理”中添加“美股交易”,RD只需添加“美股交易”的内容即可。下一节会说明,这部分内容也由远程控制来代劳了,远程控制传递过来的Date与本地存储的Date比较,如果是新的Date值,则证明这个Item为“NEW”,则对应的小红点需要显示。

(3)远程控制动态更新UI

当远程控制发生变化时(5分钟主动发一次请求),通过解析远程控制接口返回的json串,生成PersonalItem对象的列表。其中每一项对应UI上面的一个Item。需要注意的是,这里还包含了一个URL,它是点击UI控件跳转的URL。以“资产管理”为例,它包含“沪深交易”、“基金交易”等子项。当点击任意一个子项的时候启动的是同一个Activity - WebviewActivity,它包含一个WebView控件。因为每个子项的跳转URL不一样,所以这个WebView load了不同的URL,即完成了跳转不同界面的问题。 然后按照上面描述的树形结构,把PersonalItem放到Groups中。如下图所示:

Model存储了待显示的数据结构。这份数据通过Parser的解析生成UI的内容。过程如下图所示:

Parser模块是一个递归函数,递归的对Model进行解析。并将解析出来的List Item、Grid Group、GridView Item加载各自的XML文件,在程序中动态的添加UI组件。其中onClick事件是在定义PersonalItem的时候已经写好了回调。例如,“资产管理”属于Grid Group,其子项“沪深交易”、“基金交易”等属于GridView Item。在上述“Build PersonalItem Objects”步骤中,已经定义了onClick方法,调用onClick方法跳转至WebViewActivity,这个Activity会加载不同GridView Item的URL,从而实现点击不同item跳转不同页面的目的。

Note:

对于ListItem元素,即上图的列表项(不是GridView元素),并没有实现远程更新的策略。因为它们跳转的逻辑是跳转到各自的Activity,是固定不变的;并且它们的文字描述、图标、是否隐藏均不需要后台来控制更新。故实际项目中,只对GridView内容作了远程控制动态更新UI机制的处理。

另外,在通过远程控制动态更新UI的过程中也遇到了一些坑,比如远程控制更新的时刻,恰好用户退出app,此时系统刚好销毁activity。那么在执行到上述Parser模块的inflateUI的时候就需要判断当前上下文是否为空,如果为空则直接退出。


结论与数据

本文通过将UI数据进行抽象,利用组合模式进行数据的构建。利用递归的方式将数据映射为UI。同时处理了点击事件。数据源则可以通过远程控制动态的更新,RD从中解放。另外,组合+迭代器的方式完美的解决了小红点的问题,遵循了“开放-封闭”原则,将“做什么”与“怎么做”分开。下图从数据的角度描述了改版前后 代码量、Bug量 以及 RD工作量的差异。

原文:http://segmentfault.com/a/1190000003984408

Android UI:机智的远程动态更新策略的更多相关文章

  1. android模块化app开发-3远程动态更新插件

    前两章用apkplug框架实现了两个基本的功能,但它们都是在本地安装测试的,在实际开发过程中我们肯定是需要与服务器联网将更新的插件远程推送给用户手机客户端.今天利用apkplug提供的插件托管服务轻松 ...

  2. 在Android中实现service动态更新UI界面

    之前曾介绍过Android的UI设计与后台线程交互,据Android API的介绍,service一般是在后台运行的,没有界面的.那么如何实现service动态更新UI界面呢?案例:通过service ...

  3. Android Handler传递参数动态更新UI界面demo

    package com.example.demo_test; import android.app.Activity; import android.os.Bundle; import android ...

  4. android中实现service动态更新UI界面

    案例:通过service向远程服务器发送请求,根据服务器返回的结果动态更新主程序UI界面,主程序可实时关闭或重启服务. 注册BroadcastReceiver 在主程序activity中注册一个Bro ...

  5. 一步步教你为网站开发Android客户端---HttpWatch抓包,HttpClient模拟POST请求,Jsoup解析HTML代码,动态更新ListView

    本文面向Android初级开发者,有一定的Java和Android知识即可. 文章覆盖知识点:HttpWatch抓包,HttpClient模拟POST请求,Jsoup解析HTML代码,动态更新List ...

  6. android 不能在子线程中更新ui的讨论和分析

    问题描写叙述 做过android开发基本都遇见过 ViewRootImpl$CalledFromWrongThreadException,上网一查,得到结果基本都是仅仅能在主线程中更改 ui.子线程要 ...

  7. Android中动态更新ListView(转)

    在使用ListView时,会遇到当ListView列表滑动到最底端时,添加新的列表项的问题,本文通过代码演示如何动态的添加新的列表项到ListView中.实现步骤:调用ListView的setOnSc ...

  8. Android ListView动态更新数据

    ListView就是可以显示一行行Item的控件,有时候数据非常多,通常需要分页显示,但为了减少用户的输入,我们可以动态更新ListView,把下一页要显示的数据的添加到当前ListView中. 先看 ...

  9. Android UI(四)云通讯录项目之云端更新进度条实现

    作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节.交流QQ群:[编程之美 365234583]h ...

随机推荐

  1. MFC 设置窗口背景图片

    //在Onpaint函数中加入如下代码 //----------给窗口设置背景图片---------------------------- CPaintDC dc(this); CRect myrec ...

  2. webstorm 如何修改背景颜色

    http://www.cnblogs.com/zxyun/p/4744744.html 见文章底部有图文说明 15个必须知道的chrome开发者技巧(GIF):http://www.wtoutiao. ...

  3. 响应式框架中,table表头自动换行的解决办法

    最近在用bootstrap开发网站,在处理一张table的时候发现,通过PC端查看样式正常,在手机上查看时,因为屏幕小,表格被压缩的厉害,表头和数据变形如下图 后来网上找了一下,发现一个好用的CSS属 ...

  4. 再跟SQL谈一谈--基础篇

    1.简介 2.DDL & DML 3.SELECT ①DISTINCT ②WHERE ③AND & OR ④ORDER BY 4.INSERT 5.UPDATE 6.DELETE 1. ...

  5. SQL Server 表水平分区

    什么是表分区 一般情况下,我们建立数据库表时,表数据都存放在一个文件里. 但是如果是分区表的话,表数据就会按照你指定的规则分放到不同的文件里,把一个大的数据文件拆分为多个小文件,还可以把这些小文件放在 ...

  6. Eclipse 配置SSH 详解

    http://blog.csdn.net/binyao02123202/article/details/18446523 最近看了很多招聘,其中很多我想去的公司都需要一些技能,其中熟练 Java SS ...

  7. iOS开发中常用的分类方法---UIImage+Category

    在开发中使用分类对原有的系统类进行方法扩展,是增强系统原有类功能的常见做法. /** * 自由拉伸一张图片 * * @param name 图片名字 * @param left 左边开始位置比例 值范 ...

  8. mkisofs出错解决办法

    使用mkisofs遇到错误: genisoimage: Uh oh, I cant find the boot catalog directory 'beini/boot/isolinux'! 使用的 ...

  9. winform 两个TreeView间拖拽节点

    /// <summary> /// 正在拖拽的节点 /// </summary> private TreeNode DragNode = null; /// <summa ...

  10. node开子线程模块--tagg2

    tagg2包同样具有tagg包的多线程功能,采用新的node-gyp命令进行编译,同时它跨平台支持,mac,linux,windows下都可以使用,对开发人员的api也更加友好.安装方法很简单,直接n ...