在說明畫面元件時, 可以在xml裏註明android:onClick=”bt_click” 然後再於Java程式碼中撰寫public void bt_click(View v){}的方法. 此時使用者就可以經由按一下畫面元件, 執行所需的工作
此種作法, 可以應付簡易的互動工作, 也很常用. 但當按下實体按鍵時, 或想要使用其他的事件時, 就需使用Listener的面介
Listener介面
在android.view 及android.widget中, 宣告了基本的監聽介面(Listener), 常見的有如下
View.OnClickListener : 點一下事件
View.OnLongClickListener : 長按事件
View.OnKeyListener : 實体按鍵事件
View.OnTouchListener : 螢幕觸控事件
使用Listener介面的畫面元件, 都需有自己的名稱 : android:id=”@+id/名稱”
<Button
android:id="@+id/btOk"
android:text="確定"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
再來就是實作監聽介面, 將要執行的任務寫在裏面, 然後在畫面元件中註冊要監聽的介面
為了讓剛接觸Android的人有著全盤的了解, 也複習Java的類別機制, 所以分三部份說明.
第一種為內部類別, 第二種為匿名類別, 第三種為簡化匿名類別
內部類別
監聽介面, 通常是針對畫面元件設定的, 所以大都把監聽介面設計成內部類別, 並加上private, 讓外界無法存取
public class MainActivity extends AppCompatActivity {
private class MyListener implements View.OnClickListener{
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "OK button was clicked",
Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn=(Button)findViewById(R.id.btOk);
MyListener listener=new MyListener();
btn.setOnClickListener(listener);
}
}
上述紅色標示的, 即是內部類別的宣告.
棕色處使用findViewById(R.id.btOk)取得按鈕實体‧
藍色處產生監聽物件(listener), 再使用按鈕的setOnClickListener()註冊listener.
匿名類別
內部類別還要宣告class, 實在是麻煩, 因此可以使用匿名類別來簡化
public class MainActivity extends AppCompatActivity {
View.OnClickListener listener= new View.OnClickListener() {
@Override
public void onClick(View view) {
switch(view.getId()){
case R.id.btOk:
Toast.makeText(MainActivity.this, "OK button was clicked",
Toast.LENGTH_SHORT).show();
break;
case R.id.btCancel:
Toast.makeText(MainActivity.this, "Cancel button was clicked",
Toast.LENGTH_SHORT).show();
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnOk=(Button)findViewById(R.id.btOk);
Button btnCancel=(Button)findViewById(R.id.btCancel);
btnOk.setOnClickListener(listener);
btnCancel.setOnClickListener(listener);
}
}
上述紅色處使用匿名類別直接產生listener物件, 然後藍色處直接註冊
簡化匿名類別
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnOk=(Button)findViewById(R.id.btOk);
btnOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "OK button was clicked",
Toast.LENGTH_SHORT).show();
}
});
}
}
簡化程式架構
onCreate()常需要大量初始化元件, 實作及註冊監控介面, 造成方法內的程式碼雜亂不易除錯, 所以可以使用如下架構簡化
public class MainActivity extends AppCompatActivity {
private Button btnOk, btnCancel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
processViews();
processControllers();
}
private void processViews(){
btnOk=(Button)findViewById(R.id.btOk);
btnCancel=(Button)findViewById(R.id.btCancel);
}
private void processControllers(){
btnOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this, "OK button was clicked", Toast.LENGTH_SHORT).show();
}
});
}
}
在processViews方法中取得畫面元件物件
在processControllers方法中實作監控介面及註冊
OnFocusChangeListener
當畫面元件焦點改變後所引發的事件. 請注意, 得到焦點時會發生此事件, 而離開時也會, 可用第二個參數boolean值判斷是進入或者是離開
public class MainActivity extends AppCompatActivity {
private EditText edit1, edit2;
private TextView txt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
processViews();
processControllers();
}
private void processViews(){
edit1=(EditText)findViewById(R.id.edit1);
edit2=(EditText)findViewById(R.id.edit2);
txt=(TextView)findViewById(R.id.txt);
}
private void processControllers(){
View.OnFocusChangeListener focus=new View.OnFocusChangeListener(){
@Override
public void onFocusChange(View view, boolean b) {
txt.setText(b?"Edit1":"Edit2");
}
};
edit1.setOnFocusChangeListener(focus);
}
}
OnTouchListener
畫面元件的OnTouchListener, 需覆蓋onTouch(View, MotionEvent)方法. 觸發方式為在元件上點一下不放, 然後移動, 最後再放開, 就像在模擬滑鼠按下不放然後移動的過程
onTouch的第二參數為MotionEvent參考型態, 此型態可用getAction()方法取得事件, 有Down, Up, Move等較為常用, 也可用getX(), getY()取得目前位置
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn=(Button)findViewById(R.id.btn);
btn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d("Thomas", "Down");
break;
case MotionEvent.ACTION_UP:
Log.d("Thomas", "Up");
break;
case MotionEvent.ACTION_MOVE:
Log.d("Thomas", event.getX()+":"+event.getY());
}
return false;
}
});
}
}
OnCheckedChangeListener
CheckBox, ToggleButton, Switch, RadioButton可以使用
CompoundButton.OnCheckedChangeListener
而RadioGroup則使用RadioGroup.OnCheckedChangeListener
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RadioGroup group=(RadioGroup)findViewById(R.id.group);
group.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup radioGroup, int id) {
RadioButton rb=(RadioButton)findViewById(id);
Toast.makeText(MainActivity.this, rb.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
在RadioGroup 的onCheckedChanged中, 第二參數為被按下的RadioButton id, 所以可由此id找到是那一個RadioButton被按下.
Activity元件事件
上述說明的都是畫面元件的事件. 而Activity活動元件也有自己的事件
onBackPressed
當返回鍵被按下時, 應用程式會被結束. 若要在按下返回鍵時提醒使用者確定要離開嗎, 則可以在此彈出對話方塊詢問
public class MainActivity extends AppCompatActivity {
@Override
public void onBackPressed() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
dialog.setMessage("確定要離開嗎");
dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener(){
@Override
public void onClick(DialogInterface dialogInterface, int i) {
MainActivity.super.onBackPressed();
}
});
dialog.setNegativeButton("No",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface arg0, int arg1) {}
});
dialog.show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
onKeyDown
只有返回鍵被按下時才會觸發, 所以是否結束的對話方塊也可以寫在這裏
onTouchEvent
當Activity視窗範圍內有發生觸控螢幕被按, 就會執行, 同樣包含了Down, Up, Movie等事件.
請特別注意一般網站及書本錯誤的資訊
在Activity內若有畫面元件, 比如button1, 註冊OnTouchListener, 而且在onTouch()的返回值果為若為true, 則表示onTouch()被消耗掉, 就不會再傳給button1的其他事件. 若返回結果是false, 則表示沒被消耗掉, 會被button1的其他事件再度處理.
而一般網站及書本總是誤寫成 :
返回為false, 則會繼續報給其他的物件, 比如Activity也能接收此事件, 這是大錯特錯的.
如下的程式, 在onTouch()中返回true, 所以Button的onClick()就永遠接收不到訊息. 如果onTouch()返回false, 則Button的onClick()就會被觸發. 而且, 不論onTouch()是true or false, Activity onTouch永遠都收不到的, 因為觸發事件是Button的事, 跟Activity一點關係都沒有
public class MainActivity extends AppCompatActivity {
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("Thomas", "Activity onTouch");
return super.onTouchEvent(event);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn=(Button)findViewById(R.id.button);
btn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.d("Thomas", "onTouch");
return true; //消耗事件
}
});
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("Thomas","onClick");
}
});
}
}
