65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 ThinkAlike 为 Android 和 Windows 构建空气质量指数小部件( 第一部分)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2015年5月15日

LGPL3

14分钟阅读

viewsIcon

19714

downloadIcon

372

使用 ThinkAlike 实现 Android/桌面跨平台开发的新示例,包括 helloworld、Web API 访问和小部件 UI。

目录

引言

第一篇介绍 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 天的教程,无需回顾我之前的“*炉石传说卡牌参考*”文章。

挑战 1. 环境设置(2-4 小时)

我的开发工具清单保持不变

我提供的示例项目已在e(fx)clipse 中配置。您可以使用 Android Studio、NetBeans 等其他 IDE,但那样您就需要自己设置项目之间的关系(如下文所述)。

此外,如果您是 Android 或 JavaFX 编程新手,或者尤其是不熟悉使用 Eclipse IDE,我建议您在新环境中至少运行一个示例项目。(估计 2-4 小时。否则,如果任何编译器/设置问题困扰您,请记住回来寻求自我救助:))

挑战 2. 项目设置(Eclipse 用户 15 分钟)

在新环境中安顿好后,学徒可以解压名为 HelloThinkAlike 的软件包,并找到如下部署的 6 个文件夹。

ThinkAlike 项目的底部中央是通用模块,两侧是Android 模块和JavaFX 模块。配合任一特定平台模块,平台无关模块可以“唤醒”(构建)相应版本的 ThinkAlike 库。Eclipse 学徒可以通过导入模块为“现有项目”来实现,并且链接已预先配置好。

如果您不使用 Eclipse,请牢记以下提示来配置链接:
  1. ThinkAlike Android 是一个 Android 库项目,它引用(链接)到ThinkAlike generic 的源代码。
  2. ThinkAlike JavaFX 是一个 JavaFX 库项目,它也引用(链接)到ThinkAlike generic 的源代码。
  3. 尽管对于 Eclipse IDE,使用了不同类型的引用(源码级 vs 项目级)(主要因为 e(fx)clipse 插件的限制),但这并非必需。

然后继续上面的 3 个模块。那里的Application 应该被替换为您使用 ThinkAlike 库/架构进行跨平台扩展的主应用程序的真实名称。对于我们的教程包,它被命名为简单的HelloThinkAlike

对于非 Eclipse 学徒:
  1. Application Android 是一个 Android 应用程序项目,它同时引用Application generic 以获取可共享的源代码和资源文件,并引用ThinkAlike Android 作为库项目(*1)
    *1 当前版本的 Android SDK 不允许将库作为 JAR 文件分发。
  2. 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 PathAndroid Project Build Target 设置问题。Eclipse 学徒可以通过打开未调优项目的Project Properties 来进行修正。调优后,为了双重检查,您可以尝试按以下顺序构建项目(可选):ThinkAlike_genericThinkAlike_jfxThinkAlike_androidHelloThinkAlike_genericHelloThinkAlike_jfxHelloThinkAlike_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_jfxHelloThinkAlike_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 不知道特定于平台的ViewView 类负责实例化相应的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

更改项目名称

这一步也可以放在第一位。

当您决定开始自己的项目时,可以在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.propertiesbuild.xml
  • {app_name}_android 项目中,
    • 文件夹、包:src\com\{domain_name}
    • 源文件:.classpath.projectproject.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 版

© . All rights reserved.