使用 ThinkAlike 为 Android 和 Windows 构建空气质量指数小部件( 第一部分)
使用 ThinkAlike 实现 Android/桌面跨平台开发的新示例,包括 helloworld、Web API 访问和小部件 UI。
- 下载 Android 版 HelloThinkAlike 安装程序 - 284.6 KB
- 下载 HelloThinkAlike 源代码(含 ThinkAlike 库)- 2.1 MB
- 从 Github 下载 AQIService 源代码
目录
- 引言
- 背景
- 第 1 天 -- 框架:Hello, ThinkAlike Library!
- 第 2 天 -- 服务:跨平台 AsyncTask 执行器的和谐
- 第 3 天 -- UI:Android AppWidget 的不同风味
- 结论
引言
我第一篇介绍 ThinkAlike 框架的文章于 2014 年 3 月发布在 CodeProject。简而言之,它讲述了根据 MVVM 原理,首先快速开发一个 炉石传说卡牌参考 应用程序,然后通过仅替换原生的 View 层,高效地将其扩展到 Android 版本的故事。
一年过去了,许多事情都在发展(参见背景部分),并且很高兴我仍然发现 ThinkAlike 的理念很有用,并想分享一个关于该框架的新用户故事。
这个新故事源于困扰中国人的“*空气末日*”,包含三个部分:
- ThinkAlike 已被锻造成一个独立库,变得更加便捷(参见框架部分)
- 从 Android 源代码中借鉴的异步机制,用于从通用模块启动(参见服务部分)
- 以及一对小部件,分别报告室外和室内空气状况(参见上面的截图和UI部分)。
背景
在我上一篇文章发表的一年中,发生了不少事件。
- 社区:CodeProject 举办了 Android 教程大赛
尽管我的第一篇 ThinkAlike 文章获得了令人鼓舞的 20,000 多次阅读,但尚未获得足够的公众评论。这可能是由于同时学习 JavaFX 和 Android 的难度,尤其是对于 Android 新手。幸运的是,CodeProject 在去年举办了Android 教程写作比赛。涌现了大量新文章和作者,我希望我的 MVVM 跨平台解决方案能吸引更多潜在参与者。
- JavaFX:社区推出了 JavaFXPorts
JavaFXPorts 可能在 ThinkAlike 推出时(2013 年 12 月)不久就发布了。它的动机是将 Java 字节码翻译到移动操作系统上的可能性,并得到了 JavaFX 团队的支持;而 ThinkAlike 则基于 Android、MVVM 和 SWT 的经验(事实上,我曾考虑将 SWT 作为桌面 UI,它被 Eclipse 使用)。作为一个兼职项目,JavaFXPorts 也缺乏资源,但 Johan Vos(作者)非常努力地工作。我相信如果成功,它将为 JavaFX 开发者带来极大的便利。
- Android:ART 取代 Dalvik
Google 推出了一种名为 ART(Android Runtime)的新编译器,并解释了它与 Dalvik 的区别。然而,作为一个应用程序层解决方案,ThinkAlike 不需要担心这种底层修改。
- ThinkAlike:库 +炉石传说卡牌参考筛选功能已完成
首先,跨平台相关部分从原始的“all-in-one”炉石传说卡牌参考源代码中分离出来。HSCardRef 项目被分离出来,并链接到之后独立的 ThinkAlike 库项目。到目前为止,从功能上讲,HSCardRef 已升级到 Func#3,可以按法力/攻击/生命值以及技能特质筛选卡牌! (而且不得不提的是,这主要由我的同事完成,他们学习了我的教程文章!)
第 1 天 -- 框架:Hello, ThinkAlike Library!
在“*空气末日*”探险之前的挑战是为 JavaFX 和 Android 平台构建一个启动项目。我们开始吧!
挑战 1. 环境设置(2-4 小时)
我的开发工具清单保持不变
- 基础:Eclipse/e(fx)clipse 4.2 Juno + JDK 1.7
- 我强烈推荐 e(fx)clipse,它为 Eclipse IDE 提供了 JavaFX 工具,并与其他(尤其是 Android)开发保持稳定的兼容性。
JavaFX 使用 JDK 1.7。Android 项目默认会降级到 JDK 1.6 编译器合规级别。 - Android SDK 4.4 (API 19) + ADT 插件(适用于 Eclipse)
- JavaFX SDK(包含在 JDK 1.7 中)+ JavaFX Scene Builder
我提供的示例项目已在e(fx)clipse 中配置。您可以使用 Android Studio、NetBeans 等其他 IDE,但那样您就需要自己设置项目之间的关系(如下文所述)。
此外,如果您是 Android 或 JavaFX 编程新手,或者尤其是不熟悉使用 Eclipse IDE,我建议您在新环境中至少运行一个示例项目。(估计 2-4 小时。否则,如果任何编译器/设置问题困扰您,请记住回来寻求自我救助:))
- E(fx)clipse 安装
对于已在使用 Eclipse 的 Android 老手:e(fx)clipse 安装指南 -- 致雄心勃勃者(在现有 Eclipse 上安装)
对于 Eclipse 新手:e(fx)clipse 安装指南 -- 致懒惰者(一体化) - Android 独立示例
对于 E(fx)clipse 学徒:Google > 第一步。安装独立 SDK 工具,第二步。安装 Eclipse 插件,第三步。在 Eclipse 中管理项目
对于 Android Studio 学徒:Google > 构建你的第一个应用 - JavaFX 独立示例
对于 E(fx)clipse 学徒:Efxclipse > Tutorial1
对于 NetBeans 学徒:Oracle > JavaFX 风格的 Hello World
挑战 2. 项目设置(Eclipse 用户 15 分钟)
在新环境中安顿好后,学徒可以解压名为 HelloThinkAlike 的软件包,并找到如下部署的 6 个文件夹。
在 ThinkAlike 项目的底部中央是通用模块,两侧是Android 模块和JavaFX 模块。配合任一特定平台模块,平台无关模块可以“唤醒”(构建)相应版本的 ThinkAlike 库。Eclipse 学徒可以通过导入模块为“现有项目”来实现,并且链接已预先配置好。
- ThinkAlike Android 是一个 Android 库项目,它引用(链接)到ThinkAlike generic 的源代码。
- ThinkAlike JavaFX 是一个 JavaFX 库项目,它也引用(链接)到ThinkAlike generic 的源代码。
- 尽管对于 Eclipse IDE,使用了不同类型的引用(源码级 vs 项目级)(主要因为 e(fx)clipse 插件的限制),但这并非必需。
然后继续上面的 3 个模块。那里的Application 应该被替换为您使用 ThinkAlike 库/架构进行跨平台扩展的主应用程序的真实名称。对于我们的教程包,它被命名为简单的HelloThinkAlike。
- Application Android 是一个 Android 应用程序项目,它同时引用Application generic 以获取可共享的源代码和资源文件,并引用ThinkAlike Android 作为库项目(*1)。
*1 当前版本的 Android SDK 不允许将库作为 JAR 文件分发。 - Application JavaFX 是一个 JavaFX 应用程序项目,它也引用Application generic,并以 JAR 库(即ThinkAlike_jfx.jar,在其libs 子文件夹中)的形式使用相应的ThinkAlike JavaFX。
挑战 3. 构建、运行和分发(Eclipse 用户 1-2 小时)
从 Eclipse 的角度来看,6 个模块将完美显示如下(除了一些抑制的警告)。
我们的下一个目标是在笔记本上激活HelloThinkAlike_jfx 应用程序,并在指尖上激活HelloThinkAlike_android。
如果 6 个反射中的任何一个出现故障,那可能归因于Java Build Path 或Android Project Build Target 设置问题。Eclipse 学徒可以通过打开未调优项目的Project Properties 来进行修正。调优后,为了双重检查,您可以尝试按以下顺序构建项目(可选):ThinkAlike_generic、ThinkAlike_jfx、ThinkAlike_android、HelloThinkAlike_generic、HelloThinkAlike_jfx 和HelloThinkAlike_android。
事实上,大多数 e(fx)clipse 学徒可以直接从 IDE 启动HelloThinkAlike_jfx 项目。([右键菜单] Run As > Java Application,当被要求选择Application class 时,请选择"HelloThinkAlikeApp")
一个 JavaFX GUI 将会弹出,具有圆角轮廓和透明背景。这只是一个教程示例,所以只需“点击这里”,您就会揭开秘密。
HelloThinkAlike_android 项目也会发生类似的事情,只是您的Android 设备应该已连接并被您的开发操作系统和 IDE 识别。(USB 驱动程序问题可能会让您感到困惑,就像它们曾经困扰过我一样。使用虚拟设备?过于麻烦……)对于 Eclipse 学徒,[右键菜单] Run As > Android Application,在将一个 zip 压缩的 APK 传输到您的手机并部署后,一个类似的 GUI 将会出现。
一旦启动,Android 项目将不仅仅被编译,还会在其bin 子文件夹中封装一个 .apk 文件,该文件可以提交到大多数 Android 应用商店。
相比之下,JavaFX 项目在分发独立包方面有额外的学习曲线。
- 详细信息(仅限 e(fx)clipse 学徒),有一个必读的JavaFX 2 教程第七部分 - 使用 E(fx)clipse 进行部署,适用于 Windows 和 MacOS。
- 对于速读人员,您可以直接进入HelloThinkAlike_jfx\build\build.xml 查看 Ant 脚本,该脚本可帮助您直接跳到上述教程的第 4 步。然后,如果您是 Windows 用户,请下载一个名为Inno Setup 的 .exe 安装程序实用程序,并记住将其安装路径添加到您的Path 环境变量中。(对于 MacOS,不需要安装程序实用程序)最后,通过[右键菜单] Run As > Ant Build 运行build.xml,构建将需要一段时间。如果成功,将在文件夹\build\deploy\bundles 中有一个 .exe 安装程序。
挑战 4. 自定义(1-10 小时)
热身,或者说启动项目,就这样结束了吗?直到真正的力量与你同在,即代码的力量。
代码概览
HelloThinkAlike 项目可用作新开发的模板。下表说明了为HelloThinkAlike 演示定制了每个源文件的程度。基本源文件/资源都用粗体标出,稍后将进行解释。
源 | 代码行 | 定制级别 |
--- HelloThinkAlike_generic --- | ||
Config.java | 18 | 低。日志级别,资产文件根目录 |
Constant.java | 13 | 低。应用标题,资产文件路径,属性名称 |
HelloViewModel.java | 30 | 高。 ViewModel,主要逻辑 |
logo_*.png | 0 | 高。 UI,资产 |
--- HelloThinkAlike_jfx --- | ||
Assets.java | 3 | 不适用。辅助类 |
Res.java | 23 | 不适用。辅助类 |
Config.java | 3 | 不适用 |
Constant.java | 15 | 不适用 |
Factory.java | 21 | 不适用。创建特定于平台的适配器类 |
HelloThinkAlikeapp.java | 147 | 中。入口,初始化 |
MainController.java | 80 | 高。 1.UI,视图层 2.使用 ViewModel |
scene_main.fxml | 25 | 高。 UI,布局标记 |
style_base.css | 8 | 高。 UI |
bundle.properties | 1 | 高。 UI,国际化,文本 |
ApplicationPreloader.java | 7 | 不适用。可选 |
--- HelloThinkAlike_android --- | ||
Config.java | 3 | 不适用 |
Constant.java | 3 | 不适用 |
Factory.java | 27 | 不适用。创建特定于平台的适配器类 |
HelloThinkAlikeApp.java | 105 | 中。入口,初始化 |
MainActivity.java | 91 | 高。 1.UI,视图层 2.使用 ViewModel |
activity_main.xml | 31 | 高。 UI,布局标记 |
styles.xml | 26 | 高。 UI,国际化,字体大小 |
strings.xml | 5 | 高。 UI,国际化,文本 |
更改基本设置
每个特定平台项目(HelloThinkAlike_jfx 或HelloThinkAlike_android)的入口位于类com.{domain_name}.{platform}.{app_name}App
的源文件中。应用程序级别的设置可以在那里配置。
以HelloThinkAlike_jfx为例。您可以打开应用程序类com.helloworld.jfx.HelloThinkAlikeApp
,然后转到其入口方法start()
,检查需要修改的设置。(同样,Android 应用程序类有一个名为onCreate()
的入口方法)
@Override
public void start(Stage primaryStage) {
Locale.setDefault(new Locale("en", "US"));
......
//1.Initialize platform-specific, domain-specific objects
com.helloworld.jfx.common.Constant.initialize();
Config.STORAGE_BASEPATH = "C:\\Test\\"; //platform-specific
//2.Initialize Log scheme (File IO)
LogTag.removeExclusionItem(LogTag.AssetThread); //for novices, or new project
Util.trace(getClass().getSimpleName(), "......");
......
}
如上所示,可以从那里自定义区域设置、存储基础(资产文件将提取到外部文件夹)、日志过滤器(例如 LogTag.AssetThread)等。您还可以打开HelloThinkAlike_generic 模块的com.helloworld.generic.common
包,并检查特定于域的常量/配置,如应用程序标题、日志级别,这些都是静态定义的。
应用程序类还实现了ThinkAlike 库(com.thinkalike.generic.Platform
)声明的Platform 接口,以便为通用模块提供所需的特定于平台的数据/方法。例如,通过调用getOsType()
,通用模块可以知道当前平台类型,从而确定显示哪个操作系统徽标。
更改资源、资产
如果您想在屏幕上添加自己喜欢的动画,例如 AOE 圣骑士 就像我之前在 HTML5 中所做的那样,您会发现管理图像在一个文件夹中对您的Android 项目和JavaFX 项目都非常方便。HelloThinkAlike_generic\assets-{locale} 将是这样的存储库。
在ThinkAlike 的世界中,有两种方法可以渲染图像:普通方式,通过向原生类(例如android.widget.ImageView)提供路径或drawable 信息;或者一种替代方法,通过使用自适应类,这些类用通用接口封装原生控件并消耗通用内容数据,如下文详述。
更改逻辑
Model-View-ViewModel(MVVM) 项目的 UI 逻辑驻留在ViewModel 类中,这些类从实际的View 类中抽象出本质。ThinkAlike 利用MVVM 理念,使面向用户故事的代码通用于特定于平台的项目,并且易于维护,如下所示:
//com.helloworld.generic.viewmodel.HelloViewModel
import com.thinkalike.generic.viewmodel.ViewModelBase;
import com.thinkalike.generic.viewmodel.control.UIImageNode;
public class HelloViewModel extends ViewModelBase{
private static String _helloMessage = "Hello, ThinkAlike!";
private static UIImageNode _helloImage;
private void updateHelloMessage(){
//1.hello message
this.firePropertyChange(Constant.PropertyName.HelloMessage, null, _helloMessage);
//2.hello image
if(_helloImage==null){
OsType osType = Loader.getInstance().getPlatform().getOsType();
ImageNode node = new ImageNode((osType==OsType.Android) ? Constant.PATH_LOGO_ANDROID : Constant.PATH_LOGO_JAVAFX);
_helloImage = new UIImageNode(null, node, false);
}
this.firePropertyChange(Constant.PropertyName.HelloImage, null, _helloImage);
}
public void onRefresh(){
updateHelloMessage();
}
}
HelloThinkAlike 的逻辑非常简单:通过显示消息+徽标来处理点击。接受用户输入和渲染内容的细节可以通过特定于平台的View 层完成,而ViewModel 层封装了 UI 相关的数据、逻辑,并通过两种接口与View 通信:
- ICommand
用户命令(而非中间 UI 操作)将被解释为 ICommand 事件,这些事件将从View 分发并由ViewModel 处理。
onRefresh()
是一个简单的例子。对于耗时的事务,您也可以使用异步机制来处理命令。 - IPropertyChange
UI 更新反馈将被解释为
IProperty(Change)
事件,这些事件将从ViewModel 触发,并由已注册为侦听器的View 消耗。有两种属性被宣布为已更改:_helloMessage 和_helloImage。前者代表可以被普通原生 UI 控件(例如 android.widget.TextView)轻松渲染的原始数据类型/对象;后者代表ComponentModel(例如 UIImageNode),可以被平台无关的“*自适应*”组件类(例如 com.thinkalike.android.control.ImageNodeView)消耗。ComponentModel 就像一个组件级别的ViewModel,可以在不同平台 SDK 中初始化特定类型组件所需的数据。您可以修改源代码以响应工作日图像,View 层将诚实地渲染它们。
请记住,通用的ViewModel 不知道特定于平台的View 类。View 类负责实例化相应的ViewModel 并订阅它们的IPropertyChange
事件。
为特定平台更改 UI
上个月,*炉石传说*发布了其 Android 手机版本(左下角)。更紧凑的布局,更多层叠的 UI 设计,这会降低用户体验但保持可读性,使其与其平板祖先(右下角)截然不同。
vs.
在决定需要设计多少套 UI 之前,有几个关键因素需要考虑,例如可用的屏幕尺寸或 DIP(设备无关像素),可用的交互方法/设备(例如,眼镜、手表)。在某些情况下,对性能、兼容性、新功能支持、遗留源代码等的关注会导致采用特定于平台的开发。然而,*ThinkAlike* 凭借其MVVM 设计,为这些情况提供了跨平台可能性。开发者可以采用自适应 UI 组件来抑制平台之间的差异,同时他们仍然可以享受特定于平台的优势,只要将ICommand/IPropertyChanged
的内容建模到通用的ViewModel 类中。
对于 Android UI 修改,如官方文档所述,应检查res 子文件夹(例如activity_main.xml)下的资源以及Activity 类。另一方面,对于 JavaFX,组织资源没有严格的约定,目前它们在com.{domain_name}.jfx.res 包下进行管理。一个名为MainController 的控制器类解释了布局scene_main.fxml 的原生视图级逻辑(在 JavaFX 中,“Scene”等同于“Activity”)。
以下文档集合将有助于新手调整HelloThinkAlike 的 UI
- 对于 Android
- API:Android API
- GUI::Layout(Java+XML 标记):布局
- GUI::Style:样式资源
- 对于 JavaFX
- API:JavaFX 2 的 JavaDoc
- GUI::Layout(Java):使用 JavaFX 中的布局
- GUI::Layout(标记):FXML 简介
- GUI::Style:JavaFX CSS 参考指南
更改项目名称
当您决定开始自己的项目时,可以在HelloThinkAlike_* 项目中替换{app_name}和{domain_name},它们可能出现在文件夹名称、包名称或源文件内容中。
- 在{app_name}_generic 项目中,
- 文件夹、包:assets-{locale}\{app_name},src\com\{domain_name}
- 源文件:在所有源文件和.project 设置文件中全局替换字符串模式,如 "com.{domain_name}", "{app_name}"。
- 在{app_name}_jfx 项目中,
- 文件夹、包:src\com\{domain_name}
- 源文件:.classpath、.project、所有源文件、log4j.properties、build.xml。
- 在{app_name}_android 项目中,
- 文件夹、包:src\com\{domain_name}
- 源文件:.classpath、.project、project.properties、所有源文件、AndroidManifest.xml。
总结
第 1 天实际上是我关于ThinkAlike 第一篇文章的简化版。库已分离出来,省略了ThinkAlike 的实现细节。应用程序逻辑已简化,MVVM 层之间的过渡更加清晰。我希望即使是 Android 或 JavaFX 编程新手也能以最小的精力成本完成所有 4 个挑战,并为进一步的探索做好准备。
按计划,第 2 天将介绍对框架进行的AsyncTask 增强(com.thinkalike.generic.concurrent.*
)。第 3 天将介绍AppWidget UI,它高度依赖于与平台的交互,并将证明ThinkAlike 的适应性。
感谢您对 Android/桌面跨平台讨论的关注。我将尽力提供读者友好的介绍,并且绝对欢迎任何互动。 :)
历史
2015-05-06:第 1 版