編輯:關於Android編程
我覺得應用程序需要處理最多的就是用戶動作,也就是需要為用戶動作提供響應,這種為用戶動作提供響應的機制就是事件處理。Android提供了兩套事件處理機制:
基於監聽的事件處理:主要做法是為Android界面組件綁定特定的事件監聽器; 1.Event Source(事件源):事件發生的組件;
2.Event(事件):一次用戶的操作;
3.Event Listener(事件監聽器):負責監聽事件源發生的事件,並對各事件做出相應的響應;
(注:我舉個例子來說明一下:當用戶單擊按鈕或單擊一個菜單項時,這些動作就會激發一個相應的事件,該事件就會觸發事件源上已注冊的事件監聽器,事件監聽器調用相應的事件處理器來做出相應的響應。)
1、事件源最容易創建,任意界面組件都可以作為事件源;
2、事件的產生無需程序員關心,它是由系統自動產生;
3、實現事件監聽器是整個事件處理的核心;
第一種:內部類形式作為事件監聽器
【實例】當單擊按鈕時,文本框內容改變
布局文件代碼如下:
Java文件代碼如下:
public class Event_Qs extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button button = (Button) findViewById(R.id.btn1);
button.setOnClickListener(new MyClickListener());
}
class MyClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
TextView textView = (TextView) findViewById(R.id.tv1);
textView.setText("按鈕被單擊了");
}
}
}
第二種:匿名內部類形式作為事件監聽器
大部分時候,事件處理器都沒有什麼復用價值,(可復用代碼通常都被抽象成了業務邏輯方法),因此大部分事件監聽器知識臨時使用一次,所以使用匿名內部類形式的事件監聽器更合適。實際上這種形式是目前使用最廣泛的事件監聽器。
public class Event_Qs extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button button = (Button) findViewById(R.id.btn1);
button.setOnClickListener(new View.OnClickListener() {
//實現事件處理方法
@Override
public void onClick(View v) {
TextView textView = (TextView) findViewById(R.id.tv1);
textView.setText("按鈕被單擊了");
}
});
}
}
第三種:Activity本身作為事件監聽器
這種形式使用Activity本身作為監聽器類,可以直接在Activity類中定義時間處理器方法。這種形式非常簡潔,但使用過程一定要注意程序結構。
public class Event_Qs extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button btn1 = (Button) findViewById(R.id.btn1);
Button btn2 = (Button) findViewById(R.id.btn2);
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
TextView textView = (TextView) findViewById(R.id.tv1);
switch (v.getId()){
case R.id.btn1:
textView.setText("按鈕1被單擊了");
break;
case R.id.btn2:
textView.setText("按鈕2被單擊了");
break;
}
}
}
第四種:直接綁定到標簽作為事件監聽器
Android還有一種更簡潔的綁定事件監聽器方式,那就是直接在界面布局文件為指定標簽綁定事件處理方法。
以下是布局文件代碼:
java文件代碼如下:
public class Event_Qs extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button btn1 = (Button) findViewById(R.id.btn1);
}
//定義了一個事件處理方法
public void btnclick(View source) {
TextView textView = (TextView) findViewById(R.id.tv1);
textView.setText("按鈕被單擊了");
}
}
上面程序中定義了一個btnclick(View source)方法,並在xml文件的按鈕中添加了android:onClick="btnclick"的屬性,當用戶單擊按鈕時,該方法就會被激發並處理btn1按鈕的單擊事件。
正如文章開頭所說基於回調的事件處理的主要做法是重寫Android組件特定的回調方法或者重寫Activity的回調方法,那麼相對於基於監聽事件處理來說,事件源與事件監聽器就合為一體。或者說,事件監聽器就消失了。
為了實現回調機制的事件處理,Android為所有GUI組件都提供了一些事件處理的回調方法,以View為例,該類包含如下方法。
1、boolean onKeyDown(int,KeyEvent):當按下某組件時觸發;
2、boolean onKeyLongPress(int,KeyEvent):當長按某組件時觸發;
3、boolean onKeyShortcut(int,KeyEvent):當鍵盤快捷鍵事件發生時觸發;
4、boolean onKeyUp(int,KeyEvent):當組件上松開某個按鍵時觸發;
5、boolean onKeyTouchEvent(event):當用戶在組件上觸摸時觸發;
6、boolean onTrackball(event):當用戶在組件上觸發軌跡球事件時觸發;
【實例】通過自定義View來實現基於回調的事件處理機制,自定義View時重寫該View的事件處理方法。
public class MyButton extends Button {
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
Log.v("songsong.com.eventtest", "The onkeydown in MyButton" );
return true;
}
}
在上面自定義了MyButton類中,重寫Button類的onKeyDown(int keyCode, KeyEvent event)方法。
運行上面程序,先把焦點定位到該按鈕上,接著單擊模擬器上任意按鈕就可以看到效果:

小結:
幾乎所有基於回調的時間處理方法都有一個boolean類型的返回值,該返回值用於標識該處理方法是否能完全處理該事件:
如果處理事件的回調方法返回是true,表明該處理方法已經完成處理該事件,不會繼續傳播。如果處理事件的回調方法返回是false,表明處理方法並未完全處理該事件,會繼續傳播。【實例】分別在Activity中重寫onKeyDown和Button類中重寫onKeyDown,最後為按鈕添加OnKeyListener處理OnKeyDown事件,返回值皆為false.
public class MyButton extends Button {
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
Log.v("songsong.com.eventtest", "The onkeydown in MyButton");
return false;
}
}
上面Mybutton子類重寫了OnKeyDown,由於返回了false,事件還將繼續傳播。
public class Event_Qs extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_qs);
Button btn1 = (Button) findViewById(R.id.btn1);
btn1.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
//只處理按下鍵的事件
if (event.getAction() == KeyEvent.ACTION_DOWN)
Log.v("songsong.com.eventtest", "The onkeydown in OnKeyListener");
return false;//繼續外傳
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
Log.v("songsong.com.eventtest", "The onkeydown in Activity");
return false;
}
上面Activity中重寫了OnKeyDown,為按鈕添加OnKeyListener處理OnKeyDown事件,返回值皆為false。
當事件在傳播的過程,最先觸發的是按鈕上綁定的事件監聽器,然後才觸發組件提供的事件回調方法,最後才會傳播到該組件所在的Activity。當然,如果返回值是true,那麼就沒然後了。
Configuration類專門用於描述手機設備上的配置信息,這些配置信息既包括用戶特定的配置項,也包括系統的動態設置配置。
Configuration cf = getResources().getConfiguration();
當獲取完Configuration對象就可以調用該對象提供的屬性來取得系統配置信息:
1、public int KeyboardHidden:該屬性返回布爾值來標識鍵盤是否可用,如果軟鍵盤可用 KEYBOARDHIDDEN_NO,硬軟鍵盤都不可用就YES;
2、public int mcc:獲取移動信號的國家碼;
3、public int mnc:獲取移動信號的網絡碼;
4、public int navigation:判斷系統上方向導航設備的類型;
5、public intorientation:獲取系統屏幕的方向,ORIENTATION_LANDSCAPE (橫向),ORIENTATION_PORTRAIT(豎向);
6、public inttouchscreen:獲取系統觸摸屏的觸摸方式;
【實例】獲取系統設備狀態
首先定義4個文本框來顯示系統信息,通過一個按鈕觸發。布局文件代碼因為太簡單以至於不貼出來了:
MainActivity.java代碼內容:
public class ConfigurationTest extends Activity {
TextView ori, nav, touch, mnc;
Button btncg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_configuration);
ori = (TextView) findViewById(R.id.ori); //屏幕方向
nav = (TextView) findViewById(R.id.nav); //方向導航設備類型
touch = (TextView) findViewById(R.id.touch); //觸摸方式
mnc = (TextView) findViewById(R.id.mnc); //移動信號的網絡碼
btncg = (Button) findViewById(R.id.btncg);
btncg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Configuration cf = getResources().getConfiguration();
String ori_str = cf.orientation == Configuration.ORIENTATION_LANDSCAPE ? "橫向屏幕" : "縱向屏幕";
String mnc_str = cf.mnc + "";
String nav_str = cf.navigation == Configuration.NAVIGATION_NONAV ? "沒有方向控制" :
cf.navigation == Configuration.NAVIGATION_WHEEL ? "滾輪控制方向" :
cf.navigation == Configuration.NAVIGATION_DPAD ? "方向鍵控制方向" : "軌跡球控制方向";
String touch_str = cf.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH ? "無觸摸屏" : "支持觸摸屏";
ori.setText(ori_str);
nav.setText(nav_str);
touch.setText(touch_str);
mnc.setText(mnc_str);
}
});
}
}
效果如下:
如果想監聽系統設置的更改,就可以重寫Activity的onConfigurationChanged()方法,該方法是一個基於回調的事件處理方法:當系統設置發送更改時,該方法會被自動觸發。
【實例】動態更改屏幕方向
布局文件中只有一個按鈕,通過對單擊按鈕事件動態修改系統屏幕的方向。
public class Changecfg extends Activity {
Button btnchange;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_changecfg);
btnchange = (Button) findViewById(R.id.btnchange);
btnchange.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Configuration cfg = getResources().getConfiguration();
if (cfg.orientation == Configuration.ORIENTATION_LANDSCAPE) { //如果當前是橫屏的話
//設置為豎屏
Changecfg.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
if (cfg.orientation == Configuration.ORIENTATION_PORTRAIT) { //如果當前是豎屏的話
//設置為橫屏
Changecfg.this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
}
});
}
@Override
public void onConfigurationChanged(Configuration newConfig) { //用於監聽系統設置的更改
super.onConfigurationChanged(newConfig);
String screen = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE ? "橫屏" : "豎屏";
Toast.makeText(this, "當前屏幕方向為:" + screen, Toast.LENGTH_SHORT).show();
}
}
在運行之前還要在配置Activity時加上android:configChanges="orientation|screenSize",為了就是要監聽屏幕方向改變的事件。
效果:
出於性能優化的考慮,Android制定了一條簡單的規則:只允許UI線程修改Activity裡的UI組件。這樣的話就會導致新啟動的線程無法動態改變界面組件的屬性值,但在實際開發中,尤其是涉及動畫的游戲開發中,想要新啟動的線程周期性的改變界面組件的屬性值就要借助Handler的消息傳遞機制實現了。
根據書上所說,Handler類的主要作用有兩個:
在新啟動的線程中發送消息;
那麼我們要解決的問題就是:
新啟動的線程何時發送消息?主線程何時去獲取並處理消息?
顯然,我們這章節的內容是事件處理,那麼我們通過回調的方式來實現。開發者只需重寫Handler類中處理消息的方法,當新啟動的線程發送消息時,消息會發送到與之關聯的MessageQueue,而Handler會不斷地從MessageQueue中獲取並處理消息。
Handler類包含如下方法用於發送、處理消息:【實例】自動播放圖片
通過一個新的線程來周期性地修改ImageVIew所顯示的圖片,布局文件中只定義了ImageVIew組件。
public class HandlerTest extends Activity {
int[] imagesID = new int[]{
R.drawable.a1,
R.drawable.a2,
R.drawable.a3,
R.drawable.a4,
};
int currentImageID = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
final ImageView show = (ImageView) findViewById(R.id.iv1);
final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x1233) {
show.setImageResource(imagesID[currentImageID++ % imagesID.length]); //動態修改所顯示的圖片
}
}
};
new Timer().schedule(new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(0x1233); //發送空消息
}
}, 0, 1200);
}
}
上面程序中的代碼通過Timer周期性地執行指定任務。由於Android 不允許在新線程中訪問Activity裡的界面組件,因此程序只能在新的線程裡發送一條消息,通知系統更新ImageView組件。

為了更好理解Handler的工作原理,先了解與Handler一起工作的幾個組件:
Looper:每個線程只有一個Looper,它負責管理MessageQueue,會不斷的從MessageQueue中取出消息,並將消息分給對應的Handler處理。以下是工作流程圖:

在線程中使用Handler的步驟如下:
調用Looper的prepare()方法為當前線程創建Looper對象,創建Looper對象時,它的構造器會自動創建配套的MessageQueue。有了Looper之後,創建Handler子類的實例,重寫handleMessage()方法。調用Looper的loop()方法啟動Looper。【實例】在線程中計算質數
為了防止在UI線程中執行一個耗時的操作,而導致UI線程被阻塞,令應用程序失去響應,讓新線程來計算該數值范圍內的所有質數。
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class Handler_Looper extends Activity {
static final String UPPER_NUM = "upper";
EditText etNum;
CalThread calThread;
// 定義一個線程類
class CalThread extends Thread {
public Handler myhandler;
public void run() {
Looper.prepare(); //創建Looper對象
myhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x123) {
int upper = msg.getData().getInt(UPPER_NUM);
List nums = new ArrayList();
outer:
for (int i = 2; i <= upper; i++) {
for (int j = 2; j <= Math.sqrt(i); j++) {
if (i != 2 && i % j == 0) {
continue outer;
}
}
nums.add(i);
}
Toast.makeText(Handler_Looper.this, nums.toString(), Toast.LENGTH_LONG).show();
}
}
};
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler__looper);
etNum = (EditText) findViewById(R.id.et1);
calThread = new CalThread();
calThread.start();
}
public void cal(View v) {
Message msg = new Message();
msg.what = 0x123;
Bundle bundle = new Bundle();
bundle.putInt(UPPER_NUM,Integer.parseInt(etNum.getText().toString()));
msg.setData(bundle);
calThread.myhandler.sendMessage(msg);
}
}

Android WebView使用(一)
前言Android內置webkit內核的高性能浏覽器,而WebView則是在這個基礎上進行封裝後的一個 控件,WebView直譯網頁視圖,我們可以簡單的看作一個可以嵌套到
android 中的日歷控件
android 中的 日歷控件 public class MainActivity extends Activ
Android 藍牙開發全面總結
基本概念:安卓平台提供對藍牙的通訊棧的支持,允許設別和其他的設備進行無線傳輸數據。應用程序層通過安卓API來調用藍牙的相關功能,這些API使程序無線連接到藍牙設備,並擁有
魅族pro5手機怎麼截屏截圖教程
魅族pro5怎麼截屏?很多初次使用魅族pro5的用戶,還不知道該如何截圖,魅族pro5是有多鐘截屏方法,快捷組合鍵截圖。也可以借用第三方軟件進行截圖:借助第