在說明畫面元件時, 可以在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"); } }); } }