官术网_书友最值得收藏!

2.3 設備事件的響應

在應用程序的控制方面,在大多數場合下使用的是屏幕上的控件,控件本身也是包含了事件處理的載體。

在某些情況下也需要直接響應由輸入設備發送過來的事件。在Android系統中,輸入設備主要為鍵盤、觸摸屏、軌跡球和鼠標,在Android中,鍵盤響應的是KeyEvent,后三者通常響應的是MotionEvent。

2.3.1 鍵盤事件的響應

鍵盤是Android中的主要輸入設備,對按鍵事件的處理是在程序中使用鍵盤的核心內容。

鍵盤的事件通常用KeyEvent來表示,KeyEvent是android.view包中的一個類,主要包含以下一些方法:

        final int  getKeyCode()         // 獲得按鍵碼
        final int  getAction()          // 獲得按鍵的動作
        final int  getFlags()           // 獲得標志
        final int  getRepeatCount()     // 獲得重復的信息
        final int  getScanCode()        // 獲得掃描碼

通過KeyEvent接口,可以獲得按鍵相關的詳細信息,這些信息都是用整數值來表示的。按鍵相關的信息中,其中最主要的是KeyCode,用于標識哪一個按鍵發生了事件,Action表示按鍵的動作(抬起、按下),RepeatCount表示同一個按鍵被按下的次數,ScanCode(掃描碼)表示底層按鍵的原始標識。

android.view.KeyEvent.Callback是一個接口,用于表示發生按鍵事件后的回調,其中包含了如下幾個方法:

        public abstract boolean onKeyDown(int keyCode, KeyEvent event)
        public abstract boolean onKeyUp(int keyCode, KeyEvent event)
        public abstract boolean onKeyMultiple(int keyCode, int count, KeyEvent event)
        public abstract boolean onKeyLongPress(int keyCode, KeyEvent event)

KeyEvent.Callback接口中的幾個方法,均具有整型和KeyEvent類型兩個參數,KeyEvent類型中實際上包含整型的信息。

以上幾個方法的返回類型均為boolean類型,如果返回true,表示自己完成這個事件的處理,如果返回false,表示由下一個接收器處理這個事件。

View和Activity都已經實現KeyEvent.Callback接口。在這兩個類的繼承者中,可以通過重新實現以上的幾個方法來響應按鍵的事件。

通常情況下,按鍵事件屬于整個屏幕,因此在Activity中響應即可。按照一般的邏輯,當鍵盤上一個按鍵被按下的時候,不會去區分這個按鍵屬于哪一個控件。

Activity中的dispatchKeyEvent()方法用于截獲一個按鍵的事件:

        public boolean dispatchKeyEvent (KeyEvent event)

這個方法將在按鍵到達窗口之前被調用,因此通過這個方法可以阻止系統的其他部分不獲得按鍵事件。

以下的示例需要實現的內容是通過鍵盤來控制一個圖片的Alpha值,使用上鍵和右鍵增加圖片的Alpha值,使用下鍵和左鍵減少圖片的Alpha值。

這個按鍵事件響應程序的運行效果如圖2-4所示。

圖2-4 按鍵事件響應程序運行效果(左:初始化;中:中間Alpha值;右:Alpha值為0)

本例的布局文件testkeyevent.xml如下所示:

        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/screen" android:orientation="vertical"
            android:layout_width="fill_parent" android:layout_height="fill_parent" >
            <TextView android:id="@+id/alphavalue" android:layout_gravity="center"
              android:layout_width="wrap_content" android:layout_height="wrap_content" />
            <ImageView android:id="@+id/image"
              android:src="@drawable/robot" android:layout_gravity="center"
              android:layout_width="wrap_content" android:layout_height="wrap_content" />
        </LinearLayout>

從以上的布局文件可以看到,本例包含了一個文本框和一個顯示圖片的控件,這樣文本框可用來顯示當前Alpha的比例值,顯示圖片的控件ImageView用于顯示一個圖片。

本例的源代碼的核心部分實現如下所示:

        public class TestKeyEvent extends Activity {
            private static final String TAG = "TestKeyEvent";
            private ImageView mImage;                           // 界面中的圖片
            private TextView  mAlphavalueText;
            private int mAlphavalue;
            @Override
            protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.testkeyevent);
              mImage = (ImageView) findViewById(R.id.image);
              mAlphavalueText = (TextView) findViewById(R.id.alphavalue);
              mAlphavalue = 100;
              mImage.setAlpha(mAlphavalue);                      // 設置初始化的透明數值
              mAlphavalueText.setText("Alpha = " + mAlphavalue*100/0xff + "%");
            }
            @Override
            public boolean onKeyDown(int keyCode, KeyEvent msg){ // 按下事件的處理
              Log.v(TAG, "onKeyDown: keyCode =  "+ keyCode);    // 打印事件信息
              Log.v(TAG, "onKeyDown: String =  " + msg.toString());
              switch (keyCode) {
                  case KeyEvent.KEYCODE_DPAD_UP:              // 上鍵和右鍵的處理
                  case KeyEvent.KEYCODE_DPAD_RIGHT:
                      mAlphavalue += 20;
                      break;
                  case KeyEvent.KEYCODE_DPAD_DOWN:            // 下鍵和左鍵的處理
                  case KeyEvent.KEYCODE_DPAD_LEFT:
                      mAlphavalue -= 20;
                      break;
                  default:
                      break;
              }
              if(mAlphavalue>=0xFF)mAlphavalue = 0xFF;       // 控制Alpha數值的范圍
              if(mAlphavalue<=0x0)mAlphavalue = 0x0;
              mImage.setAlpha(mAlphavalue);
              mAlphavalueText.setText("Alpha = " + mAlphavalue*100/0xff + "%");
              return super.onKeyDown(keyCode, msg);
            }
            @Override
            public boolean onKeyUp(int keyCode, KeyEvent msg){  // 抬起事件的處理
              Log.v(TAG, "onKeyUp: keyCode =  "+ keyCode);     // 打印事件信息
              Log.v(TAG, "onKeyUp: String =  " + msg.toString());
              return super.onKeyUp(keyCode, msg);
            }
        }

本例使用onKeyDown()方法來獲得按鍵的事件,同類的方法還包括onKeyUp()方法,其參數int keyCode為按鍵碼,KeyEvent msg表示按鍵事件的消息(其中包含了更詳細的內容)。

通過keyCode(int)可以獲得是哪一個按鍵響應,而通過msg(KeyEvent)除了按鍵碼之外,可以獲得更多的信息,例如按鍵的動作、重復信息、掃描碼等內容。

本程序打出的一段Log信息如下所示:

        VERBOSE/TestKeyEvent(771): onKeyDown: keyCode =  20
        VERBOSE/TestKeyEvent(771): onKeyDown: String =  KeyEvent{action=0 code=20 repeat=7
    meta=0 scancode=108 mFlags=8}
        VERBOSE/TestKeyEvent(771): onKeyDown: keyCode =  20
        VERBOSE/TestKeyEvent(771): onKeyDown: String =  KeyEvent{action=0 code=20 repeat=8
    meta=0 scancode=108 mFlags=8}
        VERBOSE/TestKeyEvent(771): onKeyDown: keyCode =  20
        VERBOSE/TestKeyEvent(771): onKeyDown: String =  KeyEvent{action=1 code=20 repeat=0
    meta=0 scancode=108 mFlags=8}

由此可見,按鍵事件KeyEvent可以獲得重復按鍵的次數和這個歷史過程相關的數據信息,在一個按鍵的響應過程中,通常是先發送一個或若干個Down事件,最后是一個Up事件。

按鍵事件的響應有以下幾個需要注意的細節:

(1)對于普通按鍵,通常響應onKeyUp()事件,表示按下的時候不再處理,按鍵抬起時才有效果;

(2)onKeyDown()可以連續響應一個按鍵按下的情況,并且通過KeyEvent的getRepeatCount()可以為重復按鍵增加特殊的處理。

(3)在按鍵處理方法的最后,調用super.onKeyDown()和super.onKeyUp()表示由父類進行默認的按鍵處理。

2.3.2 運動事件的處理

觸摸屏(TouchScreen)和軌跡球(TrackBall)是Android中除鍵盤之外的主要輸入設備。如果需要使用觸摸屏和軌跡球,可以通過使用運動事件(MotionEvent)來接收它們的信息。

對于觸摸屏,一般通過絕對坐標描述它的事件;對于軌跡球,一般通過相對坐標描述它的事件。在Android系統中,鼠標設備的處理方式類似于軌跡球。

MotionEvent是android.view包中的類,用于表示運動事件的坐標、動作等信息。MotionEvent中主要的幾個方法如下所示:

        public final float  getX()                   // 獲得X坐標
        public final float  getY()                   // 獲得Y坐標
        public final int  getAction()                // 獲得動作
        public final float getRawX()                 // 獲得原始的X坐標
        public final float getRawY()                 // 獲得原始的Y坐標

對于這種運動事件,最重要的屬性為運動事件發生的坐標(X,Y),運動的動作表示按下、按住移動、抬起等信息。getRawX()和getRawY()表示沒有根據窗口或者控件做出調整的原始坐標。

運動事件有其歷史屬性,MotionEvent中相關的方法如下所示:

        public final int  getHistorySize()           // 獲得歷史的點數
        public final float  getHistoricalX(int pos)  // 獲得歷史的X坐標
        public final float  getHistoricalY(int pos)  // 獲得歷史的Y坐標

在Android API級別5版本之后,MotionEvent類中還包含了多點觸摸的相關內容,當有多個觸點同時起作用的時候,可以獲得觸點的數目和每一個觸點的坐標。

        public final int  getPointerCount()          // 獲得觸點的數目
        public final float  getX(int pointerIndex)   // 獲得某個觸點的X坐標
        public final float  getY(int pointerIndex)   // 獲得某個觸點的Y坐標

MotionEvent可以獲得多點的基礎信息,而沒有包含手勢解釋的功能。

在Activity和View中,都具有以下兩個方法,用于接收運動事件:

        public boolean onTouchEvent(MotionEvent event)
        public boolean onTrackballEvent(MotionEvent event)

在以上兩個方法中,MotionEvent類作為參數傳入,在這個參數中可以獲得運動事件的各種信息。對于觸摸屏設備的事件,通常使用onTouchEvent()獲取信息,對于軌跡球、鼠標的事件,使用onTrackballEvent()獲取信息。

Activity中的以下兩個方法用于階段觸摸屏和軌跡球的事件:

        public boolean dispatchTouchEvent (MotionEvent ev)
        public boolean dispatchTrackballEvent (MotionEvent ev)

通過dispatchTouchEvent()和dispatchTrackballEvent()兩個方法,可以使得運動事件不再向窗口中傳遞。

1.在活動中響應運動事件

運動事件可以屬于整個窗口,在活動中響應運動事件表示的就是對全窗口的運動事件進行處理。其方法為在Activity中重新實現的onTouchEvent()和onTrackballEvent()方法,接收觸摸屏和軌跡球等事件。

下面是處理簡單的運動事件的處理程序,這個程序在UI的界面中,顯示當前的MotionEvent的動作和位置。觸摸事件程序的運行效果如圖2-5所示。

圖2-5 觸摸事件程序運行效果

Action=0

為ACTION_DOWN,按下動作

Action=1

為ACTION_UP,抬起動作

Action=2

為ACTION_MOVE,移動動作

布局文件testmotionevent.xml中包含了兩個簡單的TextView,分別用于顯示運動事件的坐標和動作。本例程序的Java代碼如下所示:

        import android.app.Activity;
        import android.content.Context;
        import android.os.Bundle;
        import android.view.MotionEvent;
        import android.widget.TextView;
        public class TestMotionEvent extends Activity {
            private static final String TAG = "TestMotionEvent";
            TextView mAction;
            TextView mPostion;
            @Override
            protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.testmotionevent);
              mAction  = (TextView)findViewById(R.id.action);
              mPostion = (TextView)findViewById(R.id.postion);
            }
            @Override
            public boolean onTouchEvent(MotionEvent event) {      // 觸摸屏事件的處理
              int Action = event.getAction();
              float X = event.getX();
              float Y = event.getY();
              mAction.setText("Action = "+ Action);              // 顯示觸摸的動作
              mPostion.setText("Postion = ("+X+","+Y+")");       // 顯示觸摸的位置
              return true;
            }
            @Override
            public boolean onTrackballEvent(MotionEvent event) {  // 軌跡球事件的處理
              int Action = event.getAction();
              float X = event.getX();
              float Y = event.getY();
              mAction.setText("Trackball Action = "+ Action);
              mPostion.setText("Trackball Postion = ("+X+","+Y+")");
              return true;
            }}

對于運行事件中的信息,軌跡球提供的是相對坐標,信息比較單一。對于觸摸屏,有以下幾個注意點:

(1)Activity的onTouchEvent()中獲得的MotionEvent,是相對硬件屏幕的坐標,因此getX()和getRawX()及getY()和getRawY()的含義一般是相同的。

(2)Activity是包含標題欄的,因此對于這種響應方式,觸摸標題欄也可以觸發Activity的觸摸事件。

(3)狀態欄不屬于一個Activity,由于狀態欄的存在,在一個Activity中獲得的最小y值,可能不是0。

2.控件響應運動事件

除了響應整個活動的運動事件,也有專門屬于某一個區域的運動事件的情況。在這種情況下運動事件屬于一個控件。為了實現這種情況,就需要在Vi e w類中進行處理。

其中一種方式是實現View.OnTouchListener()接口,然后將其設置給某個控件,表示由其中的onTouch()方法處理這個控件內的觸摸事件。

以下程序接收一個控件內部的觸摸事件,并將結果顯示到文本框和標題欄當中。程序運行效果如圖2-6所示。

圖2-6 控件內的觸摸屏運行效果

Action=0

為ACTION_DOWN,按下動作

Action=1

為ACTION_UP,抬起動作

Action=2

為ACTION_MOVE,移動動作

本例在布局文件中留出邊緣,白色的區域為一個控件,它并未充滿整個屏幕,當其發生觸摸事件的時候,程序界面中的文本框顯示的是當前觸摸事件的坐標;而標題欄中顯示的是觸摸時間的原始坐標。從中可以看出坐標的原點是控件的左上角,原始坐標的原點是屏幕的左上角。

上述程序實現的核心內容如下所示:

        public class TestMotionEvent1 extends Activity implements View.OnTouchListener{
            private static final String TAG = "TestMotionEvent1";
            private TextView   mAction;
            private TextView   mPosition;
            private View      mView;
            @Override
            protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.testmotionevent1);
              mAction   = (TextView) findViewById(R.id.action);
              mPosition = (TextView) findViewById(R.id.position);
              mView = findViewById(R.id.touchview);
              mView.setOnTouchListener(this);      // 設置監聽器為當前的Activity
            }
            public boolean onTouch(View v, MotionEvent event) { // OnTouchListener的方法
              int action = event.getAction();
              float x = event.getX();               // 獲得坐標
              float y = event.getY();
              float rawx = event.getRawX();         // 獲得原始坐標
              float rawy = event.getRawY();
              Log.v(TAG, "Action = "+ action );    // 獲得原始坐標
              Log.v(TAG, "("+x+","+y+")");
              Log.v(TAG, "("+rawx+","+rawy+")");
              setTitle("A = " + action +  " Raw ["+ rawx +","+ rawy +"]"); // 顯示原始坐標
              mAction.setText("Action = "+ action);
              mPosition.setText( "Position = ("+x+","+y+")");              // 顯示坐標
              return true;
            }
        }

控件僅僅對其區域內的觸摸事件做出響應,因此在上述程序中,只有白色區域的部分能做出響應。一般情況下,對于一個控件,使用getX()和getY()得到相對其左上角的坐標的情況比較多,getRawX()和getRawY()沒有必要使用。

控件響應事件的另外一種方式,就是讓控件自己去實現onTouchEvent()和onTrackballEvent()兩個方法。

以下示例程序用標題欄不同顏色的點表示運動事件的類型,程序的結果如圖2-7所示。

圖2-7 控件自己實現并響應觸摸事件的運行結果(按下、移動、抬起)

當觸摸屏按下、移動、抬起的時候,在坐標處繪制不同顏色的點,在標題欄中顯示當時的動作和坐標。

這里使用的程序的核心內容如下所示:

        public class TestMotionEvent2 extends Activity {
            private static final String TAG = "TestMotionEvent2";
            @Override
            protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(new TestMotionView(this)); // 設置View為Activity的內容
            }
            public class TestMotionView extends View {    // 繼承實現一個新的View
              private Paint   mPaint = new Paint();
              private int    mAction;
              private float  mX;
              private float  mY;
              public TestMotionView(Context c) {
                  super(c);
                  mAction = MotionEvent.ACTION_UP;       // 初始化一些數值
                  mX = 0;
                  mY = 0;
              }
              @Override
              protected void onDraw(Canvas canvas) {
                  Paint paint = mPaint;
                  canvas.drawColor(Color.WHITE);
                  if(MotionEvent.ACTION_MOVE == mAction) {        // 移動動作
                      paint.setColor(Color.RED);
                  }else if(MotionEvent.ACTION_UP == mAction) {    // 抬起動作
                      paint.setColor(Color.GREEN);
                  }else if(MotionEvent.ACTION_DOWN == mAction) {  // 按下動作
                      paint.setColor(Color.BLUE);
                  }
                  canvas.drawCircle(mX, mY,10, paint);           // 繪制一個點
                  setTitle("A = " + mAction +  " ["+ mX +","+ mY +"]");  // 標題欄顯示
              }
              @Override
              public boolean onTouchEvent(MotionEvent event) {
                  mAction = event.getAction();           // 獲得動作
                  mX = event.getX();                      // 獲得坐標
                  mY = event.getY();
                  Log.v(TAG, "Action = "+ mAction );
                  Log.v(TAG, "("+mX+","+mY+")");
                  invalidate();                           // 重新繪制
                  return true;
              }
            }
        }

本程序使用了“自定義控件”的方法,重新實現控件內部onTouchEvent()方法來接收觸摸事件,接收到它,并且記錄發生事件的坐標(x,y)和動作(action)。調用invalidate()重新進行繪制。繪制在onDraw()中完成,根據不同的事件,繪制不同顏色的點,并設置標題欄。

在這個例子中,雖然自定義的Vi e w充滿了窗口,但是依然不包括標題欄的部分。標題欄不在控件范圍內,不會產生這個控件的觸摸事件。

主站蜘蛛池模板: 股票| 三门峡市| 汽车| 万山特区| 奉化市| 淳化县| 宜阳县| 安陆市| 墨脱县| 北票市| 红原县| 宜昌市| 新竹市| 永嘉县| 宁都县| 太和县| 常德市| 四会市| 汝州市| 辰溪县| 石首市| 社旗县| 伊春市| 福清市| 许昌市| 京山县| 讷河市| 台南县| 清镇市| 浦江县| 马关县| 临汾市| 北碚区| 嘉荫县| 邵东县| 阜宁县| 富宁县| 田阳县| 乾安县| 遵义市| 扬州市|