计划按以下的内容更新

简单 UI

关于开发一个应用,要有自己的应用名(显示用),和包名(真正唯一的应用名),简单说一台 Android 手机中所有应用的包名是唯一的,如果新安装的应用包名和已安装的应用重复则只能替换安装(不可共存)。这就需要修改 AndroidManifest.xml。

修改 AndroidManifest.xml

先看一下之前的例子里的 AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"	
	package="gooid.mainnative"
        ...
	<application android:label="WallPaperTwinkle" android:hasCode="false"  	>
        ...
		<meta-data android:name="android.app.lib_name" android:value="WallPaperTwinkle" />
        ...
	</application>
</manifest> 
go build -buildmode c-shared -o basic\lib\%ABI%\libUIDemo.so

通过定制包名,应用名,和库名就可以区分每个不同的应用了(其实还有应用图标,这个以后再讲)。
接下来就该编码了。

应用回调说明

从NDK的角度来说,一个 Android 应用实现以下的 Callbacks 就可以了,其中的回调函数可以为空,实现必要的回调就行了,所以下面忽略了不常用的回调。

type Callbacks struct {
	Create               func(*Activity, []byte)
    ...
	Destroy              func(*Activity)
	...
	FocusChanged         func(*Activity, bool)
    ....

	// Window
	WindowCreated      func(*Activity, *Window)
	WindowDestroyed    func(*Activity, *Window)
	WindowDraw         func(*Activity, *Window)
	WindowResized      func(*Activity, *Window)
	WindowRedrawNeeded func(*Activity, *Window)

	// Touch is called by the app when a touch event occurs.
	Event func(*Activity, *InputEvent)
	// Sensor
	Sensor func(*Activity, []SensorEvent)
}

如果想了解这些回调的详细信息,当然是参考管方文档了。在这里就简单说一下常用的几个。

Activity 相关
  • Create: 应用创建时被调用,从应用的生存周期来说,只调用一次。可用于初始化全局和只需执行一次的操作
  • Destroy: 应用彻底被销毁时调用,但不保证会被调用,所以一些必须要执行的清理/回收工作不能依赖它的调用
  • FocusChanged: 这个好理解,应用得到/失去焦点时被裤调用
事件处理
  • Event: 收到输入事件时被调用,包括 key 和 motion (触摸) 事件
  • Sensor: 收到Sensor事件时被调,比如处理重力加速度变化(G Sensor)和距离感应等
Window 相关
  • WindowCreated: 在 Window 创建后被调用
  • WindowDestroyed: 在 Window 销毁后被调用
  • WindowResized: 在 Window size改变后被调用
  • WindowRedrawNeeded 在 Window 需要 redraw 时调用,一般理解为 Window 的 size、client size 或 focus (准确的说是应用的 focus)改变引起必须要重绘的情况。- WindowDraw 在收到某些事件后,会自动调用。这个不是原生的回调,是为了简化程序逻辑加的,绘制操作在此函数中完成。如果没有提供这个回调,开发者自行在其它地方绘制则有可能绘制失败(openGLES 上下文限制)或应用崩溃。

用 OpenGL ES 实现 UI

在 Android NDK 下要实现图形界面(GUI)需要用OpenGLES来实现,如果熟练OpenGL,那就事半工倍了。这里并不打算细说 OpenGLES 的用法。直接参考 UIDemo, 它简单显示了一个经典的三角形,点击会出现变化。
在这里插入图片描述

有人会说了,我不会 OpenGL, 况且就算会,要用它来实现一套GUI也是很困难的事性。

稍安勿躁,之所以先给这个例子就是让大家清楚它的起源 ^_^ 。
接下来就是一个直正的GUI的例子。

用 Dear ImGui 开发界面

这个应用用 Tree 来显示 Android property (shell getprop),它是通过使用 Dear ImGui 封装的 Go 版本 来显示 Android 所有属性。
在这里插入图片描述
因为 Dear Imgui 源码是 C++,因此这次编译要用到 g++ ,还是先要配置一下环境,这次比较简单,先按上一篇所说的配置好,在上一篇的基础上:

  set CXX=%GCC_H%-g++
  set CGO_CXXFLAGS=-D__ANDROID_API__=%SDK_API%

这样就可以编译过了。

可能有老鸟会感觉出来总觉得缺点什么?恭喜你,你的感觉是对的,还没有说明关于 C++ 头文件相关的操作,幸运的是 Dear Imgui 并没有 include C++ 的头文件,所以上面没有提到它。如果要用到,类似的考考考(拷贝)就好了。
通过这一节,同时也了解了在 golang 中集成 C++ 代码的环境配置方法。

关于 Dear Imgui

adb shell free

也可以通过把增加 WINDOWSCALE 来减少内存的使用。

补充说明

在之后的例子中看到有这样的代码

	item := cam.previewIndex
	if imgui.Combo("pixels", &item, cam.comboText) {
		cam.previewIndex = item
	}
imgui.Combo("pixels", &cam.previewIndex, cam.comboText)

其它 GUI

如果想尝试其它的 GUI 还有其它选择。这里列举其中一些。
关于 Golang 版的 Dear Imgui 有另外两个:

nuklear C 版本的单文件 GUI

gxui 纯 Go 实现的 GUI (实验性的,已不再维护)

NanoGUI.go 也是纯 Go