native activity 教程
c4droid吧
全部回复
仅看楼主
level 2
叜駣 楼主
以下是本人刚整理的native activity 经验, 其中包括egl开窗,陀螺仪设备输入,以及多点触控输入的知识
2013年10月14日 15点10分 1
level 2
叜駣 楼主
首先,批评下c4droid自带的示例其实是从ndk开发包里"偸来的",居然用的是蛋疼的engine,state这类的struct封装,写得相当凌乱,毫无章法,这可能对大家,包括我的理解造成了不利影响。所以我会以极简的方法来描述如何使用native activity api写程序
2013年10月14日 15点10分 2
谢谢
2015年10月25日 09点10分
就是,看都看不懂
2017年01月27日 04点01分
同意
2018年02月14日 05点02分
level 9
插一下表示支持!
2013年10月14日 15点10分 3
谢谢!
2013年10月14日 20点10分
level 2
叜駣 楼主
先看初始化指令,程序的主入口函数是android_main(struct android_app* app),是一个回传函数,app参数是java系统扔给我们的程序状态, 初始化指令共3条,如下:
app_dummy();
app->onAppCmd = on_app_cmd;
app->onInputEvent = on_input_event;
第一条指令:开窗的必要前提是glue功能没有被忽略,所以需要呼出一个dummy函数: app_dummy()。
第二条指令与第三条指令:
与sdl不同的是,native activity 采用的是回传函数(callback)方式
app参数需要你自定义两个成员回传函数,分别是 onAppCmd,与 onInputEvent,其中onAppCmd处理的是系统事件,onInputEvent处理的是输入事件,接下来我们分别看下这2个回传函数该怎么写
2013年10月14日 15点10分 4
补充下,callback要翻译成回调函数比较好
2013年10月14日 21点10分
level 2
叜駣 楼主
app->onAppCmd = on_app_cmd;
显而易见,我们首先要完成的是 app->onAppCmd,系统事件处理回传函数,也就是自己写的on_app_cmd函数,这个函数的原型是:
void on_app_cmd(struct android_app *app, int event_type)
其中1号参数的app和android_main中的app一样,是java系统扔给我们的程序状态,而2号参数event_type则是java系统扔给我们的当前事件。
至于怎么写,原理很简单,java系统每更新一次,都回扔给我们不同的event_type。所以和写其它的回传函数一样:针对不同event_type执行不同的指令
2013年10月14日 15点10分 5
level 2
叜駣 楼主
event_type,也就是事件类型,主要有4种,处理好了这4种一般来说就足够了:
1. APP_CMD_INIT_WINDOW: 窗口初始化
2. APP_CMD_TERM_WINDOW: 窗口关闭
3. APP_CMD_GAINED_FOCUS: 获得焦点
4.APP_CMD_LOST_FOCUS:失去焦点
其中必须要处理的只有窗口初始化,所以先从窗口初始化谈起
2013年10月14日 15点10分 6
level 2
叜駣 楼主
app->onAppCmd回传函数如果接收到的event_type的值为APP_CMD_INIT_WINDOW,我们就要打开一个可用于opengl渲染的窗口,而native activity开窗用的是egl api,我们可以把egl看成是glut或glfw之类的api,开窗过程如下:
attribs[11] = {EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 8, EGL_NONE}
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(...);
eglChooseConfig(...);
eglGetConfigAttrib(...);
ANativeWindow_setBuffersGeometry(...);
sureface = eglCreateWindowSurface(...);
context=eglCreateWindowContext(...);
eglMakeCurrent(...);
过程如下:
1.把配置属性attribs填好,用eglGetDisplay()获得显示设备,并用eglInitialize()初始化显示设备。
2.以attribs为参考,用eglChooseConfig配置显示设备
3.用eglGetConfigAttrib()获得显示设备的原始图像编号,也就是EGL_NATIVE_VISUAL_ID
4.以获得的EGL_NATIVE_VISUAL_ID为参考,用ANativeWindow_setBuffersGeometry()设置安卓设备的显示缓冲平面(Buffers Geometry)
5.以刚配置好的显示设备为参考,用eglCreateWindowSurface创建一个egl窗口平面
6.以刚配置好的显示设备为参考,用eglCreateContext创建一个上下文
7.用eglMakeCurrent将先前显示设备,窗口平面,上下文设为当前egl的设备,平面,上下文
2013年10月14日 16点10分 7
楼主,这里的…是什么东西,看不懂啊,
2014年10月11日 11点10分
抱歉,问一下啊,那个attrib是什么类型呢?int?
2015年05月31日 15点05分
level 2
叜駣 楼主
然后是窗口关闭,如下:
eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroyContext(display, context);
eglDestroySurface(display, surface);
eglTerminate(display);
过程如下:
1.以EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT为参考,用eglMakeCurrent()使当前显示设备无效化
2.用eglDestroyContext()摧毁显示设备的上下文
3.用eglDestroySurface()摧毁显示设备的显示平面
4.用eglTerminate()中止显示设备
至于APP_CMD_GAINED_FOCUS与APP_CMD_LOST_FOCUS,由于和陀螺仪的配置有关,所以我们一会再谈
2013年10月14日 16点10分 8
Destroy翻译成销毁,析构更好吧
2014年03月20日 13点03分
level 2
叜駣 楼主
完成后的回传函数on_app_cmd()如下
void on_app_cmd(struct android_app *app, int event_type) /* app是java扔给我们的系统状态,event_type是java扔给我们的事件类型 */
{
switch (event_type) /* 我们根据java扔给我们的事件类型做出不同的反应 */
{
case APP_CMD_INIT_WINDOW: /* java扔给我们的事件类型是:初始化窗口 */
/* 在这里把7楼的初始化窗口代码写上 */
break;
case APP_CMD_TERM_WINDOW: /* java扔给我们的事件类型是:关闭窗口 */
/* 在这里把8楼的关闭窗口代码写上 */
break;
case APP_CMD_GAINED_FOCUS: /* java扔给我们的事件类型是:获得焦点 */
/* 讨论陀螺仪配置时,将在这里插入代码 */
break;
case APP_CMD_LOST_FOCUS: /* java扔给我们的事件类型是:失去焦点 */
/* 讨论陀螺仪配置时,将在这里插入代码 */
break;
}
}
2013年10月14日 16点10分 9
level 2
叜駣 楼主
完成了on_app_cmd()回传函数后,我们还要完成第二个自定义成员回传函数,也就是app->onInputEvent,这个函数处理的是键盘。。。[冷]与多点触控事件。在这里只讨论多点触控事件。
2013年10月14日 16点10分 10
level 2
叜駣 楼主
与app->onAppCmd回传函数不同的是,app->onInputEvent的二号参数并不是整型事件id,而是自定义类型:AInputEvent,所以我们不能直接通过它的值来switch,而是要用AInputEvent_getType()口令获得输入事件类型。
多点触控的输入事件类型是AINPUT_EVENT_TYPE_MOTION,也就是说,如果我们将AInputEvent代入AInputEvent_getType()中去,如果返回的是AINPUT_EVENT_TYPE_MOTION,说明用户触碰了屏幕。
2013年10月14日 16点10分 11
level 2
叜駣 楼主
获得用户在屏幕上的触控坐标,要用到的函数有2个:
AMotionEvent_getX(AInputEvent *input_event, int index)
AMotionEvent_getY(AInputEvent *input_event, int index)
其中1号参数*input_event是输入事件,在回传函数中也就是java扔给我们的那个AInputEvent *。2号参数是触控序号,比如我用食指先碰到了屏幕,那么食指的触碰序号就固定为0,
我又将中指碰到了屏幕,那么此时中指的触控序号就固定为1,即使此时我将食指松开,中指的触控序号还是1,而0号便留空了,此时若将无名指碰到屏幕,则无名指的触碰序号为0。这就是多点触控的原理:“触碰者触碰后获得的触控序号是第一个可用的触控序号”
知道了这一点,我们就能通过AMotionEvent_getX()与AMotionEvent_getY()分别获得不同触控者的触碰坐标了。
2013年10月14日 17点10分 12
level 2
叜駣 楼主
现在来写 app->onInputEvent(),也就是on_input_event()函数,我们用触控序号0,1,2,分别检测三根手指的触碰坐标,函数如下:
int on_input_event(struct android_app *app, AInputEvent *input_event) /* app是java扔给我们的系统状态,input_event是java扔给我们的输入事件 */
{
if (AInputEvent_getType(input_event) == AINPUT_EVENT_TYPE_MOTION) /* 我们用AInputEvent_getType()得知输入事件是屏幕触碰 */
{
x0 = AMotionEvent_getX(input_event, 0); /* 将输入事件和触碰序号0,代入AMotionEvent_getX,得到0号手指的触碰横坐标 */
y0 = AMotionEvent_getY(input_event, 0); /* 将输入事件和触碰序号0,代入AMotionEvent_getX,得到0号手指的触碰纵坐标 */
x1 = AMotionEvent_getX(input_event, 1); /* 将输入事件和触碰序号1,代入AMotionEvent_getX,得到1号手指的触碰横坐标 */
y1 = AMotionEvent_getY(input_event, 1); /* 将输入事件和触碰序号1,代入AMotionEvent_getX,得到1号手指的触碰纵坐标 */
x2 = AMotionEvent_getX(input_event, 2); /* 将输入事件和触碰序号2,代入AMotionEvent_getX,得到2号手指的触碰横坐标 */
y2 = AMotionEvent_getY(input_event, 2); /* 将输入事件和触碰序号2,代入AMotionEvent_getX,得到2号手指的触碰纵坐标 */
}
return 0; /* 应当返回0,但如果认为input_event事件已经完全处理完毕的话,可以返回1 */
}
2013年10月14日 17点10分 13
level 2
叜駣 楼主
这样两个重要的回传函数全部都写好了, 只需要在android_main中简单的赋一下值:
app->onAppCmd = on_app_cmd;
app->onInputEvent = on_input_event;
android_main的骨架就已经完成了,如下:
void android_main(struct android_app* app)/* app是java扔给我们的系统状态 */
{
app_dummy();/* 确保glue的功能没有被忽略*/
app->onAppCmd = on_app_cmd;/* 将9楼完成的on_app_cmd回传函数赋值到app的 onAppCmd成员*/
app->onInputEvent = on_input_event;/* 将13楼完成的on_input_event回传函数赋值到app的 onInputEvent成员 */
android_app = app; /* 将app赋值到一个全局变量 (后面讨论)*/
/* 陀螺仪初始化代码(后面讨论) */
}
2013年10月14日 17点10分 14
level 2
叜駣 楼主
开始处理,我们要处理的是名为source的android_poll_source结构指针,先前我们将source的内存位置代入了ALooper_pollAll,保存了当时的事件源,所以此时的source必然是一个有效指针或NULL,不会出现其它问题,如果事件源不为NULL,我们对其进行处理,struct android_poll_source有一个名为process的成员函数,只需要把java扔给我们的系统状态作为参数1,事件源指针source作为参数2代入即可,如下:
source->process(android_app, source); /* android_app 是我们在14楼保留的全局变量,口令是android_app = app; */
这样事件源的处理就完成了
2013年10月14日 18点10分 16
level 2
叜駣 楼主
这样我们的状态刷新函数也完成了,将其命名为refresh(),如下:
refresh()
{
struct android_poll_source* source;
eglSwapBuffers(display, surface); /* 交换显示缓冲,display与surface于7楼创建,假定它们是全局变量 */
while (ALooper_pollAll(0, NULL, NULL, &source)) >= 0) /* 有事件发生,保存事件源到source */
{
if (source)/* 如果事件源有效 */
rp1->process(android_app, source);/* 将java扔给我们的系统状态与事件源扔进事件源中名为process的成员函数,对事件进行处理 */
}
2013年10月14日 18点10分 17
ALooper_pollAll(0, NULL, NULL, &source))这句多了个右括号, rp1->process(android_app, source)改为 source->process(android_app, source)
2013年10月14日 21点10分
level 2
叜駣 楼主
这样,一个Native Activity程序的骨架就都出来了,我们来归纳一下步骤:
1.创建android_main()入口函数
2.呼出app_dummy(),确保glue功能没有被忽略
3.创建on_app_cmd()回传函数,用来处理4种主要系统事件,分别是:APP_CMD_INIT_WINDOW(窗口初始化), APP_CMD_TERM_WINDOW( 窗口关闭), APP_CMD_GAINED_FOCUS(获得焦点),APP_CMD_LOST_FOCUS(失去焦点)。
4.创建on_input_event()回传函数,用来处理键盘输入与触控输入
5.在android_main()中,把on_app_cmd()和on_input_event()分别赋值到java扔给我们的系统状态里对应的函数,也就是app->onAppCmd()与app->onInputEvent()
6.创建一个可以放在程序主循环中的状态刷新函数,用来交换显示缓冲,以及拉取安卓设备的事件。
好了,接下来我们将探讨如何在Native Activity程序中获得陀螺仪的状态
2013年10月14日 18点10分 18
level 2
叜駣 楼主
与陀螺议有关的类型有4个:
1:感应器: ASensor
2.感应器管理器:ASensorManager
3.感应器事件序列:ASensorEventQueue
4.感应器事件:ASensorEvent
要让陀螺仪运转,先要在android_main()入口函数中依次初始化感应器管理器,感应器,以及感应器事件序列。
假如感应器名为sensor, 感应器管理器名为sensor_manager,感应器事件序列名为sensor_event_queue,初始化它们需要的口令如下:
sensor_manager = ASensorManager_getInstance();/* 获得感应器管理器 */
sensor = ASensorManager_getDefaultSensor(sensor_manager, ASENSOR_TYPE_ACCELEROMETER);/* 从感应器管理器中获得陀螺议感应器(严格的说是重力感应器accelerometer,并不是陀螺仪gyroscope,但陀螺仪听著比较亲切。。。[滑稽]) */
sensor_event_queue = ASensorManager_createEventQueue(sensor_manager, app->looper, LOOPER_ID_USER, NULL, NULL);/* 用感应器管理器创建事件序列,并绑定刷新时的事件返回码 */
头两条指令很好理解,但第 三条的内容将在下面进行解释
2013年10月14日 19点10分 19
补充说明一下: sensor = ASensorManager_getDefaultSensor(sensor_manager, ASENSOR_TYPE_ACCELEROMETER)中,把ASENSOR_TYPE_ACCELEROMETER替换为ASENSOR_TYPE_GYROSCOPE,即可开启真正的陀螺仪!当然有的设备并不支持,所以要根据情况考量
2013年10月15日 03点10分
level 2
叜駣 楼主
sensor_event_queue = ASensorManager_createEventQueue(sensor_manager, app->looper, LOOPER_ID_USER, NULL, NULL);
这条指令的意思是为系统状态(也就是android_app或android_main中的app)创建事件序列,让系统状态的进行循环更新(looper)时,返回与感应器相关事件,能通过事件代码:LOOPER_ID_USER接收。
简而言之,就是让安卓在更新时返回与感应器相关的事件。
2013年10月14日 19点10分 20
level 2
叜駣 楼主
但要让陀螺仪返回事件,我们需要先打开螺蛇仪,而不需要陀螺仪的时候我们要关闭它,这就是9楼提到的在 APP_CMD_GAINED_FOCUS与 APP_CMD_LOST_FOCUS所要添加的代码,一共要用到3条函数,如下:
ASensorEventQueue_enableSensor(sensor_event_queue, sensor);/* 为感应器事件队列(sensor_event_queue)启用感应器(sensor) */
ASensorEventQueue_setEventRate(sensor_event_queue, sensor, rate); /* 设定感应器的事件频率(单位:微秒。。。0.000001秒,可以吐槽么。。。。[冷]) */
ASensorEventQueue_disableSensor(sensor_event_queue, sensor);/* 为感应器事件队列(sensor_event_queue)关闭感应器(sensor) */
而在APP_CMD_GAINED_FOCUS,也就是窗口获得焦点时,我们要启用感应器,并且设定感应器的事件频率,在APP_CMD_LOST_FOCUS,也就是窗口失去焦点时,我们要关闭感应器
2013年10月14日 19点10分 21
1 2 3 4 尾页