Android事件驱动机制
一般,用户经常会通过界面与应用交互,Android框架采用事件驱动的形式与用户交互,那如何处理用户界面中触发的事件?
可以通过从用户交互的View设置事件监听器的方式来实现对事件的处理,一个事件监听器是View类中一个包含单一回调方法的接口。当注册了监听器的View发生了对应的监听事件时,Android框架就会回调相应的监听方法。
* 常见的用户事件
点击事件、选择事件、触屏事件、长按事件、按键事件
1.点击事件
单击事件是事件机制中最常见的事件,通过对控件绑定View.OnClickListener 实现单击事件的监听
点击事件四种书写方式
& 私有类实现方式
& 匿名内部类实现方式
& 布局中对控件添加android:onClick
& Activity实现监听接口
例如:对按钮的事件监听
2.选择事件
复选事件
复选事件的监听接口:CompoundButton.OnCheckedChangeListener
复选控件CheckBox 有两种状态:选中与未选中状态,对复选控件
* 案例:明密文切换
通过对CheckBox控件的复选监听,实现对EditText内容明密文切换
单选事件
单选事件的监听接口:RadioGroup.OnCheckedChangeListener
RadioButton与RadioGroup组合使用才能实现单选功能
* 案例:选择字符集
通过对RadioButton控件的选择监听,实现对字符集的选择
下拉列表选择
下拉事件的监听接口:AdapterView.OnItemSelectedListener
* 案例:城市选择
通过对Spinner下拉列表监听,实现对城市的选择
3.长按与触屏事件
长按事件监听接口:View.OnLongClickListener
触屏事件监听接口:View.OnTouchListener
* 案例:长按图标设置手机桌面壁纸
clearWallpaper :清除桌面壁纸
setWallpaper(BitMap bitmap) :设置桌面壁纸
设置壁纸要添加权限:
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
4.键盘事件
Activity实现了KeyEvent.backcall接口
onKeyDown(int keyCode, KeyEvent event) :当键按下去触发
onBackPress():当返回键按下去触发 ,Activity的方法
模拟器常见的按键:
Back 返回键
Home 手机屏幕桌面
Ctrl+F11 切换模拟器横竖屏幕
F2 手机菜单
F3 电话面板
F8 手机网络开关
1.代码: activity_click.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".ClickEventActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:textSize="26sp"
android:text="点击事件实现" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="70dp"
>
<Button
android:id="@+id/anonymous_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="匿名类"
/>
<Button
android:id="@+id/inner_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="私有类"
/>
<Button
android:id="@+id/xml_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="byXml"
android:text="xml"
/>
<!--textSize="10sp": 字体大小应该在12 以上 -->
<Button
android:id="@+id/activity_btn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Activity"
android:textSize="14sp"
/>
</LinearLayout>
</RelativeLayout>
ClickEventActivity.java
package cn.opera.clickevent;
import cn.opera.clickevent.R;
import android.os.Bundle;
import android.app.Activity;
import android.app.Application;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class ClickEventActivity extends Activity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_click_event);
/**点击事件的四种实现方式
* 1. 匿名内部类
* :性能最好,但是可读性不强
* 2. 私有类
* :假如多个控件共享某个监听器
* 3. xml设置onClick属性
* :书写最简洁,但是与xml布局的耦合度增强了
* 4. Activity实现点击事件接口
* 假如有多个请求,可以通过该方式分销请求
*
*
*/
//1. 匿名内部类
Button anonymousBtn=(Button)findViewById(R.id.anonymous_btn);
anonymousBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/**瞬时显示信息
* context:上下文
* 分为两种 :
* 整个应用的上下文 :Application的实例
* 组件级别的上下文 :Activity、Service
* 作用: 引用系统的服务 ,引用系统的资源(图片资源)
*/
Toast.makeText(ClickEventActivity.this, R.string.anonymous, Toast.LENGTH_SHORT).show();
}
});
//2. 私有类
Button innerBtn=(Button)this.findViewById(R.id.inner_btn);
//设置点击监听事件
innerBtn.setOnClickListener(new MyClickListener());
//3. 对xml按钮控件设置点击监听事件
Button xmlBtn=(Button)findViewById(R.id.xml_btn);
xmlBtn.setOnClickListener(this);
//4. Activity实现点击事件接口
Button activityBtn=(Button)findViewById(R.id.activity_btn);
//设置点击监听事件
activityBtn.setOnClickListener(this);
}
private class MyClickListener implements View.OnClickListener{
@Override
public void onClick(View v) {
//getApplicationContext():引用系统的上下文
Toast.makeText(getApplicationContext(), "私有类", Toast.LENGTH_SHORT).show();
}
}
/**通过布局xml文件的android:onClick属性设置点击监听
* 注意:
* 1. 方法名必须与android:onClick属性值相同,而且是public 、并且要带View输入参数
* 2. 不是所有的控件默认都支持android:onClick属性,
* 比如: Button、ImageView、ImageButton等都支持
* TextView 默认是不支持android:onClick属性 ,假如要支持 ,需要 设置 android:clickable="true"
* 3. 假如控件即在xml布局中设置android:onClick属性,又对该控件设置了点击监听的方法,如何回调?
* 只调用监听器的方法 ,而xml属性关联的方法不回调
*
*/
public void byXml(View v){
Toast.makeText(getApplicationContext(), "XML", Toast.LENGTH_SHORT).show();
}
/**
* 4. Activity实现点击事件接口
* v:View :当前操作的控件View
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.activity_btn://假如是Activity的按钮
Toast.makeText(getApplicationContext(), "Activity实现", Toast.LENGTH_SHORT).show();
break;
case R.id.xml_btn://假如点击的是 xml命令按钮
Toast.makeText(getApplicationContext(), "XML+Activity实现", Toast.LENGTH_SHORT).show();
break;
case R.id.anonymous_btn:
default:
break;
}
}
}
2.代码: activity_event.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg_yellow"
android:orientation="vertical" >
<!-- 通过CheckBox复选控件实现明密文切换 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/info_edt"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="textPassword"
android:text="android" />
<CheckBox
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="明文显示" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:background="@android:color/darker_gray" />
<!-- 通过RadioGroup 与RadioButton 单选组 实现字符集的选择 -->
<RadioGroup
android:id="@+id/radioGroup1"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content" >
<RadioButton
android:id="@+id/radio0"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:checked="true"
android:text="GBK" />
<RadioButton
android:id="@+id/radio1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="UTF-8" />
<RadioButton
android:id="@+id/radio2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="ISO8859" />
</RadioGroup>
<!--android:entries:下拉列表条目 -->
<Spinner
android:id="@+id/city_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:entries="@array/citys"
/>
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:background="@android:color/darker_gray" />
<!-- 通过View的长按 实现 手机壁纸的设置 -->
<ImageView
android:id="@+id/image"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/girl1"
/>
</LinearLayout>
EventActivity.java
package cn.itcat.event;
import java.io.IOException;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.Toast;
public class EventActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_event);
//1. 通过CheckBox实现明密文转换
changeInfo();
//2. RadioGroup与RadioButton组合一起使用 ,实现字符集的切换
choiceCharset();
//3. 通过Spinner下拉选择城市
selectCity();
//4. 通过View的长按手机壁纸
setPaper();
}
//4. 通过View的长按手机壁纸
private void setPaper() {
final ImageView image=(ImageView)findViewById(R.id.image);
//对图片控件设置长按监听
image.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
System.out.println("onLongClick");
//当长按该图片控件时,设置手机壁纸
try {
clearWallpaper();//清除手机壁纸
/**Bitmap :位图格式的图片文件 ,以像素的形式来描述图片 ,比如 :png、jpg、bmp等都是位图文件
* Drawable :可绘制的对象 : 它的子类有 BitmapDrawable 、ClipDrawable等
*/
// Drawable drawable = getResources().getDrawable(R.drawable.ic_girl1);
// BitmapDrawable bitmapDrawable=(BitmapDrawable) drawable;
// Bitmap bitmap=bitmapDrawable.getBitmap();
// setWallpaper(bitmap);//设置壁纸
// 通过操作的图片控件的前景图片属性来设置壁纸
Drawable drawable = image.getDrawable();//取图片控件的前景属性
BitmapDrawable bitmapDrawable=(BitmapDrawable) drawable;
Bitmap bitmap=bitmapDrawable.getBitmap();
setWallpaper(bitmap);//设置壁纸
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
});
//对图片控件设置触摸监听
image.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("onTouch");
return false;//返回为真,表示请求已经耗尽(结束),告诉android框架不再继续回调后续的监听方法
//为假,表示当前请求还没有结束,会继续回调后续的其他监听方法
}
});
//对图片控件设置点击监听
image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("onClick");
}
});
}
//3. 通过Spinner下拉选择城市
private void selectCity() {
final Spinner citySpinner=(Spinner)findViewById(R.id.city_spinner);
citySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
/**当下拉项被选择时,回调该方法
* parent:Spinner
* view:View :当前操作的下拉项视图
* position: 当前选择的下拉项的数据在Adapter适配器中的位置
* id: 当前选择的下拉项的数据的行号 (id属性)
*/
@Override
public void onItemSelected(AdapterView<?> parent, View view,
int position, long id) {
//所选的下拉项的数据显示出来
// String city=citySpinner.getAdapter().getItem(position).toString();
//通过数组资源id来获取数组
String[] citys=getResources().getStringArray(R.array.citys);
System.out.println(getResources().getString(R.string.app_name));
System.out.println(getString(R.string.app_name));
String city=citys[position];
Toast.makeText(getApplicationContext(), city+"数组", Toast.LENGTH_SHORT).show();
}
//当没有任何选项时,回调该方法, 一般是下拉选择出现异常时回调该方法
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
//2. RadioGroup与RadioButton组合一起使用 ,实现字符集的切换
private void choiceCharset() {
//引用单选组
RadioGroup radioGroup=(RadioGroup)findViewById(R.id.radioGroup1);
//对单选组设置选择状态改变的监听
radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
/**当状态改变,则回调该方法
* group:RadioGroup
* checkedId:当前被选中的RadioButton 按钮控件的资源id
*/
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
//通过选中的控件的id 来引用控件
// RadioButton radioButton=(RadioButton)findViewById(checkedId);
// RadioButton radioButton=(RadioButton)EventActivity.this.findViewById(checkedId);
//通过直接的父控件对象group来查找子控件 ,
RadioButton radioButton=(RadioButton)group.findViewById(checkedId);
Toast.makeText(getApplicationContext(), radioButton.getText().toString()+"group",
Toast.LENGTH_SHORT).show();
}
});
}
//1. 通过CheckBox实现明密文转换
private void changeInfo() {
//获取编辑框对象
final EditText infoEdt=(EditText)findViewById(R.id.info_edt);
CheckBox checkBox=(CheckBox)findViewById(R.id.checkBox);
//设置复选框的状态改变的监听
checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
/**当复选控件选择状态改变,则回调该方法 ,实现明密文切换
* buttonView:CheckBox
* isChecked:是否选中
*/
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
//把隐藏的编辑框的文本还原为明文
infoEdt.setTransformationMethod(new HideReturnsTransformationMethod());
}else{
//把编辑框的文本以密文显示
infoEdt.setTransformationMethod(new PasswordTransformationMethod());
}
}
});
}
/**当按键按下去 ,回调该方法,该方法 是KeyEvent.Callback 的方法 Activity实现了KeyEvent.Callback接口
* keyCode:按键码
* event:按键事件
*/
private long currentTime=0;//当前时间
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
System.out.println("按键码"+keyCode);
//返回键的按键码是4
if(keyCode==4){
if(System.currentTimeMillis()-currentTime>=2000){
System.out.println(System.currentTimeMillis());
Toast.makeText(getApplicationContext(), "在两秒之内,再按一次,退出系统",
Toast.LENGTH_SHORT).show();
currentTime=System.currentTimeMillis();
}else{
finish();//当按下返回键时,销毁该Activity ,退出系统的方法
}
}
// return super.onKeyDown(keyCode, event);
return true;
}
//当按下返回键,则回调该方法,该方法是Activity的方法
@Override
public void onBackPressed() {
System.out.println("onBackPressed");
// finish();结束,销毁当前的Activity
super.onBackPressed();
}
//当两秒钟之内连续按返回键退出系统
}
3.代码
activity_advanced_ui.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/bg_yellow"
tools:context=".AdvanceUIActivity" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<!-- TextView 默认不支持onClick方法 ,要支持 ,设置 android:clickable=true -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="声音开关"
android:textSize="20sp"
android:onClick="setVoice"
android:clickable="true"
android:textColor="@color/voice_color_select"
android:layout_marginRight="10dp"
/>
<ToggleButton
android:id="@+id/voice_toggleBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/voice_picture_select"
android:textOn=""
android:textOff=""
/>
</LinearLayout>
<Switch
android:id="@+id/switch1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="wifi开关" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<!--style="@android:style/Widget.ProgressBar.Horizontal 引用系统的样式 设置控件的样式 : -->
<ProgressBar
android:id="@+id/progressBar1"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="add"
style="@android:style/Widget.Button.Inset"
android:text="增加" />
</LinearLayout>
<!-- 滚屏视图 ScrollView:它是FrameLayout的子类,只能拥有一个直接的子控件,只支持垂直滚屏
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
</LinearLayout>
</ScrollView>
-->
<!-- 滚屏视图 HorizontalScrollView:它是FrameLayout的子类,只能拥有一个直接的子控件,只支持水平滚屏 -->
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
<Button
android:layout_width="100dp"
android:layout_height="80dp"
android:text="控件"
/>
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
advancedUi.java
package cn.itcast.advanceui;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.Switch;
import android.widget.Toast;
import android.widget.ToggleButton;
public class AdvanceUIActivity extends Activity {
private ToggleButton mToggleButton;
private ProgressBar mprogressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_advance_ui);
mToggleButton=(ToggleButton)findViewById(R.id.voice_toggleBtn);
//对开关按钮设置状态改变的监听
mToggleButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
/**当开关按钮状态改变,则回调该方法
* buttonView:ToggleButton
* isChecked: 当前控件的状态
*/
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Toast.makeText(getApplicationContext(), isChecked?"声音开":"声音关",
Toast.LENGTH_SHORT).show();
}
});
//2. 滑动开关按钮:
Switch switch1=(Switch) findViewById(R.id.switch1);
switch1.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Toast.makeText(getApplicationContext(), isChecked?"wifi开":"wifi关",
Toast.LENGTH_SHORT).show();
}
});
//3. 设置进度条
mprogressBar=(ProgressBar)findViewById(R.id.progressBar1);
mprogressBar.setMax(10);//设置进度条的最大值
}
//当按下声音开发的TextView时,回调该方法
public void setVoice(View v){
mToggleButton.toggle();//取反
}
//增加进度值
public void add(View v){
int currentProgress=mprogressBar.getProgress();//取得当前的进度值
mprogressBar.setProgress(currentProgress+1);//设置进度值
// ScrollView scrollView;
}
}