在Linux核心支援的基礎上,Android在其2.0源碼中加入多點觸摸功能。由此觸摸屏在Android的frameworks被完全分為2種實作途徑:單點觸摸屏的單點方式,多點觸摸屏的單點和多點方式。
在Linux的input.h中,多點觸摸功能依賴于以下幾個主要的軟體位:
………………………..
#define SYN_REPORT0
#define SYN_CONFIG1
#define SYN_MT_REPORT2
………………………...
#define ABS_MT_TOUCH_MAJOR0x30/* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR0x31/* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR0x32/* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR0x33/* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION0x34/* Ellipse orientation */
#define ABS_MT_POSITION_X0x35/* Center X ellipse position */
#define ABS_MT_POSITION_Y0x36/* Center Y ellipse position */
#define ABS_MT_TOOL_TYPE0x37/* Type of touching device */
#define ABS_MT_BLOB_ID0x38/* Group a set of packets as a blob */
…………………………
在Android中對應的軟體位定義在RawInputEvent.java中:
…………………..
public class RawInputEvent {
……………….
public static final int CLASS_TOUCHSCREEN_MT = 0x00000010;
………………..
public static final int ABS_MT_TOUCH_MAJOR = 0x30;
public static final int ABS_MT_TOUCH_MINOR = 0x31;
public static final int ABS_MT_WIDTH_MAJOR = 0x32;
public static final int ABS_MT_WIDTH_MINOR = 0x33;
public static final int ABS_MT_ORIENTATION = 0x34;
public static final int ABS_MT_POSITION_X = 0x35;
public static final int ABS_MT_POSITION_Y = 0x36;
public static final int ABS_MT_TOOL_TYPE = 0x37;
public static final int ABS_MT_BLOB_ID = 0x38;
………………….
public static final int SYN_REPORT = 0;
public static final int SYN_CONFIG = 1;
public static final int SYN_MT_REPORT = 2;
在Android中,多點觸摸的實作方法在具體的代碼實作中和單點是完全區分開的。在Android代碼的EventHub.cpp中,單點屏和多點屏由如下代碼段來判定:
int EventHub::open_device(const char *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("It is a multi-touch screen!");
}
//single-touch?
else if (test_bit(BTN_TOUCH, key_bitmask)
&& test_bit(ABS_X, abs_bitmask)
&& test_bit(ABS_Y, abs_bitmask)) {
device->classes |= CLASS_TOUCHSCREEN;
//LOGI("It is a single-touch screen!");
}
我們知道,在觸摸屏驅動中,通常在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(struct input_dev * input)函數。在每波的每個點上報後需要緊跟一句input_mt_sync(), 當這波所有點上報後再使用input_sync()進行同步。例如一波要上報3個點:
/* 上報點1*/
……………..
input_mt_sync(input);
/* 上報點2*/
/* 上報點3*/
input_sync(input);
注:即使是僅上報一個點的單點事件,也需要一次input_my_sync。
上面我們曾說到generateAbsMotion這個方法,它們在InputDevice類的内部類MotionState中實作,該類被定義為InputDevice類的靜态成員類(static class),調用它們可以直接使用:
InputDeviceClass.MotionStateClass.generateAbsMotion()。
public class InputDevice {
……………………………
static class MotionState {//下面是這個内部類的幾個函數
……………………………….
/* mLastNumPointers 為上一個動作在觸屏上按鍵的個數 */
int mLastNumPointers = 0;
final int[] mLastData = new int[MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS];
/* mNextNumPointers 為下一個動作在觸屏上按鍵的個數 */
/* 通過對這2個值大小的判斷,可以确認新的動作方式 */
int mNextNumPointers = 0;
final int[] mNextData = new int[(MotionEvent.NUM_SAMPLE_DATA * MAX_POINTERS)
+ MotionEvent.NUM_SAMPLE_DATA];
………………………………….
int[] generateAveragedData(int upOrDownPointer, int lastNumPointers,
int nextNumPointers) { //平滑處理
…………………………………….
}
private boolean assignPointer(int nextIndex, boolean allowOverlap) {//指派按鍵
……………………………………
private int updatePointerIdentifiers() {//更新按鍵ID
………………………………….
void removeOldPointer(int index) {
MotionEvent generateAbsMotion(InputDevice device, long curTime,
long curTimeNano, Display display, int orientation,
int metaState) {
int upOrDownPointer = updatePointerIdentifiers();
final int numPointers = mLastNumPointers;
………………………………………
/* 對行為的判斷 */
if (nextNumPointers != lastNumPointers) { //前後在觸屏上點個數不同,說明有手指up或down
if (nextNumPointers > lastNumPointers) {
if (lastNumPointers == 0) { //上次觸屏上沒有按鍵,新值又大,說明有按鍵按下
action = MotionEvent.ACTION_DOWN;
mDownTime = curTime;
} else {//有新點按下,配置設定給新點ID号
action = MotionEvent.ACTION_POINTER_DOWN
| (upOrDownPointer << MotionEvent.ACTION_POINTER_ID_SHIFT);
}
} else {//新動作比原來pointer數量少
if (numPointers == 1) { //原來隻有1個點按下,是以現在的動作是全部按鍵up
action = MotionEvent.ACTION_UP;
} else { //原來有多點按下,現在是ACTION_POINTER_UP動作,
action = MotionEvent.ACTION_POINTER_UP
}
currentMove = null;
} else { //前後觸屏pointer個數相同,是以是移動動作ACTION_MOVE
action = MotionEvent.ACTION_MOVE;
}
/* 後面則是根據螢幕的height和width以及螢幕方向orientation對這些點進行二次處理 */
MotionEvent generateRelMotion(InputDevice device, long curTime,
long curTimeNano, int orientation, int metaState) {
/* 軌迹球等的處理方式 */
…………………………………………..
}
void finish() { //結束這輪動作
mNextNumPointers = mAddingPointerOffset = 0;
mNextData[MotionEvent.SAMPLE_PRESSURE] = 0;
…………………………………….
……………………………….
……………………………………
我們平時所看到的用2個手指對圖檔放大縮小、旋轉等手勢都是由應用程式編寫浏覽器實作的。這些應用程式大多會使用Android2.0以上的在MotionEvent.java中實作的新的接口。是以,我們還需要給MotionEvent類補充盡量全的接口。這裡可以完全參照google新的android代碼。
綜上,在硬體支援基礎上,Android1.6如果要實作多點觸摸功能,主要工作可簡述為以下幾個方面:
1、 驅動中,除了增加多點的事件上報方式,還要完全更改單點的事件上報方式。
2、 Android的Frameworks層需要修改的檔案有:EventHub.cpp,RawInputEvent.java,KeyInputQueue.java,InputDevice.java,MotionEvent.java。
3、 編寫新的支援多點觸摸功能的多媒體浏覽器。
4、 為了代碼簡練,android2.0在軌迹球和單點屏事件方式中也全使用了新的變量名,以友善多點屏事件同樣能使用這些變量,是以修改時還需要注意許多細節方面。