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