天天看點

android 多點 第一章摘要 第二章軟體位 第三章同步方式 第四章觸摸事件數組的處理 第五章接口 第六章總結

在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在軌迹球和單點屏事件方式中也全使用了新的變量名,以友善多點屏事件同樣能使用這些變量,是以修改時還需要注意許多細節方面。

繼續閱讀