基于監(jiān)聽的事件處理機(jī)制
在前面的章節(jié)我們都是以開發(fā)布局為主,涉及到的邏輯非常少,這樣安排是因?yàn)榫帉?UI 會(huì)更加直觀,寫完即能看到效果,可以增加我們的學(xué)習(xí)興趣,并能夠?qū)?Android 開發(fā)有一個(gè)直觀的感受。在我們?cè)O(shè)計(jì)出精美的 UI 之后,需要讓它服務(wù)于我們的應(yīng)用程序,這就需要有事件處理機(jī)制了,讓各個(gè) View 進(jìn)行操作的時(shí)候它會(huì)執(zhí)行相應(yīng)的邏輯,完成我們給它分配的任務(wù)。
1. 事件處理
事件對(duì)應(yīng)一個(gè)行為,它通常發(fā)生在用戶和App進(jìn)行交互的時(shí)候,比如輸入文字、點(diǎn)擊按鈕、手勢(shì)等等。Android系統(tǒng)將事件處理設(shè)計(jì)成了一種先進(jìn)先出(FIFO)的隊(duì)列形式,所以我們可以按照用戶操作的順序來(lái)依次處理用戶事件
2. 事件監(jiān)聽
在系統(tǒng)發(fā)生了一個(gè)事件之后,我們?nèi)绾谓邮盏竭@個(gè)事件呢?這就需要在事件發(fā)生之前提前向系統(tǒng)注冊(cè)一個(gè)事件監(jiān)聽器,告訴 Android 系統(tǒng)我關(guān)心那些事件,那么系統(tǒng)就會(huì)在事件發(fā)生的相應(yīng)時(shí)間點(diǎn)給你一個(gè)回調(diào)通知,常見(jiàn)的事件監(jiān)聽器有以下幾個(gè):
- OnClickListener:
用來(lái)監(jiān)聽控件的點(diǎn)擊事件,即在用戶點(diǎn)擊某個(gè) View 的時(shí)候回調(diào)此接口。(這也是開發(fā)過(guò)程中最最最常見(jiàn)的接口,一定要牢牢掌握!) - OnLongClickListener:
顧名思義,在 View 被長(zhǎng)按的時(shí)候回調(diào) - OnFocusChangeListener:
當(dāng)控件的焦點(diǎn)發(fā)生變化的時(shí)候回調(diào) - OnKeyListener:
當(dāng)用戶點(diǎn)擊手機(jī)上的按鍵的時(shí)候回調(diào)此接口,通常可以用來(lái)攔截按鍵事件,然后針對(duì)特殊場(chǎng)景做特殊處理 - OnTouchListener:
當(dāng)用戶觸摸屏幕的時(shí)候回調(diào),此接口會(huì)發(fā)生在OnClickListener
回調(diào)的前面,所以我們可以在Touch事件進(jìn)行一些更早期的預(yù)處理事務(wù)。 - OnMenuItemClickListener:
當(dāng)用戶點(diǎn)擊菜單的時(shí)候調(diào)用
以上就是 Android 系統(tǒng)提供的常用事件處理監(jiān)聽器,其中最為常見(jiàn)的就是OnClickListener
,未來(lái)的開發(fā)中會(huì)大量的使用到,所以必須掌握。所以接下來(lái)會(huì)以OnClickListener
為例子來(lái)演示如何完成事件處理,其他的監(jiān)聽器使用方式也都大同小異。
3. 事件處理方式
事件處理要經(jīng)過(guò)以下 4 大步驟:
- 注冊(cè)監(jiān)聽器
- 用戶進(jìn)行相應(yīng)操作,系統(tǒng)將事件入隊(duì)
- 事件經(jīng)過(guò)系統(tǒng)層層分發(fā),最終回調(diào)步驟 1 中注冊(cè)的接口
- 執(zhí)行回調(diào)中的邏輯,完成事件處理
Android 中所有的事件處理都會(huì)經(jīng)過(guò)以上 4 個(gè)步驟,但是具體的處理方式會(huì)有所不同,接下來(lái)介紹一下幾種不同的處理方式,最終達(dá)到的效果是每次點(diǎn)擊 Button 的時(shí)候彈出一個(gè) Toast,如下圖:
3.1 聲明內(nèi)部類
通過(guò)新增內(nèi)部類的形式實(shí)現(xiàn)OnClickListener
接口,代碼如下:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.bottom);
button.setOnClickListener(new EventHandle());
}
private class EventHandle implements View.OnClickListener {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Button被點(diǎn)擊了", Toast.LENGTH_LONG).show();
}
}
}
3.2 匿名內(nèi)部類
匿名內(nèi)部類的寫法會(huì)比較簡(jiǎn)單直接,但是缺點(diǎn)是只能用一次,并且代碼會(huì)集中在方法體內(nèi),如果處理邏輯過(guò)于復(fù)雜會(huì)導(dǎo)致方法代碼冗余。所以通常在只需要使用一次并且內(nèi)部邏輯不太復(fù)雜的時(shí)候使用。
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
// 創(chuàng)建匿名內(nèi)部類綁定點(diǎn)擊監(jiān)聽器
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 在回調(diào)中處理點(diǎn)擊事件
Toast.makeText(MainActivity.this, "Button被點(diǎn)擊了", Toast.LENGTH_LONG).show();
}
});
}
}
3.3 外部類
如果你的事件處理邏輯需要在多個(gè)類中使用,那么以上兩種方式都無(wú)法滿足,這時(shí)候就需要聲明一個(gè)外部類來(lái)實(shí)現(xiàn)OnClickListener
接口了:
package com.emercy.myapplication;
import android.content.Context;
import android.view.View;
import android.widget.Toast;
public class EventHandle implements View.OnClickListener {
Context mContext;
public EventHandle(Context context) {
mContext = context;
}
@Override
public void onClick(View v) {
// 點(diǎn)擊回調(diào)中處理事件
Toast.makeText(mContext.getApplicationContext(), "Button被點(diǎn)擊了", Toast.LENGTH_LONG).show();
}
}
由于需要彈 Toast,所以這里在構(gòu)造器中傳入了一個(gè) Context 對(duì)象,這樣一來(lái) MainActivity 就可以更整潔一些了:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
// 綁定點(diǎn)擊監(jiān)聽器
button.setOnClickListener(new EventHandle(this));
}
}
3.4 Activity 自身實(shí)現(xiàn)接口
我們也可以讓 Activity 去實(shí)現(xiàn) OnClickListener
接口,這樣就可以直接在 Activity 中覆寫 OnClick
方法,將所有的邏輯都封裝在了 Activity 內(nèi)部:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
// 直接綁定Activity即可
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// 在回調(diào)中處理點(diǎn)擊事件
Toast.makeText(MainActivity.this, "Button被點(diǎn)擊了", Toast.LENGTH_LONG).show();
}
}
3.5 通過(guò) xml 標(biāo)簽指定
以上四種本質(zhì)上其實(shí)都是通過(guò)實(shí)現(xiàn)OnClickListener
接口去監(jiān)聽點(diǎn)擊事件的,除此之外還可以在通過(guò)布局文件中添加onClick
標(biāo)簽的方式靜態(tài)綁定點(diǎn)擊事件。這種寫法非常少見(jiàn),在某些場(chǎng)景下可以幫助簡(jiǎn)化很多代碼,但是它不太靈活,大家了解一下即可:
首先在 xml 中找到對(duì)應(yīng)的 <Button/>
標(biāo)簽,然后添加onClick
屬性:
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:onClick="eventHandle"
android:text="點(diǎn)擊事件處理">
</Button>
接著在 Activity 中聲明eventHandle
方法,這樣就不需要手動(dòng)獲取 Button 實(shí)例,也不用綁定點(diǎn)擊事件了。(注意eventHandle的方法簽名必須是固定的)
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// Java代碼中無(wú)需綁定,直接實(shí)現(xiàn)處理函數(shù)即可
public void eventHandle(View v) {
Toast.makeText(getApplicationContext(), "Button被點(diǎn)擊了", Toast.LENGTH_LONG).show();
}
}
4. 總結(jié)
本節(jié)介紹了 Android 的事件處理機(jī)制以及主要常用的集中事件,并以最常用的OnClickListener
為例詳細(xì)講解了集中實(shí)現(xiàn)方式,其他的集中事件幾乎都是照壺畫瓢,大家有興趣的也可以模仿本節(jié)示例自行實(shí)現(xiàn)一下。在本節(jié)之前的內(nèi)容大多是為 UI 布局為主,本節(jié)之后大家將會(huì)見(jiàn)到很多事件相應(yīng)及邏輯控制,只有將 UI 和事件處理和在一起,才能寫出各式各樣的App,你打算寫一個(gè)什么樣的呢?