用户界面设计

Download Report

Transcript 用户界面设计

基于Android的移
动应用开发
用户界面
Android用户界面
了解各种用户界面的控件的使用方法
掌握各种界面布局的特点和使用方法
掌握选项菜单、子菜单和快捷菜单的使用方法
掌握按键事件和触摸事件的处理方法
2
用户界面基础
用户界面(User Interface,UI)是系统和用户之间进行信息交换的媒介,
实现信息的内部形式与人类可以接受形式之间的转换
 在计算机出现早期,批处理界面(1945-1968)和命
令行界面(1969-1983)得到广泛的使用
 目前,流行图像用户界面(Graphical User Interface,
GUI),采用图形方式与用户进行交互的界面
 未来的用户界面将更多的运用虚拟现实技术,使用
户能够摆脱键盘与鼠标的交互方式,而通过动作、
语言,甚至是脑电波来控制计算机
3
用户界面基础
设计手机用户界面应解决的问题
◦ 需要界面设计与程序逻辑完全分离,这样不仅有利于他们的并
行开发,而且在后期修改界面时,也不用再次修改程序的逻辑
代码
◦ 根据不同型号手机的屏幕解析度、尺寸和纵横比各不相同,自
动调整界面上部分控件的位置和尺寸,避免因为屏幕信息的变
化而出现显示错误
◦ 能够合理利用较小的屏幕显示空间,构造出符合人机交互规律
的用户界面,避免出现凌乱、拥挤的用户界面
◦ Android已经解决了前两个问题,使用XML文件描述用户界面;
资源资源文件独立保存在资源文件夹中;对界用户面描述非常
灵活,允许不明确定义界面元素的位置和尺寸,仅声明界面元
素的相对位置和粗略尺寸
4
用户界面基础
Android用户界面框架
◦ Android用户界面框架
(Android UI Framework)采
用MVC(Model-ViewController)模型
◦ 提供了处理用户输入的控
制器(Controller)
◦ 显示用户界面和图像的视
图(View),以及保存数
据和代码的模型(Model)
键盘等输入
视图
控制器
更新
绘制界面
模型
5
用户界面基础
Android用户界面框架
◦ MVC模型
6
◦
MVC模型中的控制器能够接受并响应程序的外部动作,如按键动作或触摸屏动作等
◦
控制器使用队列处理外部动作,每个外部动作作为一个对立的事件被加入队列中,然后
Android用户界面框架按照“先进先出”的规则从队列中获取事件,并将这个事件分配给所对
应的事件处理函数
用户界面基础
Android用户界面框架
◦ Android用户界面框架(Android UI
Framework)采用视图树(View Tree)
模型
◦ Android用户界面框架中的界面元
素以一种树型结构组织在一起,
称为视图树
◦ Android系统会依据视图树的结构
从上至下绘制每一个界面元素。
每个元素负责对自身的绘制,如
果元素包含子元素,该元素会通
知其下所有子元素进行绘制
7
ViewGroup
View
ViewGroup
View
View
View
View
用户界面基础
Android用户界面框架
◦ 视图树
8
◦
视图树由View和ViewGroup构成
◦
View是界面的最基本的可视单元,存储了屏幕上特定矩形区域内所显示内容的数据结构,并
能够实现所占据区域的界面绘制、焦点变化、用户输入和界面事件处理等功能
◦
View也是一个重要的基类,所有在界面上的可见元素都是View的子类
◦
ViewGroup是一种能够承载含多个View的显示单元
◦
ViewGroup功能:一个是承载界面布局,另一个是承载具有原子特性的重构模块
用户界面基础
Android用户界面框架
◦ 单线程用户界面
9
◦
在单线程用户界面中,控制器从队列中获取事件和视图在屏幕上绘制用户界面,使用的都是
同一个线程
◦
特点:处理函数具有顺序性,能够降低应用程序的复杂程度,同时也能减低开发的难度
◦
缺点:如果事件处理函数过于复杂,可能会导致用户界面失去响应
界面控件
Android系统的界面控件分为定制控件和系统控件
◦ 定制控件是用户独立开发的控件,或通过继承并修改系统控件后所
产生的新控件。能够为用户提供特殊的功能或与众不同的显示需求
方式
◦ 系统控件是Android系统提供给用户已经封装的界面控件。提供在应
用程序开发过程中常见功能控件。系统控件更有利于帮助用户进行
快速开发,同时能够使Android系统中应用程序的界面保持一致性
常见的系统控件包括TextView、EditText、Button、
ImageButton、Checkbox、RadioButton、Spinner、ListView和
TabHost等
10
界面控件
11
界面控件
TextView、EditText和Button
◦ TextView是一种用于显示字符串的控件
◦ EditText则是用来输入和编辑字符串的控件
◦
EditText是一个具有编辑功能的TextView
◦ Button是一种按钮控件,用户能够在该控件上点击,并后引发
相应的事件处理函数
12
界面控件- myGUIDemo
TextView、EditText和Button
◦ 建立一个名为“myGUIDemo”的项目,包含TextView、
EditText和Button三个控件
◦
13
上方“用户名”部分使用的是TextView,中间的文字输入框使用的是EditText,“确定”按钮
使用的是Button
界面控件- myGUIDemo
TextView、EditText和Button
◦ 在activity_main.xml文件中的代码

<TextView
android:id="@+id/textview1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="用户名:"
/>
<EditText
android:id="@+id/edittext1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:text="确认"
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
14 />

android:id属性声明了相应控件的ID,主
要用于在代码中引用这个对象

“@+id/textview1”表示所设置的ID
值

@表示后面的字符串是ID资源

加号(+)表示需要建立新资源名称,
并添加到R.java文件中

斜杠后面的字符串(textview1)表
示新资源的名称

如果资源不是新添加的,或属于
Android框架的ID资源,则不需要使
用加号(+),但必须添加Android包
的命名空间,例如
android:id="@android:id/empty“
android:layout_width和
layout_height属性表示控件的宽度和高
度。 fill_parent表示控件宽度将等于父控
件的宽度, wrap_content表示控件只要
能够包含所显示字符串即可。
界面控件- myGUIDemo
TextView、EditText和Button
◦ MainActivity.java文件中代码的修改
(onCreate方法)
1.
2.
3.
4.
5.
6.
◦
◦
final TextView textView =
(TextView)findViewById(R.id.textview1);
final EditText editText = (EditText)findViewById(R.id.edittext1);
Button button1 = (Button) findViewById(R.id.button1);
textView.setText(“User name:");
editText.setText("");
button1.setText(“OK");
第1行代码的findViewById()函数能够通过ID引
用界面上的任何控件,只要该控件在XML文
件中定义过ID即可
第4行代码的setText()函数用来设置TextView所
显示的内容
15
界面控件- myGUIDemo
◦ 按钮响应点击事件:添加点击事件的监听器
1.
2.
3.
4.
5.
6.
◦
◦
◦
◦
16
button1.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Editable str = editText.getText();
textView.setText(str);
}
});
第1行代码中button1对象通过调用setOnClickListener()函数,注册
一个点击(Click)事件的监听器View.OnClickListener()
第2行代码是点击事件的回调函数
第2行代码得到编辑框中的内容
第4行代码将TextView的显示内容更改为编辑框的内容
界面控件- myGUIDemo
View.OnClickListener()
17
◦
View.OnClickListener()是View定义的点击事件的监听器接口,并在接口中仅定义了onClick()函
数
◦
当Button从Android界面框架中接收到事件后,首先检查这个事件是否是点击事件,如果是点
击事件,同时Button又注册了监听器,则会调用该监听器中的onClick()函数
◦
每个View仅可以注册一个点击事件的监听器,如果使用setOnClickListener()函数注册第二个点
击事件的监听器,之前注册的监听器将被自动注销
◦
多个按钮可以注册到同一个点击事件的监听器上,代码如下
界面控件- myGUIDemo
1. Button.OnClickListener buttonListener = new Button.OnClickListener(){
2.
@Override
3.
public void onClick(View v) {
4.
switch(v.getId()){
5.
case R.id.button1:
6.
textView.setText("Button 1 按钮");
7.
return;
8.
case R.id.button2:
9.
textView.setText(“button 2 按钮");
10.
return;
11.
}
12.
}};
13.
button1.setOnClickListener(buttonListener);
14.
button2.setOnClickListener(buttonListener);
◦
◦
◦
18
第1行至第12行代码定义了一个名为buttonListener的点击事件监
听器
第13行代码将该监听器注册到button1上
第14行代码将该监听器注册到button2上
界面控件- TodoList2
创建新的视图(控件):1)扩展已存在的视图、2)组建复合的控件、
3)创建全新视图。
本示例通过扩展已有控件来实现新视图
创建名为TodoList2的项目,效果展示如下:
19
界面控件- TodoList2
步骤1:创建名为TodoList2的项目,并配置与TodoList项目相同的源码、
资源。
步骤2:创建一个扩展了TextView的新类MyTextView,见
MyTextView.java。包含一个重写的onDraw方法及init方法。
public class MyTextView extends TextView{
public MyTextView(Context context) {
super(context); init();
}
public MyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); init();
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs); init();
}
private void init(){}
public void onDraw(Canvas canvas){super.onDraw(canvas);}
20
界面控件- TodoList2
步骤3:在res/values文件夹下创建一个新的colors.xml资源,并为页面、
边缘、行和文本设置新才颜色值。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="notepad_paper">#AAFFFF99</color>
<color name="notepad_lines">#FF0000FF</color>
<color name="notepad_margin">#90FF0000</color>
<color name="notepad_text">#AA0000FF</color>
</resources>
21
界面控件- TodoList2
步骤4:在dimens.xml资源文件中(若不存在,则在res/values文件夹下
创建dimens.xml文件),为页面边缘的宽度添加新值。
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="notepad_margin">30dp</dimen>
</resources>
22
界面控件- TodoList2
步骤5:利用已定义的资源,完善init方法。
private void init(){
Resources myResource = getResources();
//创建在onDraw方法中使用的画刷
marginPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
marginPaint.setColor(myResource.getColor(R.color.notepad_margin));
linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
linePaint.setColor(myResource.getColor(R.color.notepad_lines));
//获得页面背景色和边缘宽度
paperColor = myResource.getColor(R.color.notepad_paper);
margin = myResource.getDimension(R.dimen.notepad_margin);
}
23
界面控件- TodoList2
步骤6:重写onDraw方法。
public void onDraw(Canvas canvas){
//绘制页面的颜色
canvas.drawColor(paperColor);
//绘制规划的直线
canvas.drawLine(0, 0, getMeasuredHeight(), 0,linePaint);
canvas.drawLine(0, getMeasuredHeight(),getMeasuredWidth(),
getMeasuredHeight(),linePaint);
//绘制边缘
canvas.drawLine(margin, 0, margin, getMeasuredHeight(),linePaint);
//移动文本,让它跨过边缘
canvas.save();
canvas.translate(margin, 0);
//使用TextView渲染文本
super.onDraw(canvas);
canvas.restore();
}
24
界面控件- TodoList2
步骤7:创建新的布局资源todolistitem.xml来指定每条待办事项是如何
显示的,只需要由MyTextView组成即可。
<?xml version="1.0" encoding="utf-8"?>
<com.example.todolist2.MyTextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:scrollbars="vertical"
android:textColor="@color/notepad_text"
android:fadingEdge="vertical"
/>
25
界面控件- TodoList2
步骤8:修改MainActivity类的onCreate方法,使用新的
R.layout.todolistitem布局的引用替换默认的
android.R.layout.simple_list_item_1布局的引用。
final ArrayList<String> todoItems = new ArrayList<String>();
int resID = R.layout.todolistitem;
final ArrayAdapter<String> aa = new ArrayAdapter<String>(this, resID, todoItems);
myListView.setAdapter(aa);
26
界面控件- TodoList2
运行结果:
27
界面布局
界面布局
◦ 界面布局(Layout)是用户界面结构的描述,定义了界面中所
有的元素、结构和相互关系
◦ 声明Android程序的界面布局有两种方法
◦ 使用XML文件描述界面布局
◦ 在程序运行时动态添加或修改界面布局
◦ 用户既可以独立使用任何一种声明界面布局的方式,也可以同
时使用两种方式
28
界面布局
界面布局
◦ 使用XML文件声明界面布局的特点
◦
将程序的表现层和控制层分离
◦
在后期修改用户界面时,无需更改程序的源代码
◦
用户还能够通过可视化工具直接看到所设计的用户界面,有利于加快界面设计的过程,并且
为界面设计与开发带来极大的便利性
29
界面布局
线性布局
◦ 线性布局(LinearLayout)是一种重要的界面布局中,也是经
常使用到的一种界面布局
◦ 在线性布局中,所有的子元素都按照垂直或水平的顺序在界面
上排列
30
◦
如果垂直排列,则每行仅包含一个界面元素
◦
如果水平排列,则每列仅包含一个界面元素
界面布局
框架布局
◦ 框架布局(FrameLayout)是最简单的界面布局,是用来存放
一个元素的空白空间,且子元素的位置是不能够指定的,只能
够放置在空白空间的左上角
◦ 如果有多个子元素,后放置的子元素将遮挡先放置的子元素
◦ 使用Android SDK中提供的层级观察器(Hierarchy Viewer)进一
步分析界面布局
◦ 层级观察器能够对用户界面进行分析和调试,并以图形化的方
式展示树形结构的界面布局
◦ 还提供了一个精确的像素级观察器(Pixel Perfect View),以
栅格的方式详细观察放大后的界面界面
31
界面布局
表格布局
◦ 表格布局(TableLayout)也是一种常用的界面布局,它将屏幕
划分网格,通过指定行和列可以将界面元素添加的网格中
32
◦
网格的边界对用户是不可见的
◦
表格布局还支持嵌套,可以将另一个表格布局放置在前一个表格布局的网格中,也可以在表
格布局中添加其他界面布局,例如线性布局、相对布局等等
界面布局
表格布局
◦ 表格布局效果图
◦ 表格布局示意图
Row 1
Row 2
TextView
EditText
Button
Button
表格布局
33
界面布局
相对布局
◦ 相对布局(RelativeLayout)是一种非常灵活的布局方式,能够
通过指定界面元素与其他元素的相对位置关系,确定界面中所
有元素的布局位置
◦ 特点:能够最大程度保证在各种屏幕类型的手机上正确显示界
面布局
34
界面布局
相对布局
◦ 相对布局示例说明
◦
添加TextView控件(“用户名”),相对布局会将TextView控件放置在屏幕的最上方
◦
然后添加EditText控件(输入框),并声明该控件的位置在TextView控件的下方,相对布局会
根据TextView的位置确定EditText控件的位置
◦
之后添加第一个Button控件(“取消”按钮),声明在EditText控件的下方,且在父控件的最
右边
◦
最后,添加第二个Button控件(“确认”按钮),声明该控件在第一个Button控件的左方,
且与第一个Button控件处于相同的水平位置
35
界面布局
绝对布局
◦ 绝对布局(AbsoluteLayout)能通过指定界面元素的坐标位置,
来确定用户界面的整体布局
◦ 绝对布局是一种不推荐使用的界面布局,因为通过X轴和Y轴确
定界面元素位置后,Android系统不能够根据不同屏幕对界面
元素的位置进行调整,降低了界面布局对不同类型和尺寸屏幕
的适应能力
36
界面布局
绝对布局
◦ 每一个界面控件都必须指定坐标(X,Y),例如“确认”按钮
的坐标是(40,120),“取消”按钮的坐标是(120,120)。
坐标原点(0,0)在屏幕的左上角
37
界面布局
一些创建布局原则:
◦ 避免不必要的嵌套
◦ 避免使用过多的视图
◦ 扩展布局中的每个额外视图都需要时间和资源
◦ 不应超过80个
◦ 避免深层嵌套
◦ 最好不超过10层
◦ 优化布局层次结构以提高效率和消除不必要嵌套十分重要
38
Android菜单
android提供了三种菜单类型,分别为options menu,context
menu,sub menu。
options menu(主菜单、选项菜单)就是通过按菜单键来显示,
context menu(右键菜单、快捷菜单)在view上按上2s后显示,
两种menu都有可以加入子菜单(sub menu),子菜单不能嵌套
子菜单。
android还提供了对菜单项进行分组的功能,可以把相似功能
的菜单项分成同一个组,这样可以通过调用
setGroupCheckable, setGroupEnabled, setGroupVisible来设置菜
单属性,而无须单独设置。
39
菜单
选项菜单(options menu)
◦ 选项菜单是一种经常被使用的Android系统菜单
◦ 打开方式:通过“菜单键”(MENU key)打开
◦ 选项菜单分类
◦
图标菜单(Icon Menu)
◦
扩展菜单(Expanded Menu)
◦ 重载Activity的onCreateOptionMenu()函数,才能够在Android应
用程序中使用选项菜单
◦ 初次使用选项菜单时,会调用onCreateOptionMenu()函数,用
来初始化菜单子项的相关内容
40
◦
设置菜单子项自身的子项的ID和组ID
◦
菜单子项显示的文字和图片等
菜单
子菜单
◦ 子菜单是能够显示更加详细信息的菜单子项
◦ 菜单子项使用了浮动窗体的显示形式,能够更好适应小屏幕的
显示方式
◦ Android系统的子菜单使用非常灵活,可以在选项菜单或快捷
菜单中使用子菜单,有利于将相同或相似的菜单子项组织在一
起,便于显示和分类
◦ 子菜单不支持嵌套
◦ 子菜单的添加是使用addSubMenu()函数实现
41
菜单
快捷菜单(context menu)
◦ 快捷菜单同样采用了动窗体的显示方式,与子菜单的实现方式
相同,但两种菜单的启动方式却截然不同
◦ 启动方式:快捷菜单类似于普通桌面程序中的“右键菜单”,
当用户点击界面元素超过2秒后,将启动注册到该界面元素的
快捷菜单
◦ 使用方法:与使用选项菜单的方法非常相似,需要重载
onCreateContextMenu()函数和onContextItemSelected()函数
◦ onCreateContextMenu()函数主要用来添加快捷菜单所显示的标
题、图标和菜单子项等内容
42
界面事件
在Android系统中,存在多种界面事件,如点击事件、触摸事件、焦点事
件和菜单事件等等
在这些界面事件发生时,Android界面框架调用界面控件的事件处理函数
对事件进行处理
43
处理用户输入:为View设监听器
View.OnClickListener
◦ OnClick()
View.OnLongClickListener
◦ OnLongClick()
View.OnFocusChangeListener
◦ OnFocusChange()
View.OnKeyListener
◦ OnKey()
View.OnTouchListener
◦ OnTouch()
View.OnCreateMenuListener
◦ OnCreateContextMenu()
为View设监听器以处理某一类型事件
44
界面事件
按键事件
◦ 在MVC模型中,控制器根据界面事件(UI Event)类型不同,
将事件传递给界面控件不同的事件处理函数。
45
◦
按键事件(KeyEvent)将传递给onKey()函数进行处理
◦
触摸事件(TouchEvent)将传递给onTouch()函数进行处理
界面事件
按键事件
◦
Android系统界面事件的传递和处理遵循的规则
◦
◦
◦
46
如果界面控件设置了事件监听器,则事件将先传递给事件监听器
如果界面控件没有设置事件监听器,界面事件则会直接传递给界
面控件的其他事件处理函数
即使界面控件设置了事件监听器,界面事件也可以再次传递给其
他事件处理函数
界面事件
按键事件
◦
Android系统界面事件的传递和处理遵循的规则
◦
◦
◦
47
是否继续传递事件给其他处理函数是由事件监听器处理函数的返
回值决定的
如果监听器处理函数的返回值为true,表示该事件已经完成处理
过程,不需要其他处理函数参与处理过程,这样事件就不会再继
续进行传递
如果监听器处理函数的返回值为false,则表示该事件没有完成处
理过程,或需要其他处理函数捕获到该事件,事件会被传递给其
他的事件处理函数
界面事件
按键事件
◦ 以EditText控件中的按键事件为例,说明Android系统界面事件
传递和处理过程,假设EditText控件已经设置了按键事件监听
器
48
◦
当用户按下键盘上的某个按键时,控制器将产生KeyEvent按键事件
◦
Android系统会首先判断EditText控件是否设置了按键事件监听器,因为EditText控件已经设置
按键事件监听器OnKeyListener,所以按键事件先传递到监听器的事件处理函数onKey()中
界面事件
按键事件
◦
事件能够继续传递给EditText控件的其他事件处理函数,完全根据onKey()函数的返回值来确定
◦
如果onKey()函数返回false,事件将继续传递,这样EditText控件就可以捕获到该事件,将按键
的内容显示在EditText控件中
◦
如果onKey()函数返回true,将阻止按键事件的继续传递,这样EditText控件就不能够捕获到按
键事件,也就不能够将按键内容显示在EditText控件中
49
界面事件
按键事件
◦ Android界面框架支持对按键事件的监听,并能够将按键事件
的详细信息传递给处理函数
◦ 为了处理控件的按键事件,先需要设置按键事件的监听器,并
重载onKey()函数
◦ 示例代码如下
1.
2.
3.
4.
5.
6.
50
editText.setOnKeyListener(new OnKeyListener(){
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
//过程代码……
return true/false;
}
界面事件
按键事件
◦
第1行代码是设置控件的按键事件监听器
◦
第3行代码的onKey ()函数中的参数
◦
51
◦
第1个参数view表示产生按键事件的界面控件
◦
第2个参数keyCode表示按键代码
◦
第3个参数keyEvent则包含了事件的详细信息,如按键的重复次数、硬件编码和按键标志
等
第5行代码是onKey ()函数的返回值
◦
返回true,阻止事件传递
◦
返回false,允许继续传递按键事件
界面事件
按键事件
◦ KeyEventTest是一个说明如何处
理按键事件的示例
◦ KeyEventTest用户界面
◦
◦
◦
52
最上方的EditText控件是输入字符
的区域
中间的CheckBox控件用来控制
onKey()函数的返回值
最下方的TextView控件用来显示
按键事件的详细信息,包括按键
动作、按键代码、按键字符、
Unicode编码、重复次数、功能
键状态、硬件编码和按键标志
界面事件
按键事件
◦ 在EditText中,每当任何一个键子按下或抬起时,都会引发按
键事件
◦ 为了能够使EditText处理按键事件,需要使用setOnKeyListener ()
函数在代码中设置按键事件监听器,并在onKey()函数添加按
键事件的处理过程
1.
2.
3.
4.
5.
6.
53
entryText.setOnKeyListener(new OnKeyListener(){
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
int metaState = keyEvent.getMetaState();
int unicodeChar = keyEvent.getUnicodeChar();
String msg = "";
界面事件
按键事件
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
54
msg +="按键动作:" + String.valueOf(keyEvent.getAction())+"\n";
msg +="按键代码:" + String.valueOf(keyCode)+"\n";
msg +="按键字符:" + (char)unicodeChar+"\n";
msg +="UNICODE:" + String.valueOf(unicodeChar)+"\n";
msg +="重复次数:" + String.valueOf(keyEvent.getRepeatCount())+"\n";
msg +="功能键状态:" + String.valueOf(metaState)+"\n";
msg +="硬件编码:" + String.valueOf(keyEvent.getScanCode())+"\n";
msg +="按键标志:" + String.valueOf(keyEvent.getFlags())+"\n";
labelView.setText(msg);
if (checkBox.isChecked())
return true;
else
return false;
}
界面事件
触摸事件
◦ Android界面框架支持对触摸事件的监听,并能够将触摸事件
的详细信息传递给处理函数
◦ 需要设置触摸事件的监听器,并重载onTouch ()函数
1.
2.
3.
4.
5.
6.
touchView.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
//过程代码……
return true/false;
}
第1行代码是设置控件的触摸事件监听器
在代码第3行的onTouch()函数中,第1个参数View表示产生触摸事件的界
面控件;第2个参数MontionEvent表示触摸事件的详细信息,如产生时间、
坐标和触点压力等
◦ 第5行是onTouch()函数的返回值
注:多点触控用onTouchEvent()函数
◦
◦
◦
55
界面事件
触摸事件
◦ TouchEventDemo是一个说明如
何处理触摸事件的示例
◦ TouchEventDemo用户界面
◦
◦
56
浅蓝色区域是可以接受触摸事
件的区域,用户可以在Android
模拟器中使用鼠标点击屏幕,
用以模拟触摸手机屏幕
下方黑色区域是显示区域,用
来显示触摸事件的类型、相对
坐标、绝对坐标、触点压力、
触点尺寸和历史数据量等信息
界面事件
触摸事件
◦ 在用户界面中使用了线性布局,并加入了3个TextView控件
57
◦
第1个TextView(ID为touch_area)用来标识触摸事件的测试区域
◦
第2个TextView(ID为history_label)用来显示触摸事件的历史数据量
◦
第3个TextView(ID为event_label)用来触摸事件的详细信息,包括类型、相对坐标、绝对坐
标、触点压力和触点尺寸
界面事件
触摸事件
◦ 当手指接触到触摸屏、在触摸屏上移动或离开触摸屏时,分别
会引发ACTION_DOWN、ACTION_UP和ACTION_MOVE触摸事件,
而无论是哪种触摸事件,都会调用onTouch()函数进行处理
◦ 事件类型包含在onTouch()函数的MotionEvent参数中,可以通
过getAction()函数获取到触摸事件的类型,然后根据触摸事件
的不同类型进行不同的处理
◦ 为了能够使屏幕最上方的TextView处理触摸事件,需要使用
setOnTouchListener()函数在代码中设置触摸事件监听器,并在
onTouch()函数添加触摸事件的处理过程
58
界面事件
触摸事件
1. touchView.setOnTouchListener(new View.OnTouchListener(){
2.
@Override
3.
public boolean onTouch(View v, MotionEvent event) {
4.
int action = event.getAction();
5.
switch (action) {
6.
case (MotionEvent.ACTION_DOWN):
7.
Display("ACTION_DOWN",event);
8.
break;
9.
case (MotionEvent.ACTION_UP):
10.
int historySize = ProcessHistory(event);
11.
historyView.setText("历史数据量:"+historySize);
12.
Display("ACTION_UP",event);
13.
break;
14.
case (MotionEvent.ACTION_MOVE):
15.
Display("ACTION_MOVE",event);
16.
break;
17.
}
59
界面事件
触摸事件
18.
19.
20.
60
return true;
}
});
◦
第7行代码的Display()是一个自定义函数,主要用来显示触摸事件的详细信息,函数的代码和
含义将在后面进行介绍
◦
第10行代码的ProcessHistory()也是一个自定义函数,用来处理触摸事件的历史数据,后面进
行介绍
◦
第11行代码是使用TextView显示历史数据的数量
界面事件
触摸事件
◦ MotionEvent参数中不仅有触摸事件的类型信息,还触点的坐
标信息,获取方法是使用getX()和getY()函数,这两个函数获取
到的是触点相对于父界面元素的坐标信息。如果需要获取绝对
坐标信息,则可使用getRawX()和getRawY()函数
◦ 触点压力是一个介于0和1之间的浮点数,用来表示用户对触摸
屏施加压力的大小,接近0表示压力较小,接近1表示压力较大,
获取触摸事件触点压力的方式是调用getPressure()函数
◦ 触点尺寸指用户接触触摸屏的接触点大小,也是一个介于0和1
之间的浮点数,接近0表示尺寸较小,接近1表示尺寸较大,可
以使用getSize()函数获取
61
界面事件
触摸事件
◦ Display()将MotionEvent参数参数中的事件信息提取出来,并显
示在用户界面上
1. private void Display(String eventType, MotionEvent event){
2.
int x = (int)event.getX();
3.
int y = (int)event.getY();
4.
float pressure = event.getPressure();
5.
float size = event.getSize();
6.
int RawX = (int)event.getRawX();
7.
int RawY = (int)event.getRawY();
8.
9.
String msg = "";
10.
msg += "事件类型:" + eventType + "\n";
11.
msg += "相对坐标:"+String.valueOf(x)+","+String.valueOf(y)+"\n";
12.
msg += "绝对坐标:"+String.valueOf(RawX)+","+String.valueOf(RawY)+"\n";
13.
msg += "触点压力:"+String.valueOf(pressure)+", ";
14.
msg += "触点尺寸:"+String.valueOf(size)+"\n";
15.
labelView.setText(msg);
16.
}
62
界面事件
触摸事件
◦ 一般情况下,如果用户将手指放在触摸屏上,但不移动,然后
抬起手指,应先后产生ACTION_DOWN和ACTION_UP两个触摸
事件
◦ 但如果用户在屏幕上移动手指,然后再抬起手指,则会产生这
样的事件序列:ACTION_DOWN → ACTION_MOVE →
ACTION_MOVE → ACTION_MOVE → ……→ ACTION_UP
63
界面事件
触摸事件
◦ 在手机上运行的应用程序,效率是非常重要的。如果Android
界面框架不能产生足够多的触摸事件,则应用程序就不能够很
精确的描绘触摸屏上的触摸轨迹
◦ 如果Android界面框架产生了过多的触摸事件,虽然能够满足
精度的要求,但却降低了应用程序效率
◦ Android界面框架使用了“打包”的解决方法。在触点移动速
度较快时会产生大量的数据,每经过一定的时间间隔便会产生
一个ACTION_MOVE事件,在这个事件中,除了有当前触点的
相关信息外,还包含这段时间间隔内触点轨迹的历史数据信息,
这样既能够保持精度,又不至于产生过多的触摸事件。
64
界面事件
触摸事件
◦ 通常情况下,在ACTION_MOVE的事件处理函数中,都先处理
历史数据,然后再处理当前数据
1. private int ProcessHistory(MotionEvent event)
2.
{
3.
int historySize = event.getHistorySize();
4.
for (int i = 0; i < historySize; i++) {
5.
long time = event.getHistoricalEventTime(i);
6.
float pressure = event.getHistoricalPressure(i);
7.
float x = event.getHistoricalX(i);
8.
float y = event.getHistoricalY(i);
9.
float size = event.getHistoricalSize(i);
10.
11.
// 处理过程......
12.
}
13.
return historySize;
14.
}
65
界面事件
触摸事件
◦ 第3行代码获取了历史数据的数量
◦ 然后在第4行至12行中循环处理这些历史数据
◦ 第5行代码获取了历史事件的发生时间
◦ 第6行代码获取历史事件的触点压力
◦ 第7行和第8行代码获取历史事件的相对坐标
◦ 第9行获取历史事件的触点尺寸
◦ 在第14行返回历史数据的数量,主要是用于界面显示
◦ Android模拟器并不支持触点压力和触点尺寸的模拟,所有触
点压力恒为1.0,触点尺寸恒为0.0
◦ 同时Android模拟器上也无法产生历史数据,因此历史数据量
一直显示为0
66
示例-ToDoList3
步骤1:创建ToDoList3项目,扩展ToDoList2
步骤2:把支持菜单功能所需要的包导入到MainActivity类中
import android.view.Menu;
import android.view.MenuItem;
import
android.view.ContextMenu;
步骤3:添加私有静态final变量,为每个菜单项定义唯一的ID
import android.widget.AdapterView;
static final private int ADD_NEW_TODO = Menu.FIRST;
static final private int REMOVE_TODO = Menu.FIRST + 1;
67
示例-ToDoList3
步骤4:重写onCreateOptionsMenu方法来添加两个心的菜单项。
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
super.onCreateOptionsMenu(menu);
// 创建和添加一个新的菜单项
MenuItem itemAdd = menu.add(0, ADD_NEW_TODO, Menu.NONE,
R.string.add_new);
MenuItem itemRem = menu.add(0, REMOVE_TODO, Menu.NONE, R.string.remove);
// 分配图标
itemAdd.setIcon(R.drawable.add_new_item);
itemRem.setIcon(R.drawable.remove_item);
//分配快捷方式
itemAdd.setShortcut('0', 'a');
itemRem.setShortcut(‘1’, ‘r’);
return true;
68
}
示例-ToDoList3
步骤5:重写onCreateContextMenu方法来创建上下文菜单,并修改
onCreate方法来为ListView注册一个上下文菜单。
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.setHeaderTitle("R.string.context_menu_title");
menu.add(0, REMOVE_TODO, Menu.NONE, R.string.remove);
}
protected void onCreate(Bundle savedInstanceState) {
[… 已有的代码]
registerForContextMenu(myListView);
}
69
示例-ToDoList3
步骤6:重写onPrepareOptionsMenu方法来定制菜单项。
private boolean addingNew = false;
public boolean onPrepareOptionsMenu(Menu menu){
super.onPrepareOptionsMenu(menu);
int idx = myListView.getSelectedItemPosition();
String removeTitle = getString(addingNew ? R.string.cancel : R.string.remove);
MenuItem removeItem = menu.findItem(REMOVE_TODO);
removeItem.setTitle(removeTitle);
removeItem.setVisible(addingNew || idx > -1);
return true;
}
70
示例-ToDoList3
步骤7:修改MainActivity类,将如下变量的作用域扩大到onCreate方法之
外,即将这些变量声明放到类作用域中。
private ListView myListView;
private EditText myEditText;
private ArrayList<String> todoItems;
private ArrayAdapter<String> aa;
71
示例-ToDoList3
步骤8:重写onOptionsItemSelected方法来处理活动菜单选择。
public boolean onOptionsItemSelected(MenuItem item){
super.onOptionsItemSelected(item);
int idx = myListView.getSelectedItemPosition();
switch(item.getItemId()) {
case (REMOVE_TODO):{
if(addingNew){cancelAdd();}
else {removeItem(idx);}
return true;
}
case (ADD_NEW_TODO):{addNewItem();return true;}
}
return false;
}
72
示例-ToDoList3
步骤9:重写onContextItemSelected方法来处理上下文菜单选择。
public boolean onContextItemSelected(MenuItem item){
super.onContextItemSelected(item);
switch(item.getItemId()) {
case (REMOVE_TODO):{
AdapterView.AdapterContextMenuInfo menuInfo;
menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
int idx = menuInfo.position;
removeItem(idx);
return true;
}
}
return false;
}
73
示例-ToDoList3
步骤10:实现私有方法。
private void cancelAdd(){
addingNew = false;
myEditText.setVisibility(View.GONE);
}
private void addNewItem(){
addingNew = true;
myEditText.setVisibility(View.VISIBLE);
}
private void removeItem(int idx){
todoItems.remove(idx);
aa.notifyDataSetChanged();
}
74
示例-ToDoList3
步骤11:修改onCreate方法,实现文本框隐藏功能。
myEditText.setOnKeyListener(new OnKeyListener(){
public boolean onKey(View v, int keyCode, KeyEvent event){
if(event.getAction() == KeyEvent.ACTION_DOWN)
if(keyCode == KeyEvent.KEYCODE_DPAD_CENTER){
todoItems.add(0, myEditText.getText().toString());
myEditText.setText(null);
aa.notifyDataSetChanged();
cancelAdd();
return true;
}
return false;
}
});
75
示例-ToDoList3
步骤12:修改activiey_main.xml布局来隐藏文本框,保证UI的一致性。
<EditText
android:id="@+id/myEditText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/init_text“
/>
76
示例-ToDoList3
运行结果:
77
Q&A