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

2.3 設(shè)備事件的響應(yīng)

在應(yīng)用程序的控制方面,在大多數(shù)場(chǎng)合下使用的是屏幕上的控件,控件本身也是包含了事件處理的載體。

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

2.3.1 鍵盤事件的響應(yīng)

鍵盤是Android中的主要輸入設(shè)備,對(duì)按鍵事件的處理是在程序中使用鍵盤的核心內(nèi)容。

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

        final int  getKeyCode()         // 獲得按鍵碼
        final int  getAction()          // 獲得按鍵的動(dòng)作
        final int  getFlags()           // 獲得標(biāo)志
        final int  getRepeatCount()     // 獲得重復(fù)的信息
        final int  getScanCode()        // 獲得掃描碼

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

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

        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接口中的幾個(gè)方法,均具有整型和KeyEvent類型兩個(gè)參數(shù),KeyEvent類型中實(shí)際上包含整型的信息。

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

View和Activity都已經(jīng)實(shí)現(xiàn)KeyEvent.Callback接口。在這兩個(gè)類的繼承者中,可以通過重新實(shí)現(xiàn)以上的幾個(gè)方法來響應(yīng)按鍵的事件。

通常情況下,按鍵事件屬于整個(gè)屏幕,因此在Activity中響應(yīng)即可。按照一般的邏輯,當(dāng)鍵盤上一個(gè)按鍵被按下的時(shí)候,不會(huì)去區(qū)分這個(gè)按鍵屬于哪一個(gè)控件。

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

        public boolean dispatchKeyEvent (KeyEvent event)

這個(gè)方法將在按鍵到達(dá)窗口之前被調(diào)用,因此通過這個(gè)方法可以阻止系統(tǒng)的其他部分不獲得按鍵事件。

以下的示例需要實(shí)現(xiàn)的內(nèi)容是通過鍵盤來控制一個(gè)圖片的Alpha值,使用上鍵和右鍵增加圖片的Alpha值,使用下鍵和左鍵減少圖片的Alpha值。

這個(gè)按鍵事件響應(yīng)程序的運(yùn)行效果如圖2-4所示。

圖2-4 按鍵事件響應(yīng)程序運(yùn)行效果(左:初始化;中:中間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>

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

本例的源代碼的核心部分實(shí)現(xiàn)如下所示:

        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);                      // 設(shè)置初始化的透明數(shù)值
              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數(shù)值的范圍
              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()方法,其參數(shù)int keyCode為按鍵碼,KeyEvent msg表示按鍵事件的消息(其中包含了更詳細(xì)的內(nèi)容)。

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

本程序打出的一段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可以獲得重復(fù)按鍵的次數(shù)和這個(gè)歷史過程相關(guān)的數(shù)據(jù)信息,在一個(gè)按鍵的響應(yīng)過程中,通常是先發(fā)送一個(gè)或若干個(gè)Down事件,最后是一個(gè)Up事件。

按鍵事件的響應(yīng)有以下幾個(gè)需要注意的細(xì)節(jié):

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

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

(3)在按鍵處理方法的最后,調(diào)用super.onKeyDown()和super.onKeyUp()表示由父類進(jìn)行默認(rèn)的按鍵處理。

2.3.2 運(yùn)動(dòng)事件的處理

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

對(duì)于觸摸屏,一般通過絕對(duì)坐標(biāo)描述它的事件;對(duì)于軌跡球,一般通過相對(duì)坐標(biāo)描述它的事件。在Android系統(tǒng)中,鼠標(biāo)設(shè)備的處理方式類似于軌跡球。

MotionEvent是android.view包中的類,用于表示運(yùn)動(dòng)事件的坐標(biāo)、動(dòng)作等信息。MotionEvent中主要的幾個(gè)方法如下所示:

        public final float  getX()                   // 獲得X坐標(biāo)
        public final float  getY()                   // 獲得Y坐標(biāo)
        public final int  getAction()                // 獲得動(dòng)作
        public final float getRawX()                 // 獲得原始的X坐標(biāo)
        public final float getRawY()                 // 獲得原始的Y坐標(biāo)

對(duì)于這種運(yùn)動(dòng)事件,最重要的屬性為運(yùn)動(dòng)事件發(fā)生的坐標(biāo)(X,Y),運(yùn)動(dòng)的動(dòng)作表示按下、按住移動(dòng)、抬起等信息。getRawX()和getRawY()表示沒有根據(jù)窗口或者控件做出調(diào)整的原始坐標(biāo)。

運(yùn)動(dòng)事件有其歷史屬性,MotionEvent中相關(guān)的方法如下所示:

        public final int  getHistorySize()           // 獲得歷史的點(diǎn)數(shù)
        public final float  getHistoricalX(int pos)  // 獲得歷史的X坐標(biāo)
        public final float  getHistoricalY(int pos)  // 獲得歷史的Y坐標(biāo)

在Android API級(jí)別5版本之后,MotionEvent類中還包含了多點(diǎn)觸摸的相關(guān)內(nèi)容,當(dāng)有多個(gè)觸點(diǎn)同時(shí)起作用的時(shí)候,可以獲得觸點(diǎn)的數(shù)目和每一個(gè)觸點(diǎn)的坐標(biāo)。

        public final int  getPointerCount()          // 獲得觸點(diǎn)的數(shù)目
        public final float  getX(int pointerIndex)   // 獲得某個(gè)觸點(diǎn)的X坐標(biāo)
        public final float  getY(int pointerIndex)   // 獲得某個(gè)觸點(diǎn)的Y坐標(biāo)

MotionEvent可以獲得多點(diǎn)的基礎(chǔ)信息,而沒有包含手勢(shì)解釋的功能。

在Activity和View中,都具有以下兩個(gè)方法,用于接收運(yùn)動(dòng)事件:

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

在以上兩個(gè)方法中,MotionEvent類作為參數(shù)傳入,在這個(gè)參數(shù)中可以獲得運(yùn)動(dòng)事件的各種信息。對(duì)于觸摸屏設(shè)備的事件,通常使用onTouchEvent()獲取信息,對(duì)于軌跡球、鼠標(biāo)的事件,使用onTrackballEvent()獲取信息。

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

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

通過dispatchTouchEvent()和dispatchTrackballEvent()兩個(gè)方法,可以使得運(yùn)動(dòng)事件不再向窗口中傳遞。

1.在活動(dòng)中響應(yīng)運(yùn)動(dòng)事件

運(yùn)動(dòng)事件可以屬于整個(gè)窗口,在活動(dòng)中響應(yīng)運(yùn)動(dòng)事件表示的就是對(duì)全窗口的運(yùn)動(dòng)事件進(jìn)行處理。其方法為在Activity中重新實(shí)現(xiàn)的onTouchEvent()和onTrackballEvent()方法,接收觸摸屏和軌跡球等事件。

下面是處理簡(jiǎn)單的運(yùn)動(dòng)事件的處理程序,這個(gè)程序在UI的界面中,顯示當(dāng)前的MotionEvent的動(dòng)作和位置。觸摸事件程序的運(yùn)行效果如圖2-5所示。

圖2-5 觸摸事件程序運(yùn)行效果

Action=0

為ACTION_DOWN,按下動(dòng)作

Action=1

為ACTION_UP,抬起動(dòng)作

Action=2

為ACTION_MOVE,移動(dòng)動(dòng)作

布局文件testmotionevent.xml中包含了兩個(gè)簡(jiǎn)單的TextView,分別用于顯示運(yùn)動(dòng)事件的坐標(biāo)和動(dòng)作。本例程序的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);              // 顯示觸摸的動(dòng)作
              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;
            }}

對(duì)于運(yùn)行事件中的信息,軌跡球提供的是相對(duì)坐標(biāo),信息比較單一。對(duì)于觸摸屏,有以下幾個(gè)注意點(diǎn):

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

(2)Activity是包含標(biāo)題欄的,因此對(duì)于這種響應(yīng)方式,觸摸標(biāo)題欄也可以觸發(fā)Activity的觸摸事件。

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

2.控件響應(yīng)運(yùn)動(dòng)事件

除了響應(yīng)整個(gè)活動(dòng)的運(yùn)動(dòng)事件,也有專門屬于某一個(gè)區(qū)域的運(yùn)動(dòng)事件的情況。在這種情況下運(yùn)動(dòng)事件屬于一個(gè)控件。為了實(shí)現(xiàn)這種情況,就需要在Vi e w類中進(jìn)行處理。

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

以下程序接收一個(gè)控件內(nèi)部的觸摸事件,并將結(jié)果顯示到文本框和標(biāo)題欄當(dāng)中。程序運(yùn)行效果如圖2-6所示。

圖2-6 控件內(nèi)的觸摸屏運(yùn)行效果

Action=0

為ACTION_DOWN,按下動(dòng)作

Action=1

為ACTION_UP,抬起動(dòng)作

Action=2

為ACTION_MOVE,移動(dòng)動(dòng)作

本例在布局文件中留出邊緣,白色的區(qū)域?yàn)橐粋€(gè)控件,它并未充滿整個(gè)屏幕,當(dāng)其發(fā)生觸摸事件的時(shí)候,程序界面中的文本框顯示的是當(dāng)前觸摸事件的坐標(biāo);而標(biāo)題欄中顯示的是觸摸時(shí)間的原始坐標(biāo)。從中可以看出坐標(biāo)的原點(diǎn)是控件的左上角,原始坐標(biāo)的原點(diǎn)是屏幕的左上角。

上述程序?qū)崿F(xiàn)的核心內(nèi)容如下所示:

        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);      // 設(shè)置監(jiān)聽器為當(dāng)前的Activity
            }
            public boolean onTouch(View v, MotionEvent event) { // OnTouchListener的方法
              int action = event.getAction();
              float x = event.getX();               // 獲得坐標(biāo)
              float y = event.getY();
              float rawx = event.getRawX();         // 獲得原始坐標(biāo)
              float rawy = event.getRawY();
              Log.v(TAG, "Action = "+ action );    // 獲得原始坐標(biāo)
              Log.v(TAG, "("+x+","+y+")");
              Log.v(TAG, "("+rawx+","+rawy+")");
              setTitle("A = " + action +  " Raw ["+ rawx +","+ rawy +"]"); // 顯示原始坐標(biāo)
              mAction.setText("Action = "+ action);
              mPosition.setText( "Position = ("+x+","+y+")");              // 顯示坐標(biāo)
              return true;
            }
        }

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

控件響應(yīng)事件的另外一種方式,就是讓控件自己去實(shí)現(xiàn)onTouchEvent()和onTrackballEvent()兩個(gè)方法。

以下示例程序用標(biāo)題欄不同顏色的點(diǎn)表示運(yùn)動(dòng)事件的類型,程序的結(jié)果如圖2-7所示。

圖2-7 控件自己實(shí)現(xiàn)并響應(yīng)觸摸事件的運(yùn)行結(jié)果(按下、移動(dòng)、抬起)

當(dāng)觸摸屏按下、移動(dòng)、抬起的時(shí)候,在坐標(biāo)處繪制不同顏色的點(diǎn),在標(biāo)題欄中顯示當(dāng)時(shí)的動(dòng)作和坐標(biāo)。

這里使用的程序的核心內(nèi)容如下所示:

        public class TestMotionEvent2 extends Activity {
            private static final String TAG = "TestMotionEvent2";
            @Override
            protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(new TestMotionView(this)); // 設(shè)置View為Activity的內(nèi)容
            }
            public class TestMotionView extends View {    // 繼承實(shí)現(xiàn)一個(gè)新的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;       // 初始化一些數(shù)值
                  mX = 0;
                  mY = 0;
              }
              @Override
              protected void onDraw(Canvas canvas) {
                  Paint paint = mPaint;
                  canvas.drawColor(Color.WHITE);
                  if(MotionEvent.ACTION_MOVE == mAction) {        // 移動(dòng)動(dòng)作
                      paint.setColor(Color.RED);
                  }else if(MotionEvent.ACTION_UP == mAction) {    // 抬起動(dòng)作
                      paint.setColor(Color.GREEN);
                  }else if(MotionEvent.ACTION_DOWN == mAction) {  // 按下動(dòng)作
                      paint.setColor(Color.BLUE);
                  }
                  canvas.drawCircle(mX, mY,10, paint);           // 繪制一個(gè)點(diǎn)
                  setTitle("A = " + mAction +  " ["+ mX +","+ mY +"]");  // 標(biāo)題欄顯示
              }
              @Override
              public boolean onTouchEvent(MotionEvent event) {
                  mAction = event.getAction();           // 獲得動(dòng)作
                  mX = event.getX();                      // 獲得坐標(biāo)
                  mY = event.getY();
                  Log.v(TAG, "Action = "+ mAction );
                  Log.v(TAG, "("+mX+","+mY+")");
                  invalidate();                           // 重新繪制
                  return true;
              }
            }
        }

本程序使用了“自定義控件”的方法,重新實(shí)現(xiàn)控件內(nèi)部onTouchEvent()方法來接收觸摸事件,接收到它,并且記錄發(fā)生事件的坐標(biāo)(x,y)和動(dòng)作(action)。調(diào)用invalidate()重新進(jìn)行繪制。繪制在onDraw()中完成,根據(jù)不同的事件,繪制不同顏色的點(diǎn),并設(shè)置標(biāo)題欄。

在這個(gè)例子中,雖然自定義的Vi e w充滿了窗口,但是依然不包括標(biāo)題欄的部分。標(biāo)題欄不在控件范圍內(nèi),不會(huì)產(chǎn)生這個(gè)控件的觸摸事件。

主站蜘蛛池模板: 措勤县| 武平县| 蒙自县| 鹿泉市| 股票| 阿克苏市| 台湾省| 耿马| 东安县| 松阳县| 名山县| 鹿邑县| 浙江省| 洱源县| 微博| 凤阳县| 隆安县| 石泉县| 临泽县| 望谟县| 鄱阳县| 同江市| 敦化市| 称多县| 樟树市| 正安县| 来宾市| 长汀县| 合作市| 东阿县| 龙口市| 南召县| 收藏| 苏尼特左旗| 临潭县| 喀什市| 鸡泽县| 莫力| 东乌珠穆沁旗| 万山特区| 赤壁市|