事件处理概述
基于监听的事件处理
事件监听器
事件源
事件
Button button = findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this,"单击了按钮",Toast.LENGTH_SHORT).show();
}
});
基于回调的事件处理
重写android组件特定的回调方法
重写activity的回调方法
常重写的回调方法有:onTouchEvnet、onKeyDown、onKeyUp
public class MainActivity extends Activity {
private long exitTime = 0; //退出时间变量值
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
exit();
return true; //拦截返回键
}
return super.onKeyDown(keyCode, event);
}
public void exit() {
if ((System.currentTimeMillis() - exitTime) > 2000) { //计算按键时间差是否大于两秒
Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
finish();
System.exit(0); //销毁强制退出
}
}
}
两个事件处理方式:
- 通过的事件 - 使用基于回调的事件处理
- 特定的事件 - 使用基于监听的事件处理
物理按键事件处理
物理按键:菜单键、主屏建、返回键、音量键、电源键
每个物理按键都有3个状态:
按下,但是没有松开 : onKeyDown
松开按钮的时候 : onKeyUp
长按按键 : onKeyLongPress
区分时按下的哪个键 :
android对每一个物理键都通过常量进行了定义,如返回键 KEYCODE_BACK
实例:连续按两次返回键退出应用
public class MainActivity extends Activity {
private long exitTime = 0; //退出时间变量值
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
exit();
return true; //拦截返回键
}
return super.onKeyDown(keyCode, event);
}
public void exit() {
if ((System.currentTimeMillis() - exitTime) > 2000) { //计算按键时间差是否大于两秒
Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show();
exitTime = System.currentTimeMillis();
} else {
finish();
System.exit(0); //销毁强制退出
}
}
}
触摸屏事件处理
单击事件
setOnClickListener实现View.OnClickListener接口中的onClick方法
Button button = findViewById(R.id.button_1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(MainActivity.this,"单击了按钮",Toast.LENGTH_SHORT).show();
}
});
}
长按事件
长按,选中文字
长按,选择已读、删除
长按时间:2秒以上才会触发
setOnLongClickListener实现View.OnLongClickListener接口中的onLongClick方法
长按图片弹出菜单
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = (ImageView) findViewById(R.id.imageView); //获取图片组件
imageView.setOnLongClickListener(new View.OnLongClickListener() { //创建长按监听事件
@Override
public boolean onLongClick(View v) {
registerForContextMenu(v); //将长按事件注册菜单中
openContextMenu(v); //打开菜单
return false;
}
});
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { //创建菜单
super.onCreateContextMenu(menu, v, menuInfo);
menu.add("收藏"); //为菜单添加参数
menu.add("举报");
}
}
触摸事件
setOnTouchListener实现View.OnTouchListener接口中的onTouch方法
MotionEvent 类 :保存发生触摸的位置、时间等细节信息
实例:通过触摸屏帮企鹅戴好帽子
1、主layout文件,仅配置一个没有帽子的企鹅图片
2、创建帽子的View
/**
* Created by Administrator on 2016/3/4.
*/
public class HatView extends View {
public float bitmapX; // 帽子显示位置的X坐标
public float bitmapY; // 帽子显示位置的Y坐标
public HatView(Context context) {//重写构造方法
super(context);
bitmapX = 65; // 设置帽子的默认显示位置的X坐标
bitmapY = 0; // 设置帽子的默认显示位置的Y坐标
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint(); //创建Paint对象
//根据图片生成位图对象
Bitmap bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.hat);
canvas.drawBitmap(bitmap, bitmapX, bitmapY, paint); // 绘制帽子
if (bitmap.isRecycled()) { // 判断图片是否回收
bitmap.recycle(); // 强制回收图片
}
}
}
3、触摸屏操作
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout relativeLayout = (RelativeLayout) findViewById(R.id.relativeLayout); // 获取相对局管理器
final HatView hat = new HatView(MainActivity.this); // 创建并实例化HatView类
// 为帽子添加触摸事件监听
hat.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
hat.bitmapX = event.getX()-80; // 设置帽子显示位置的X坐标
hat.bitmapY = event.getY()-50; // 设置帽子显示位置的Y坐标
hat.invalidate(); // 重绘hat组件
return true;
}
});
relativeLayout.addView(hat); //将hat添加到布局管理器中
}
}
单击事件和触摸事件的区别
在单击事件方法时,实际单击事件和触摸事件都发生了
先触发触摸事件,如果触摸消费事件没有完全消费掉,再触发单击事件
在重写触摸事件时,返回了true,表示消费了触摸事件,否则没有完全消费触摸事件,再交给后面的事件进行处理
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button= (Button) findViewById(R.id.btn); //获得按钮对象
// 为按钮添加单击事件监听器
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("onClick","单击事件");
}
});
// 为按钮添加触摸事件监听器
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction()==MotionEvent.ACTION_DOWN){ //表示手指按下时
Log.i("onTouch","按下");
}else if (event.getAction()==MotionEvent.ACTION_UP){ //表示手指抬起时
Log.i("onTouch","抬起");
}
return false; //表示未消耗掉这个事件
}
});
}
}
手势
手势检测
GestureDetector 手势检测的类
GestureDetector.OnGestureListener一个监听器,对用户的手势行为进行响应
包含6个需要重写的方法
onDown : 触摸事件按下时触发
onFling : 用户的手指在屏幕上拖过是触发
onLongPress : 长按时触发
onScroll :
onShowPress
onSingleTapUp : 手势轻击
实例:实现手机相册的查看相片功能
使用了ViewFlipper,使用动画显示多个组件的切换效果
1、在主layout文件中使用ViewFlipper
<?xml version="1.0" encoding="utf-8"?>
<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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.mingrisoft.MainActivity">
<ViewFlipper
android:id="@+id/flipper"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
</ViewFlipper>
</RelativeLayout>
2、创建4个切换动画
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-100%p"
android:toXDelta="0"
android:duration="500"/>
</set>
3、手势检测
public class MainActivity extends AppCompatActivity implements GestureDetector.OnGestureListener {
ViewFlipper flipper; //定义ViewFlipper
GestureDetector detector; //定义手势检测器
Animation[] animation = new Animation[4];//定义动画数组,为ViewFlipper指定切换动画
final int distance = 50; //定义手势动作两点之间最小距离
//定义图片数组
private int[] images = new int[]{R.drawable.img01, R.drawable.img02, R.drawable.img03,
R.drawable.img04, R.drawable.img05, R.drawable.img06, R.drawable.img07, R.drawable.img08,
R.drawable.img09,
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
detector = new GestureDetector(this, this); //创建手势检测器
flipper = (ViewFlipper) findViewById(R.id.flipper); //获取ViewFlipper
for (int i = 0; i < images.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setImageResource(images[i]);
flipper.addView(imageView); //加载图片
}
//初始化动画数组
animation[0] = AnimationUtils.loadAnimation(this, R.anim.slide_in_left);
animation[1] = AnimationUtils.loadAnimation(this, R.anim.slide_out_left);
animation[2] = AnimationUtils.loadAnimation(this, R.anim.slide_in_right);
animation[3] = AnimationUtils.loadAnimation(this, R.anim.slide_out_right);
}
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
/*
如果第一个触点事件的X坐标到第二个触点事件的X坐标的距离超过distance就是从右向左滑动
*/
if (e1.getX() - e2.getX() > distance) {
//为flipper设置切换的动画效果
flipper.setInAnimation(animation[2]);
flipper.setOutAnimation(animation[1]);
flipper.showPrevious();
return true;
/*
如果第二个触点事件的X坐标到第一个触点事件的X坐标的距离超过distance就是从左向右滑动
*/
} else if (e2.getX() - e1.getX() > distance) {
//为flipper设置切换的动画
flipper.setInAnimation(animation[0]);
flipper.setOutAnimation(animation[3]);
flipper.showNext();
return true;
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//将该Activity上的触碰事件交给GestureDetector处理
return detector.onTouchEvent(event);
}
}
手势的创建和导出
通过下面软件创建手势
生成的手势文件在sdcard目录下
手势的识别
1、将保存的手势文件保存到raw目录下
2、在layout目录下添加EditText和一个手势组件
<?xml version="1.0" encoding="utf-8"?>
<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:background="@drawable/beijing"
tools:context="com.mingrisoft.MainActivity">
<EditText
android:id="@+id/editText"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:layout_marginLeft="40dp"
android:layout_marginTop="190dp"/>
<android.gesture.GestureOverlayView
android:id="@+id/gesture"
android:layout_width="320dp"
android:layout_height="180dp"
android:layout_alignParentBottom="true"
android:gestureStrokeType="multiple"
>
</android.gesture.GestureOverlayView>
</RelativeLayout>
3、识别手势并输出
public class MainActivity extends Activity implements GestureOverlayView.OnGesturePerformedListener {
private GestureLibrary library;
private EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
library = GestureLibraries.fromRawResource
(MainActivity.this, R.raw.gestures); //加载手势文件
editText = (EditText) findViewById(R.id.editText); //获取编辑框
if (!library.load()) {// 如果加载失败则退出
finish();
}
GestureOverlayView gestureOverlayView = (GestureOverlayView) findViewById(R.id.gesture);
gestureOverlayView.setGestureColor(Color.BLACK);
gestureOverlayView.setFadeOffset(1000);
gestureOverlayView.addOnGesturePerformedListener(this);// 增加事件监听器
}
@Override
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<Prediction> gestures = library.recognize(gesture);// 获得全部预测结果
int index = 0;// 保存当前预测的索引号
double score = 0.0;// 保存当前预测的得分
for (int i = 0; i < gestures.size(); i++) {// 获得最佳匹配结果
Prediction result = gestures.get(i);// 获得一个预测结果
if (result.score > score) {
index = i;
score = result.score;
}
}
String text = editText.getText().toString();// 获得编辑框中已经包含的文本
text += gestures.get(index).name;// 获得最佳匹配
editText.setText(text);// 更新编辑框
}
}