Android多点触摸.docx
《Android多点触摸.docx》由会员分享,可在线阅读,更多相关《Android多点触摸.docx(13页珍藏版)》请在冰豆网上搜索。
Android多点触摸
第一章摘要在Linux内核支持的基础上,Android在其2.0源码中加入多点触摸功能。
由此触摸屏在Android的frameworks被完全分为2种实现途径:
单点触摸屏的单点方式,多点触摸屏的单点和多点方式。
第二章软件位在Linux的input.h中,多点触摸功能依赖于以下几个主要的软件位:
………………………..
#defineSYN_REPORT0
#defineSYN_CONFIG1
#defineSYN_MT_REPORT2
………………………...
#defineABS_MT_TOUCH_MAJOR0x30/*Majoraxisoftouchingellipse*/
#defineABS_MT_TOUCH_MINOR0x31/*Minoraxis(omitifcircular)*/
#defineABS_MT_WIDTH_MAJOR0x32/*Majoraxisofapproachingellipse*/
#defineABS_MT_WIDTH_MINOR0x33/*Minoraxis(omitifcircular)*/
#defineABS_MT_ORIENTATION0x34/*Ellipseorientation*/
#defineABS_MT_POSITION_X0x35/*CenterXellipseposition*/
#defineABS_MT_POSITION_Y0x36/*CenterYellipseposition*/
#defineABS_MT_TOOL_TYPE0x37/*Typeoftouchingdevice*/
#defineABS_MT_BLOB_ID0x38/*Groupasetofpacketsasablob*/
…………………………
在Android中对应的软件位定义在RawInputEvent.java中:
…………………..
publicclassRawInputEvent{
……………….
publicstaticfinalintCLASS_TOUCHSCREEN_MT=0x00000010;
………………..
publicstaticfinalintABS_MT_TOUCH_MAJOR=0x30;
publicstaticfinalintABS_MT_TOUCH_MINOR=0x31;
publicstaticfinalintABS_MT_WIDTH_MAJOR=0x32;
publicstaticfinalintABS_MT_WIDTH_MINOR=0x33;
publicstaticfinalintABS_MT_ORIENTATION=0x34;
publicstaticfinalintABS_MT_POSITION_X=0x35;
publicstaticfinalintABS_MT_POSITION_Y=0x36;
publicstaticfinalintABS_MT_TOOL_TYPE=0x37;
publicstaticfinalintABS_MT_BLOB_ID=0x38;
………………….
publicstaticfinalintSYN_REPORT=0;
publicstaticfinalintSYN_CONFIG=1;
publicstaticfinalintSYN_MT_REPORT=2;
………………..
在Android中,多点触摸的实现方法在具体的代码实现中和单点是完全区分开的。
在Android代码的EventHub.cpp中,单点屏和多点屏由如下代码段来判定:
intEventHub:
:
open_device(constchar*deviceName)
{
………………………
if(test_bit(ABS_MT_TOUCH_MAJOR,abs_bitmask)
&&test_bit(ABS_MT_POSITION_X,abs_bitmask)
&&test_bit(ABS_MT_POSITION_Y,abs_bitmask)){
device->classes|=CLASS_TOUCHSCREEN|CLASS_TOUCHSCREEN_MT;
//LOGI("Itisamulti-touchscreen!
");
}
//single-touch?
elseif(test_bit(BTN_TOUCH,key_bitmask)
&&test_bit(ABS_X,abs_bitmask)
&&test_bit(ABS_Y,abs_bitmask)){
device->classes|=CLASS_TOUCHSCREEN;
//LOGI("Itisasingle-touchscreen!
");
}
………………..
}
我们知道,在触摸屏驱动中,通常在probe函数中会调用input_set_abs_params给设备的input_dev结构体初始化,这些input_dev的参数会在Android的EventHub.cpp中被读取。
如上可知,如果我们的触摸屏想被当成多点屏被处理,只需要在驱动中给input_dev额外增加以下几个参数即可:
input_set_abs_params(mcs_data.input,ABS_MT_POSITION_X,pdata->abs_x_min,pdata->abs_x_max,0,0);
input_set_abs_params(mcs_data.input,ABS_MT_POSITION_Y,pdata->abs_y_min,pdata->abs_y_max,0,0);
input_set_abs_params(mcs_data.input,ABS_MT_TOUCH_MAJOR,0,15,0,0);
//相当于单点屏的ABX_PRESSURE
input_set_abs_params(mcs_data.input,ABS_MT_WIDTH_MAJOR,0,15,0,0);
//相当于单点屏的ABS_TOOL_WIDTH
注:
为了让我们的驱动代码支持所有的Android版本,无论是多点屏还是单点屏,一般都会保留单点屏的事件,如ABS_TOUCH,ABS_PRESSURE,ABS_X,ABS_Y等。
另外,由于在Android2.0前支持多点的frameworks大多是用HAT0X,HAT0Y来实现的,所以一般也会上报这2个事件。
第三章同步方式由于多点触摸技术需要采集到多个点,然后再一起处理这些点,所以在软件实现中需要保证每一波点的准确性和完整性。
因此,Linux内核提供了input_mt_sync(structinput_dev*input)函数。
在每波的每个点上报后需要紧跟一句input_mt_sync(),当这波所有点上报后再使用input_sync()进行同步。
例如一波要上报3个点:
/*上报点1*/
……………..
input_mt_sync(input);
/*上报点2*/
……………..
input_mt_sync(input);
/*上报点3*/
……………..
input_mt_sync(input);
input_sync(input);
注:
即使是仅上报一个点的单点事件,也需要一次input_my_sync。
文章出处:
飞诺网():
在Android的KeyInputQueue.java中,系统创建了一个线程,然后把所有的Input事件放入一个队列:
publicabstractclassKeyInputQueue{
……………………
ThreadmThread=newThread("InputDeviceReader"){
publicvoidrun(){
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
try{
RawInputEventev=newRawInputEvent();
while(true){
InputDevicedi;
//block,doesn'treleasethemonitor
readEvent(ev);
if(ev.type==RawInputEvent.EV_DEVICE_ADDED){
synchronized(mFirst){
di=newInputDevice(ev.deviceId);
mDevices.put(ev.deviceId,di);
configChanged=true;
}
}elseif(ev.type==RawInputEvent.EV_DEVICE_REMOVED){
synchronized(mFirst){
Log.i(TAG,"Deviceremoved:
id=0x"
+Integer.toHexString(ev.deviceId));
di=mDevices.get(ev.deviceId);
if(di!
=null){
mDevices.delete(ev.deviceId);
configChanged=true;
}else{
Log.w(TAG,"Baddeviceid:
"+ev.deviceId);
}
}
}else{
di=getInputDevice(ev.deviceId);
//firstcrackatit
send=preprocessEvent(di,ev);
if(ev.type==RawInputEvent.EV_KEY){
di.mMetaKeysState=makeMetaState(ev.keycode,
ev.value!
=0,di.mMetaKeysState);
mHaveGlobalMetaState=false;
}
}
if(di==null){
continue;
}
if(configChanged){
synchronized(mFirst){
addLocked(di,SystemClock.uptimeMillis(),0,
RawInputEvent.CLASS_CONFIGURATION_CHANGED,
null);
}
}
if(!
send){
continue;
}
synchronized(mFirst){
……………………….
if(type==RawInputEvent.EV_KEY&&
(classes&RawInputEvent.CLASS_KEYBOARD)!
=0&&
(scancodescancode>RawInputEvent.BTN_LAST)){
/*键盘按键事件*/
…………………….
}elseif(ev.type==RawInputEvent.EV_KEY){
/*下面是EV_KEY事件分支,只支持单点的触摸屏有按键事件,
*而支持多点的触摸屏没有按键事件,只有绝对坐标事件
*/
if(ev.scancode==RawInputEvent.BTN_TOUCH&&
(classes&(RawInputEvent.CLASS_TOUCHSCREEN
|RawInputEvent.CLASS_TOUCHSCREEN_MT))
==RawInputEvent.CLASS_TOUCHSCREEN){
/*只支持单点的触摸屏的按键事件*/
…………………………………
}elseif(ev.scancode==RawInputEvent.BTN_MOUSE&&
(classes&RawInputEvent.CLASS_TRACKBALL)!
=0){
/*鼠标和轨迹球*/
……………………….
}elseif(ev.type==RawInputEvent.EV_ABS&&
(classes&RawInputEvent.CLASS_TOUCHSCREEN_MT)!
=0){
/*下面才是多点触摸屏上报的事件*/
if(ev.scancode==RawInputEvent.ABS_MT_TOUCH_MAJOR){
di.mAbs.changed=true;
di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+MotionEvent.SAMPLE_PRESSURE]=ev.value;
}elseif(ev.scancode==RawInputEvent.ABS_MT_POSITION_X){
di.mAbs.changed=true;
di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+MotionEvent.SAMPLE_X]=ev.value;
}elseif(ev.scancode==RawInputEvent.ABS_MT_POSITION_Y){
di.mAbs.changed=true;
di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+MotionEvent.SAMPLE_Y]=ev.value;
}elseif(ev.scancode==RawInputEvent.ABS_MT_WIDTH_MAJOR){
di.mAbs.changed=true;
di.mAbs.mNextData[di.mAbs.mAddingPointerOffset
+MotionEvent.SAMPLE_SIZE]=ev.value;
}
/*上面这段就是多点触摸屏要用到的事件上报部分;
*使用一个数组mNextData来保存,其中di.mAbs.mAddingPointerOffset
*是当前点的偏移量,在每个点中还在MotionEvent中定义了X,Y,PRESSURE
*SIZE等偏移量,多点触摸屏的压力值由绝对坐标事件ABS_MT_TOUCH_MAJOR确定。
*/
}elseif(ev.type==RawInputEvent.EV_ABS&&
(classes&RawInputEvent.CLASS_TOUCHSCREEN)!
=0){
/*这里是对单点触摸屏上报坐标事件的新的处理方法,同样使用了数组来保存*/
if(ev.scancode==RawInputEvent.ABS_X){
di.mAbs.changed=true;
di.curTouchVals[MotionEvent.SAMPLE_X]=ev.value;
}elseif(ev.scancode==RawInputEvent.ABS_Y){
di.mAbs.changed=true;
di.curTouchVals[MotionEvent.SAMPLE_Y]=ev.value;
}elseif(ev.scancode==RawInputEvent.ABS_PRESSURE){
di.mAbs.changed=true;
di.curTouchVals[MotionEvent.SAMPLE_PRESSURE]=ev.value;
di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
+MotionEvent.SAMPLE_PRESSURE]=ev.value;
}elseif(ev.scancode==RawInputEvent.ABS_TOOL_WIDTH){
di.mAbs.changed=true;
di.curTouchVals[MotionEvent.SAMPLE_SIZE]=ev.value;
di.curTouchVals[MotionEvent.NUM_SAMPLE_DATA
+MotionEvent.SAMPLE_SIZE]=ev.value;
}
…………………………………………….}
/*下面是关键的同步处理方法*/
if(ev.type==RawInputEvent.EV_SYN
&&ev.scancode==RawInputEvent.SYN_MT_REPORT
&&di.mAbs!
=null){
/*在这里实现了对SYN_MT_REPORT事件的处理,
*改变了di.mAbs.mAddingPointerOffset的值,从而将
*新增的点的参数保存到下一组偏移量的位置。
*/
…………………….
finalintnewOffset=(num<=InputDevice.MAX_POINTERS)
?
(num*MotionEvent.NUM_SAMPLE_DATA)
:
(InputDevice.MAX_POINTERS*
MotionEvent.NUM_SAMPLE_DATA);
di.mAbs.mAddingPointerOffset=newOffset;
di.mAbs.mNextData[newOffset
+MotionEvent.SAMPLE_PRESSURE]=0;
}
……………….
}elseif(send||(ev.type==RawInputEvent.EV_SYN
&&ev.scancode==RawInputEvent.SYN_REPORT)){
/*这里实现了对SYN_REPORT事件的处理
*如果是单点触摸屏,即使用di.curTouchVals数组保存的点
*转化为多点触摸屏的mNextData数组保存
*最后是调用InputDevice中的generateAbsMotion处理这个数组。
这个函数
*的具体实现方法将在后面补充
*/
…………………………..
ms.finish();//重置所有点和偏移量
……………………..
}
由于上层的代码仍然使用ABS_X,ABS_Y这些事件,为了使多点触摸屏代码有良好的兼容性,在KeyInputQueue.java的最后,我们将多点事件类型转化为单点事件类型,返回一个新的InputDevice:
privateInputDevicenewInputDevice(intdeviceId){
intclasses=getDeviceClasses(deviceId);
Stringname=getDeviceName(deviceId);
InputDevice.AbsoluteInfoabsX;
InputDevice.AbsoluteInfoabsY;
InputDevice.AbsoluteInfoabsPressure;
InputDevice.AbsoluteInfoabsSize;
if((classes&RawInputEvent.CLASS_TOUCHSCREEN_MT)!
=0){
absX=loadAbsoluteInfo(deviceId,
RawInputEvent.ABS_MT_POSITION_X,"X");
absY=loadAbsoluteInfo(deviceId,
RawInputEvent.ABS_MT_POSITION_Y,"Y");
absPressure=loadAbsoluteInfo(deviceId,
RawInputEvent.ABS_MT_TOUCH_MAJOR,"Pressure");
absSize=loadAbsoluteInfo(deviceId,
RawInputEvent.ABS_MT_WIDTH_MAJOR,"Size");
}elseif((classes&RawInputEvent.CLASS_TOUCHSCREEN)!
=0){
absX=loadAbsoluteInfo(deviceId,
RawInputEvent.ABS_X,"X");
absY=loadAbsoluteInfo(deviceId,
RawInputEvent.ABS_Y,"Y");
absPressure=loadAbsoluteInfo(deviceId,
RawInputEvent.ABS_PRESSURE,"Pressure");
absSize=loadAbsoluteInfo(deviceId,
RawInputEvent.ABS_TOOL_WIDTH,"Size");
}else{
absX=null;
absY=null;
absPressure=null;
absSize=null;
}
returnnewInputDevice(deviceId,classes,name,absX,absY,absPressure,absSize);
}
文章出处:
飞诺网():
第四章触摸事件数组的处理上面我们曾说到generateAbsMotion这个方法,它们在InputDevice类的内部类MotionState中实现,该类被定义为InputDevice类的静态成员类(staticclass),调用它们可以直接使用:
InputDeviceClass.MotionStateClass.generateAbsMotion()。
publicclassIn