Android教材 | 第三章 Android界面事件处理(一)—— 杰瑞教育原创教材试读
前 言
JRedu
Android应用开发中,除了界面编程外,另一个重要的内容就是组件的事件处理。在Android系统中,存在多种界面事件,比如触摸事件、按键事件、点击事件等。在用户交互过程中,App必须为用户提供响应逻辑,这种响应就是通过事件处理完成的。
本章内容将详细介绍Android事件的具体处理及常见事件。
3.1 Android 事件基础知识 |
Android中提供了两种处理事件的方式:
基于监听器的事件处理
基于回调的事件处理
在事件处理过程中涉及到三个重要概念,具体如下:
事件:事件封装了界面组件上发生的事件的具体信息。通过Event对象,可以获取界面组件事件的相关信息。
事件源:事件产生的来源,通常是指界面上的各种组件,比如文本框、按钮、窗口、菜单等。
事件监听器:监听事件源,并处理事件。
三者之间的关系如图3-1所示。
(图 3-1)
3.2 基于监听器的Android事件处理 |
Android中基于监听的事件处理,最主要的做法是为界面组件绑定对应的事件监听器。主要有四种实现方式:
内部类作为事件监听器
匿名内部类作为事件监听器
Activity作为监听器
布局文件中直接绑定
3.2.1内部类作为事件监听器
内部类作为事件监听器有两个优点,一是在本类中可以复用该监听器;二是作为内部类可以访问外部类中的界面组件。下面通过实例讲解具体用法。
实例3-1:
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
tools:context="com.jredu.event.MainActivity">
<TextView
android:id="@+id/show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginBottom="10dp"
/>
<Button
android:id="@+id/btnOK"
android:layout_width="match_parent" //按钮作为事件源
android:layout_height="wrap_content"
android:textSize="16sp"
android:text="点击"
/>
</LinearLayout>
上面布局文件中含有1个按钮,该按钮作为事件源,当点击该按钮后将触发点击事件。为按钮绑定事件监听器的程序如下:
public class MainActivity extends AppCompatActivity {
TextView show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = (TextView)findViewById(R.id.show);
Button btn = (Button)findViewById(R.id.btnOK); //获取布局文件中按钮
btn.setOnClickListener(new InnerListener()); //为按钮绑定监听器
}
private class InnerListener implements View.OnClickListener{ //单击事件的监听器
@Override
public void onClick(View v) { //事件发生时,执行的方法
show.setText("这是内部类作为事件监听器!");
}
}
}
程序中定义了一个内部类,该内部类实现了View.OnClickListener,将作为单击事件的监听器。实例运行效果如图3-2。
(图 3-2)
从该案例可以得出,基于监听的事件处理编写步骤如下:
- 通过findViewById获取要监听的界面组件,即事件源。
- 编写事件监听器类,该类需要实现特定的监听器接口。监听器类由程序员负责编写,核心工作就是实现接口中的方法。
- 为界面组件绑定监听器。
3.2.2匿名内部类作为事件监听器
使用匿名内部类作为事件监听器是目前使用比较广泛的一种事件监听器形式。具体案例代码如下:
public class AnonymousListenerActivity extends Activity {
TextView show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = (TextView)findViewById(R.id.show);
Button btn = (Button)findViewById(R.id.btnOK);
btn.setOnClickListener(new View.OnClickListener() { //匿名类作为监听器
@Override
public void onClick(View v) {
show.setText("这是匿名内部类作为事件监听器!"); //事件处理方法
}
});
}
}
上面程序通过直接new一个匿名类作为监听器,该形式应用广泛,缺点是代码可读性差,语法不易掌握。
3.2.3Activity作为监听器
使用Activity作为监听器,也是一种常见的事件监听器形式。这种形式需要Activity实现对应的监听器接口。具体案例代码如下:
public class ActivityAsListener extends Activity implements View.OnClickListener{ //实现单击监听器接口
TextView show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listener);
show = (TextView)findViewById(R.id.show);
findViewById(R.id.btnOK).setOnClickListener(this); //获取按钮,并绑定监听器
findViewById(R.id.btnCancel).setOnClickListener(this);
} @Override
public void onClick(View v) { //实现接口中的单击方法,根据组件ID的进行不同的处理。
switch (v.getId()){
case R.id.btnOK:
show.setText("确定:Activity作为事件监听器!!");
break;
case R.id.btnCancel:
show.setText("取消:Activity作为事件监听器!!");
break;
default:
break;
}
}
}
上面程序使用了Activity作为监听器,该形式的优点是在同一个事件处理方法中可以处理界面中多个相同的事件;缺点是使程序结构显得比较混乱,可读性较差。
3.2.4控件绑定
最为简洁的方式就是在布局文件中为标签直接绑定事件处理方法。在Android大多数标签都支持onClick属性,通过该属性可以为标签直接绑定事件监听器。
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp"
tools:context="com.jredu.event.MainActivity"> <TextView
android:id="@+id/show"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:layout_marginBottom="10dp"
/> <Button
android:id="@+id/btnOK"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:onClick="clickToShow" //通过onClick属性绑定事件处理方法clickToShow
android:text="确定"
/> </LinearLayout>
public class TagBindActivity extends Activity {
TextView show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tag_listener);
show = (TextView)findViewById(R.id.show);
}
public void clickToShow(View v){ //事件处理方法,参数v为事件源
show.setText("通过标签直接绑定事件监处理方法!!");
}
}
3.2.5View中事件监听器
View中常见的事件监听器如下表:
监听器 | 说明 |
View.onClickListener | 点击事件监听器 |
View.onLongClickListener | 长按事件监听器 |
View.onKeyListener | 键盘事件监听器 |
View.onFocusChangeListener | 焦点事件监听器 |
View.onTouchListener | 触摸事件监听器 |
在基于回调函数的事件处理中,UI控件承担了事件源和事件监听器双重职责。当触发UI控件的事件时,该控件会调用相应的回调函数进行处理事件,程序员所要做事情就是在回调函数中编写事件处理逻辑。下面以Android的触摸事件为例,讲解基于回调函数的事件处理方式。
通过自定义组件并重写onTouchEvent方法完成触摸事件的处理。自定义组件的具体知识可参照第十四章内容。
public class TouchView extends View { //TouchView继承自View
private int x=0;
private int y=0;
private Bitmap bitmap;
private Paint mPaint;
private boolean isPressed =false;
public TouchView(Context context) {
super(context);
init(context);
} public TouchView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
} private void init(Context context){
bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ship_center); //初始化图片资源
mPaint = new Paint(); //创建画笔
mPaint.setAntiAlias(true); //为图片边缘去锯齿
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap,x,y,mPaint);
}
private int oX;
private int oY;
@Override
public boolean onTouchEvent(MotionEvent event) { //事件参数,封装事件相关信息
switch (event.getAction()){
case MotionEvent.ACTION_DOWN: //处理ACTION_DOWN触摸事件,记录触摸点的坐标,并设置按下标识isPressed为true。
oX = (int)event.getX();
oY = (int)event.getY();
isPressed = true;
break;
case MotionEvent.ACTION_UP:
isPressed = false;
break;
case MotionEvent.ACTION_MOVE: //处理ACTION_MOVE触摸事件,计算移动的距离,并改变图片的位置。
if(isPressed) {
int mX = (int) event.getX() - oX;
int mY = (int) event.getY() - oY;
oX = (int) event.getX();
oY = (int) event.getY();
x += mX;
y += mY;
invalidate(); //通知TouchView进行重绘
}
break;
}
return true; //返回true,表明组件已经处理了该事件。
}
}
在上面自定义的TouchView类中,我们重写了onTouchEvent方法。该方法负责处理自定义组件TouchView的触摸事件,案例效果如图3-3.
(图3-3)
如上面的案例程序,onTouchEvent方法处理了多种事件。当手指触摸到屏幕、在屏幕上移动或者离开屏幕时,会分别触发ACTION_DOWN、ACTION_MOVE和ACTION_UP事件。这些事件都由onTouchEvent进行处理。
在View中除了onTouchEvent方法,还提供了用于处理其他事件的回调方法,具体参看表3-2。
方法 | 说明 |
boolean onKeyDown(int keyCode,KeyEvent event) | 处理手机按键按下事件 |
boolean onKeyUp(int keyCode,KeyEvent event) | 处理手机按键抬起事件 |
boolean onKeyLongPress(int keyCode,KeyEvent event) | 处理手机按键长按事件 |
boolean onTouchEvent(MotionEvent event) | 处理手机的触屏事件 |
boolean onTrackballEvent(MotionEvent event) | 处理手机轨迹球事件 |
通过表3-1,可以发现这些回调方法都有一个boolean类型的返回值,该返回值用于表明该方法有没有处理完对应的事件。
- 如果方法返回true,则表明该回调方法完成事件处理,事件到此为止,不会继续传播。
- 如果方法返回false,则表明该回调方法未完成事件处理,事件会继续传播。
下面通过案例说明控件的事件传递过程。
public class JreduButton extends Button { //JreduButton继承Button
private Context mContext;
public JreduButton(Context context) {
super(context);
this.mContext = context;
} public JreduButton(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
} @Override
public boolean onTouchEvent(MotionEvent event) { //重写onTouchEvent方法
Toast t= Toast.makeText(this.mContext, "Button的onTouchEvent完成触摸事件的处理!", Toast.LENGTH_LONG); //定义Toast对象,并设置Toast出现的位置
t.setGravity(Gravity.CENTER,0,0);
t.show();
return true; //返回值为true,表明该方法处理了触摸事件,该事件不再传递
}
}
重写Activity中的onTouchEvent方法,同样用于处理触屏事件,Activity类的代码如下:
public class EventActivity extends AppCompatActivity {
TextView show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event);
show = (TextView)findViewById(R.id.show);
} @Override
public boolean onTouchEvent(MotionEvent event) { //重写onTouchEvent的方法,在该方法中设置文本显示内容
show.setText("Activity的onTouchEvent处理了事件!");
return true;
}
}
运行上面的程序,效果如图3-4。
(图3-4)
将上面程序中自定义按钮JreduButton中的onTouchEvent的返回值改为false。运行程序,效果如图3-5。
(图3-5)
基于监听的事件处理和基于回调的事件处理二者并不是孤立,Android对同一个事件的处理往往会提供这两种事件处理方式,比如针对触屏事件、按键事件,具体参看下表:
基于回调 | 基于监听 |
onTouchEvent(MotionEvent event) | View.onTouchListener |
onKeyDown(int keyCode,KeyEvent event) | View.onKeyListener |
onKeyUp(int keyCode,KeyEvent event) | View.onKeyListener |
3.4 Android触屏手势操作 |
通过上一节重写View的onTouchEvent方法或是实现onTouchListener接口可以处理一些简单的触屏事件,比如按下、移动、抬起等。如果想要处理一些复杂的手势,则很难处理。Android提供了GestureDetector类,通过该类可以识别手势。
类、接口 | 说明 |
GestureDetector | 手势识别类 |
OnGestureListener | 手势滑动监听接口 |
OnDoubleTapListener | 双击手势监听器接口 |
SimpleOnGestureListener | OnGestureListener和OnDoubleTapListener的实现类 |
下面我们通过一个案例来讲解手势识别类的具体用法,该案例通过手势在屏幕上滑动可以完成图片切换。
<?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"
android:gravity="center"
tools:context="com.jredu.gesture.MainActivity"> <ImageView //用于切换显示图片的ImageView
android:id="@+id/img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/jredu01" />
</RelativeLayout>
public class MainActivity extends Activity {
private GestureDetector detector;
private ImageView imageView;
private int index = 0;
private int[] arr = {
R.drawable.jredu01,R.drawable.jredu02,
R.drawable.jredu03,R.drawable.jredu04};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
detector = new GestureDetector(this,new SimpleGestureListener()); //创建手势识别对象
imageView = (ImageView)findViewById(R.id.img);
} @Override
public boolean onTouchEvent(MotionEvent event) { //将触屏事件交由手势识别对象处理
return detector.onTouchEvent(event);
} private class SimpleGestureListener extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if(e2.getX()-e1.getX()>30 && Math.abs(velocityX)>Math.abs(velocityY)){ //水平滑动距离大于30并且X轴速度大Y轴速度时进行图片切换。
index++;
if(index>=arr.length){
index=0;
}
imageView.setImageResource(arr[index]);
}
return true; //该事件已被处理。
}
}
}
在上面程序中定义了一个手势识别对象,并且该对象设置了一个手势监听器SimpleGestureListener。SimpleGesutreListener继承了GestureDetector.SimpleOnGestureListener并重写了onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)方法。该方法用于检测滑屏手势,方法中共有4个参数,分别为:
- 参数MotionEvent e1,手势起点的移动事件。
- 参数MotionEvent e2,手势终点的移动事件。
- 参数float velocityX,每秒X轴方向上移动的速度。
- 参数float velocityY,每秒Y轴方向上移动的速度。
该案例显示效果如图3-6.
(图3-6)
想要处理什么样的手势,只需要实现监听器对应的方法即可。除了上面的onFling方法,手势监听器中还包含下面这些方法。
方法 | 所属接口 |
说明 |
onDown(MotionEvent e) | OnGestureListener | 单击,触摸屏按下时触发。 |
onSingleTapUp(MotionEvent e) | OnGestureListener | 抬起,手指离开屏幕时触发。长按、滚动、滑动时不触发。 |
onLangPress(MotionEvent) | OnGestureListener | 长按触摸屏时触发。 |
onShowPress(MotionEvent) | OnGestureListener | 短按触摸屏时触发。 |
onScroll(MotionEvent e1,MotionEvent e2,float distanceX,float distanceY) | OnGestureListener | 滚屏,触摸屏按下后滚动触发。 |
onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) | OnGestureListener | 滑屏,触摸屏按下后快速移动并抬起,会先触发滚动手势,跟着触发一个滑动手势。 |
onDoubleTap(MotionEvent e) | OnDoubleTapListener | 双击,手指在触屏上迅速点击第二次是触发。 |
onDoubleTapEvent(MotionEvent e) | OnDoubleTapListener | 双击的按下和抬起各触发一次该事件。 |
onSingleTapConfirmed(MotionEvent e) | OnDoubleTapListener | 单击确认。 |
编者按
杰瑞教育原创系列教材将于年后与大家正式见面。为更好的借鉴读者意见,我们将会陆续地在博客园推出一系列教材试读。我们也热忱的欢迎广大博友与我们互动,提出宝贵意见。我们也将为积极互动的博友,免费提供我们的原创教材以及更多福利,也欢迎大家加入下方QQ群与我们交流,谢谢大家!
出处:http://www.cnblogs.com/jerehedu/
版权声明:本文版权归烟台杰瑞教育科技有限公司和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
技术咨询:
Android教材 | 第三章 Android界面事件处理(一)—— 杰瑞教育原创教材试读的更多相关文章
- Android教材 | 第三章 Android界面事件处理(二)—— 杰瑞教育原创教材试读
编者按 JRedu 杰瑞教育原创系列教材将于年后与大家正式见面.为更好的借鉴读者意见,我们将会陆续地在博客园推出一系列教材试读.我们也热忱的欢迎广大博友与我们互动,提出宝贵意见. 本篇博客将推出教材 ...
- 第三章Android移植平台工具介绍
第三章Android移植平台工具介绍 进行 Android 移植的学习并不一定需要一款 Android 手机,但必须要有一款主流的开发板,开发板是用来进行嵌入式系统开发的电路板,包括中央处理器.存储器 ...
- 第三章 Android绘图机制与处理技巧
1.屏幕尺寸信息 屏幕大小:屏幕对角线长度,单位“寸”:分辨率:手机屏幕像素点个数,例如720x1280分辨率:PPI(Pixels Per Inch):即DPI(Dots Per Inch),它是对 ...
- 【ALearning】第三章 Android基本常见控件
本章主要介绍主要的寻常较多使用的控件,包含TextView.EditView.ImageView.Button等.本章将介绍相关控件基本属性的使用,为以后章节的进阶学习提供基础.案例中引用的Linea ...
- Android开发(三)——Android布局中实现圆角边框
设置corners_bg.xml(设置边框圆角可以在drawable-mdpi目录里定义一个xml): <?xml version="1.0" encoding=" ...
- 简单的学习心得:网易云课堂Android开发第三章自定义控件
这一章分三部分: (1)自定义控件:老师先简单讲解了一些细节,如为什么不用px,而要用dp,只因机型的屏幕分辨率不同,用px会导致差异太大.然后演示了制作自定义控件的步骤,先在xml文件中添加对应的自 ...
- 《Android群英传》读书笔记 (2) 第三章 控件架构与自定义控件详解 + 第四章 ListView使用技巧 + 第五章 Scroll分析
第三章 Android控件架构与自定义控件详解 1.Android控件架构下图是UI界面架构图,每个Activity都有一个Window对象,通常是由PhoneWindow类来实现的.PhoneWin ...
- 第三部分:Android 应用程序接口指南---第二节:UI---第三章 菜单
第3章 菜单 在许多不同类型的应用中,菜单通常是一种用户界面组件.为了提供给用户提供熟悉且一致的体验,你需要使用菜单API来展示用户动作和你Activity中的其他选项. 从安卓3.0系统(API l ...
- Android系统移植与驱动开发--第三章 Git使用入门及在学习中有感
第三章 Git使用入门 使用Git的目的是减少各种版本的Linux的压缩大小,提供源代码在Linux上进行编译. 在这一个章节中,其实就是关键步骤的操作,虽然Git与我们学习的android没有很大的 ...
随机推荐
- JZYZOJ 2043 多项式除法和取余 NTT 多项式
http://172.20.6.3/Problem_Show.asp?id=2043 最开始用了FFT,交上去全tle和wa了(tle的比较多),测了一组数据发现求逆元的过程爆double了(毕竟系数 ...
- POJ.1379.Run Away(模拟退火)
题目链接 POJ输出不能用%lf! mmp从4:30改到6:00,把4:30交的一改输出也过了. 于是就有了两份代码.. //392K 500MS //用两点构成的矩形更新,就不需要管边界了 #inc ...
- ARM LDR/STR, LDM/STM 指令
这里比较下容易混淆的四条指令,已经在这4条指令的混淆上花费了很多精力,现在做个小结,LDR,STR,LDM,STM这四条指令, 关于LDM和STM的说明,见另外一个说明文件,说明了这两个文件用于栈操作 ...
- PHP 如何创建守护(daemon)进程
先讲几个概念 守护进程: Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装入时启动,在系统关闭时终止. ...
- html5模拟平抛运动
<html> <head> <meta charset=utf-8> <title>html5炮弹</title> <script&g ...
- 该对象尚未初始化。请确保在所有其他初始化代码后面的应用程序启动代码中调用 HttpConfiguration.EnsureInitialized()。
WebAPI使用属性路由,配置config.MapHttpAttributeRoutes();后出现错误: System.InvalidOperationException: 该对象尚未初始化.请确保 ...
- Win10专业版永久激活方法
自从升级安装了Windows10系统以后,我想很多朋友和我一样,想要激活Windows10系统,但是小编找了半天以后发现,很多激活工具都是批量激活的,也就是只有180天的使用时间,那么我们怎么永久激活 ...
- springcloud 分布式服务跟踪sleuth+zipkin
原文:https://www.jianshu.com/p/6ef0b76b9c26 分布式服务跟踪需求 随着分布式服务越来越多,调用关系越来越复杂,组合接口越来越多,要进行分布式服务跟踪监控的需求也越 ...
- 查看Oracle数据库名和实例名的命令
查看数据库名 SQL> select name from v$database; NAME --------- ORCL SQL> desc v$database; 名称 ...
- win7下设置环境变量
手工当然可以进行环境变量的设置,但是如果一个小组有需要设置固定环境变量的操作,这可能就会有点麻烦了,xp下设置环境变量比较简单,直接用set,win7下需要使用setx SETX XX_HOME &q ...