編輯:關於android開發
前 言
JRedu
Android應用開發中,除了界面編程外,另一個重要的內容就是組件的事件處理。在Android系統中,存在多種界面事件,比如觸摸事件、按鍵事件、點擊事件等。在用戶交互過程中,App必須為用戶提供響應邏輯,這種響應就是通過事件處理完成的。
本章內容將詳細介紹Android事件的具體處理及常見事件。
3.1 Android 事件基礎知識
Android中提供了兩種處理事件的方式:
基於監聽器的事件處理
基於回調的事件處理
在事件處理過程中涉及到三個重要概念,具體如下:
事件:事件封裝了界面組件上發生的事件的具體信息。通過Event對象,可以獲取界面組件事件的相關信息。
事件源:事件產生的來源,通常是指界面上的各種組件,比如文本框、按鈕、窗口、菜單等。
事件監聽器:監聽事件源,並處理事件。
三者之間的關系如圖3-1所示。
(圖 3-1)
3.2 基於監聽器的Android事件處理Android中基於監聽的事件處理,最主要的做法是為界面組件綁定對應的事件監聽器。主要有四種實現方式:
內部類作為事件監聽器
匿名內部類作為事件監聽器
Activity作為監聽器
布局文件中直接綁定
內部類作為事件監聽器有兩個優點,一是在本類中可以復用該監聽器;二是作為內部類可以訪問外部類中的界面組件。下面通過實例講解具體用法。
實例3-1:
程序清單3-1 布局文件<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp" tools:context="com.jredu.event.MainActivity"> <TextView android:id="@+id/show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" android:layout_marginBottom="10dp" /> <Button android:id="@+id/btnOK" android:layout_width="match_parent" //按鈕作為事件源 android:layout_height="wrap_content" android:textSize="16sp" android:text="點擊" /> </LinearLayout>
上面布局文件中含有1個按鈕,該按鈕作為事件源,當點擊該按鈕後將觸發點擊事件。為按鈕綁定事件監聽器的程序如下:
程序清單3-2 內部類作為監聽器public class MainActivity extends AppCompatActivity { TextView show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); show = (TextView)findViewById(R.id.show); Button btn = (Button)findViewById(R.id.btnOK); //獲取布局文件中按鈕 btn.setOnClickListener(new InnerListener()); //為按鈕綁定監聽器 } private class InnerListener implements View.OnClickListener{ //單擊事件的監聽器 @Override public void onClick(View v) { //事件發生時,執行的方法 show.setText("這是內部類作為事件監聽器!"); } } }
程序中定義了一個內部類,該內部類實現了View.OnClickListener,將作為單擊事件的監聽器。實例運行效果如圖3-2。
(圖 3-2)
從該案例可以得出,基於監聽的事件處理編寫步驟如下:
使用匿名內部類作為事件監聽器是目前使用比較廣泛的一種事件監聽器形式。具體案例代碼如下:
程序清單3-3 匿名內部類作為事件監聽器public class AnonymousListenerActivity extends Activity { TextView show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); show = (TextView)findViewById(R.id.show); Button btn = (Button)findViewById(R.id.btnOK); btn.setOnClickListener(new View.OnClickListener() { //匿名類作為監聽器 @Override public void onClick(View v) { show.setText("這是匿名內部類作為事件監聽器!"); //事件處理方法 } }); } }
上面程序通過直接new一個匿名類作為監聽器,該形式應用廣泛,缺點是代碼可讀性差,語法不易掌握。
使用Activity作為監聽器,也是一種常見的事件監聽器形式。這種形式需要Activity實現對應的監聽器接口。具體案例代碼如下:
程序清單3-4 Activity作為事件監聽器public class ActivityAsListener extends Activity implements View.OnClickListener{ //實現單擊監聽器接口 TextView show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_listener); show = (TextView)findViewById(R.id.show); findViewById(R.id.btnOK).setOnClickListener(this); //獲取按鈕,並綁定監聽器 findViewById(R.id.btnCancel).setOnClickListener(this); } @Override public void onClick(View v) { //實現接口中的單擊方法,根據組件ID的進行不同的處理。
switch (v.getId()){ case R.id.btnOK: show.setText("確定:Activity作為事件監聽器!!"); break; case R.id.btnCancel: show.setText("取消:Activity作為事件監聽器!!"); break; default: break; } } }
上面程序使用了Activity作為監聽器,該形式的優點是在同一個事件處理方法中可以處理界面中多個相同的事件;缺點是使程序結構顯得比較混亂,可讀性較差。
最為簡潔的方式就是在布局文件中為標簽直接綁定事件處理方法。在Android大多數標簽都支持onClick屬性,通過該屬性可以為標簽直接綁定事件監聽器。
程序清單3-5 控件綁定布局<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="16dp" tools:context="com.jredu.event.MainActivity"> <TextView android:id="@+id/show" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" android:layout_marginBottom="10dp" /> <Button android:id="@+id/btnOK" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="16sp" android:onClick="clickToShow" //通過onClick屬性綁定事件處理方法clickToShow android:text="確定" /> </LinearLayout>
程序清單3-6 控件綁定程序
public class TagBindActivity extends Activity { TextView show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tag_listener); show = (TextView)findViewById(R.id.show); } public void clickToShow(View v){ //事件處理方法,參數v為事件源 show.setText("通過標簽直接綁定事件監處理方法!!"); } }
View中常見的事件監聽器如下表:
在基於回調函數的事件處理中,UI控件承擔了事件源和事件監聽器雙重職責。當觸發UI控件的事件時,該控件會調用相應的回調函數進行處理事件,程序員所要做事情就是在回調函數中編寫事件處理邏輯。下面以Android的觸摸事件為例,講解基於回調函數的事件處理方式。
通過自定義組件並重寫onTouchEvent方法完成觸摸事件的處理。自定義組件的具體知識可參照第十四章內容。
程序清單3-7 自定義View並重寫onTouchEvent方法public class TouchView extends View { //TouchView繼承自View private int x=0; private int y=0; private Bitmap bitmap; private Paint mPaint; private boolean isPressed =false; public TouchView(Context context) { super(context); init(context); } public TouchView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } private void init(Context context){ bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ship_center); //初始化圖片資源
mPaint = new Paint(); //創建畫筆 mPaint.setAntiAlias(true); //為圖片邊緣去鋸齒 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(bitmap,x,y,mPaint); } private int oX; private int oY; @Override public boolean onTouchEvent(MotionEvent event) { //事件參數,封裝事件相關信息 switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //處理ACTION_DOWN觸摸事件,記錄觸摸點的坐標,並設置按下標識isPressed為true。 oX = (int)event.getX(); oY = (int)event.getY(); isPressed = true; break; case MotionEvent.ACTION_UP: isPressed = false; break; case MotionEvent.ACTION_MOVE: //處理ACTION_MOVE觸摸事件,計算移動的距離,並改變圖片的位置。 if(isPressed) { int mX = (int) event.getX() - oX; int mY = (int) event.getY() - oY; oX = (int) event.getX(); oY = (int) event.getY(); x += mX; y += mY; invalidate(); //通知TouchView進行重繪 } break; } return true; //返回true,表明組件已經處理了該事件。 } }
在上面自定義的TouchView類中,我們重寫了onTouchEvent方法。該方法負責處理自定義組件TouchView的觸摸事件,案例效果如圖3-3.
(圖3-3)
如上面的案例程序,onTouchEvent方法處理了多種事件。當手指觸摸到屏幕、在屏幕上移動或者離開屏幕時,會分別觸發ACTION_DOWN、ACTION_MOVE和ACTION_UP事件。這些事件都由onTouchEvent進行處理。
在View中除了onTouchEvent方法,還提供了用於處理其他事件的回調方法,具體參看表3-2。
通過表3-1,可以發現這些回調方法都有一個boolean類型的返回值,該返回值用於表明該方法有沒有處理完對應的事件。
下面通過案例說明控件的事件傳遞過程。
程序清單3-8 自定義JreduButton並重寫onTouchEvent方法public class JreduButton extends Button { //JreduButton繼承Button private Context mContext; public JreduButton(Context context) { super(context); this.mContext = context; } public JreduButton(Context context, AttributeSet attrs) { super(context, attrs); this.mContext = context; } @Override public boolean onTouchEvent(MotionEvent event) { //重寫onTouchEvent方法 Toast t= Toast.makeText(this.mContext, "Button的onTouchEvent完成觸摸事件的處理!", Toast.LENGTH_LONG); //定義Toast對象,並設置Toast出現的位置 t.setGravity(Gravity.CENTER,0,0); t.show(); return true; //返回值為true,表明該方法處理了觸摸事件,該事件不再傳遞 } }
重寫Activity中的onTouchEvent方法,同樣用於處理觸屏事件,Activity類的代碼如下:
程序清單3-9重寫Activity的onTouchEvent方法public class EventActivity extends AppCompatActivity { TextView show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_event); show = (TextView)findViewById(R.id.show); } @Override public boolean onTouchEvent(MotionEvent event) { //重寫onTouchEvent的方法,在該方法中設置文本顯示內容 show.setText("Activity的onTouchEvent處理了事件!"); return true; } }
運行上面的程序,效果如圖3-4。
(圖3-4)
將上面程序中自定義按鈕JreduButton中的onTouchEvent的返回值改為false。運行程序,效果如圖3-5。
(圖3-5)
基於監聽的事件處理和基於回調的事件處理二者並不是孤立,Android對同一個事件的處理往往會提供這兩種事件處理方式,比如針對觸屏事件、按鍵事件,具體參看下表:
3.4 Android觸屏手勢操作
通過上一節重寫View的onTouchEvent方法或是實現onTouchListener接口可以處理一些簡單的觸屏事件,比如按下、移動、抬起等。如果想要處理一些復雜的手勢,則很難處理。Android提供了GestureDetector類,通過該類可以識別手勢。
下面我們通過一個案例來講解手勢識別類的具體用法,該案例通過手勢在屏幕上滑動可以完成圖片切換。
程序清單3-10 手勢布局文件<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:gravity="center" tools:context="com.jredu.gesture.MainActivity"> <ImageView //用於切換顯示圖片的ImageView android:id="@+id/img" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/jredu01" /> </RelativeLayout>
程序清單3-11 手勢程序
public class MainActivity extends Activity { private GestureDetector detector; private ImageView imageView; private int index = 0; private int[] arr = { R.drawable.jredu01,R.drawable.jredu02, R.drawable.jredu03,R.drawable.jredu04}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); detector = new GestureDetector(this,new SimpleGestureListener()); //創建手勢識別對象 imageView = (ImageView)findViewById(R.id.img); } @Override public boolean onTouchEvent(MotionEvent event) { //將觸屏事件交由手勢識別對象處理 return detector.onTouchEvent(event); } private class SimpleGestureListener extends GestureDetector.SimpleOnGestureListener{ @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if(e2.getX()-e1.getX()>30 && Math.abs(velocityX)>Math.abs(velocityY)){ //水平滑動距離大於30並且X軸速度大Y軸速度時進行圖片切換。 index++; if(index>=arr.length){ index=0; } imageView.setImageResource(arr[index]); } return true; //該事件已被處理。 } } }
在上面程序中定義了一個手勢識別對象,並且該對象設置了一個手勢監聽器SimpleGestureListener。SimpleGesutreListener繼承了GestureDetector.SimpleOnGestureListener並重寫了onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)方法。該方法用於檢測滑屏手勢,方法中共有4個參數,分別為:
該案例顯示效果如圖3-6.
(圖3-6)
想要處理什麼樣的手勢,只需要實現監聽器對應的方法即可。除了上面的onFling方法,手勢監聽器中還包含下面這些方法。
傑瑞教育原創系列教材將於年後與大家正式見面。為更好的借鑒讀者意見,我們將會陸續地在博客園推出一系列教材試讀。我們也熱忱的歡迎廣大博友與我們互動,提出寶貴意見。我們也將為積極互動的博友,免費提供我們的原創教材以及更多福利,也歡迎大家加入下方QQ群與我們交流,謝謝大家!
作者:傑瑞教育
Android自定義控件系列案例【四】 案例效果: 模擬器上運行有些鋸齒,真機上和預期一樣好 案例分析: 看效果,第一直覺肯定是Android原生態控件中沒有這樣的控
android的布局-----TableLayout(表格布局),tablelayout布局學習導圖 (1)TableLayout的相關簡介 java
Android工程打包成jar文件,並且將工程中引用的jar一起打入新的jar文件中,androidjar前言: 關於.jar文件: 平時我們Android項目開發中經常
Android開發框架,android框架總覺得不用框架直接開發比較繁瑣,java下有很多,我使用ssh,php下有很多,我使用ThinkPHP,Delphi下還沒有找到