在 Android 外部 imgui 绘制 (一) 里介绍了怎么实现一个外部 imgui, 其实还有一种方法,就是在 aosp 环境下编译一个可执行文件出来,有点类似于
可执行文件 + skia 绘制
但这个其实很麻烦,需要下载 android 源码 (100 多 g), 然后还要自己编译,最后还得修改代码。所以不太建议这么做。虽然效率可能确实会有提升.
获取触摸
在绘制 imgui 后,其实是没有触摸反馈的,原因很简单,在官方示例中,他的入口是
1 | void android_main(struct android_app* app) |
这个就很像之前 ndk-sample 的 native-activity 了.
https://github.com/android/ndk-samples/tree/main/native-activity
, 其实可以把这个官方的 imgui 示例改造成 native-activity.
注意看官方示例
1 | // Poll all events. If the app is not visible, this loop blocks until g_Initialized == true. |
你就可以发现它是通过调用 ALooper_pollAll 来获取系统的输入事件,
然而在我们的 hello-gl2 代码里并不能通过 ALooper_pollAll 来获取输入事件的,
那么怎么获取系统输入呢?
(ue4) 找到 android_app 并 hook onInputEvent 函数
这个方法可以在 ue4 的游戏中找到,可以把 ue4 的游戏理解成 native-activity, 然后找到 android_app, 在它的 onInputEvent 里挂钩子,将屏幕输入事件转发给 imgui.
双悬浮窗
比较简单的实现方案,将一个可点击透明的悬浮窗覆盖在 imgui 窗口上,且该悬浮窗随着 imgui 窗口移动,把对该窗口的输入事件转发到 imgui 窗口里.
读取系统触摸
这个方法就是直接读系统输入的方法了,也就是安卓源码是怎么读取屏幕输入的.
原理就是读取 /dev/input/event? 文件,这里的?表示代表屏幕输入文件的数字.
1 | sprintf(dev_name, "/dev/input/event%d", i); |
然后将原始的屏幕输入转成安卓的屏幕输入。最后传给 imgui 即可.
这里可以参考
linux 读取触摸屏事件数据
至于怎么加工原始的输入数据,可以参考安卓源码.
在我的手机上,触摸屏的分辨率是 14400x32000.
而我的屏幕分辨率是 1080x2400.
所以需要进一步转换.
大致如下:
1 | x = (x-xmin) * 手机像素宽 / (xmax-xmin); |
这里 xmin,xmax,ymin,ymax 可以通过命令拿到.
1 | getevent -p /dev/input/event7 |
1 | 0035 : value 0, min 0, max 14399, fuzz 0, flat 0, resolution 0 |
屏幕像素可以通过 getRealMetrics 获取.
1 | DisplayMetrics dm = new DisplayMetrics(); |
参考
Android 从触碰屏幕开始的事件采集,解析及分发 (1).
屏幕旋转判断
当屏幕旋转后系统会回调 onSurfaceChanged 方法
在 onSurfaceChanged 里读取屏幕旋转方向,然后将屏幕方向赋值给 native 的全局变量.
1 | public void onSurfaceChanged(GL10 gl, int width, int height) { |
屏幕旋转后输入事件的处理
旋转后,屏幕坐标会旋转,但是读取到的输入坐标不会改变,此时需要将横坐标改为纵坐标,横坐标改为屏幕宽度减去横坐标.
1 | io.MousePos = ImVec2(y, physic_w - x); |
Android12 的改进
按照官方说明
不受信任的触摸事件被屏蔽.
需要做出如下改动
1 | # A specific app |
你可以直接敲下面的命令
1 | adb shell settings put global block_untrusted_touches 0 |
之后你的 imgui 就可以随意的拖动,点击了.