视频课:https://edu.csdn.net/course/play/7621

学习内容

Ø Android中的手势识别

Ø Android中的语音朗读

能力目标

Ø 熟练掌握如何通过手势缩放图片

Ø 掌握手势文件的创建方法及技巧

Ø 熟练掌握如何通过手势输入字符串

Ø 熟练掌握如何通过手势调用程序

本章简介

输入输出一直是手机等小型设备的一个弱项,以手机为例,因为键盘很小,用户无法像操作PC机键盘一样操作手机键盘。因此利用触摸屏及话筒等设备实现输入输出应运而生,比如IPhone中的siri技术。本章中讲到的手势是一个非常流行的输入技术,在使用ie浏览器的时候大家会发现借助插件我们可以通过简单的手势操作完成很多原本很复杂的功能,甚至在一些最近出现的界面比较绚的ios、win8等操作系统中默认都原生对手势提供了很好的支持,当然Android系统也不例外。另外Android系统还提供了TTS技术,通过此技术可以让手机以说话的方式输出内容给使用者。在本章节中我们将通过具体的案例详细讲解Android中手势的识别、自定义手势等知识,最后还给大家讲解了语音朗读相关的知识。

核心技能部分

6.1 手势识别

当前的Android手机中,输入设备主要以屏幕上的模拟键盘居多,这种设计初衷是为了减少手机重量,同时为用户提供更大的屏幕显示界面,然而这么做的弊端也同时产生,在本就不大的手机屏幕上,往往既要显示虚拟键盘,同时还要显示应用程序的常用按钮,同时还要有一大部分空间需要预留给内容显示区域,这样手机界面就会显得比较拥挤,而手势操作则可以解决这种问题,使用手势操作,我们就无需在界面中为各个功能添加相应的按钮,这样也就解决了挤占屏幕空间的问题。

手势(Gesture)指的是用户手指或触摸笔在触摸屏幕上的连续触碰行为,比如大家经常用到的通过在屏幕上滑动出几何图形来打开指定应用程序,就是一个最简单的手势的应用。手势非常类似于手写输入,只是通过手势可以完成很多手写输入无法完成的工作。

6.1.1 通过手势缩放图片

在图片查看过程中,很多时候我们需要将图片全屏显示,以最大限度地利用我们有限的屏幕区域,同时我们还经常需要将图片放大缩小等功能来查看图片各个位置的具体细节或图片的整体效果,而如果通过添加两个按钮来完成此功能相对来说比较简单,但按钮本身却会占用屏幕空间,遮挡住屏幕中某些位置,这样的处理方式就不是很理想,而如果通过简单的手势操作来实现图像的放大缩小则会比较合理。

我们经常使用的Google地图中,当我们要查看某一详细信息时就可以通过缩放组件对其进行控制,如下图6.1.1所示。但这些缩放方式太“传统”了,它利用的原理基本上和利用Matrix借助SeekBar组件实现一样。下面我们通过手势实现只需要在图片上随意地挥动手指就能实现图片缩放的功能。

图6.1.1 Google地图

使用Android手势检测的步骤:

Ø 创建一个GestureDetector对象,创建该对象时必须实现一个GestureDetector.OnGestureListener监听器实例。

Ø 为Activity中指定的组件的TouchEvent事件绑定监听器,在事件处理中指定将TouchEvent事件交给GestureDetector处理。

示例6.1

根据用户手势进行图片的缩放,当手指从左向右挥动时图片被放大,从右向左挥动时图片被缩小,挥动速度越快,缩放比越大。

本程序的实现思路比较简单:使用一个GestureDetector来检测用户的手势,并根据用户手势在水平方向上的速度来缩放图片。

布局文件比较简单,只在屏幕中间提供了一个id为的ImageView组件,注意,为了保证图片在放大缩小时不变形,这里要给该ImageView组件的scaleType属性设置值为fitXY,以保持图像的长宽比。

Activity类代码如下:

public class ZoomPicActivity extends Activity {

private GestureDetector detector;// 定义手势检测器实例

private ImageView imageView;

private Bitmap bitmap;// 初始的图片资源

private int width;// 定义图片的宽

private int height;// 定义图片的高

private float currentScale = 1;// 记录当前的缩放比

private Matrix matrix;// 控制图片缩放的Matrix对象

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.zoompic);

detector = new GestureDetector(new GestureListener());// 创建手势检测器

imageView = (ImageView) findViewById(R.id.pic);

matrix = new Matrix();

// 获取被缩放的源图片

bitmap = BitmapFactory.decodeResource(this.getResources(), R.drawable.mm);

width = bitmap.getWidth();// 获得位图宽度

height = bitmap.getHeight();// 获得位图高度

// 设置ImageView初始化时显示的图片。

imageView.setImageBitmap(BitmapFactory.decodeResource(this.getResources(), R.drawable.mm));

}

@Override

public boolean onTouchEvent(MotionEvent me) {

// 将该Activity上的触碰事件交给GestureDetector处理

return detector.onTouchEvent(me);

}

// //

class GestureListener implements OnGestureListener {

@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) {

// 用户按下屏幕并拖动,相当于windows 的mouse_move

return false;

}

@Override

public void onLongPress(MotionEvent e) {

// 用户长按屏幕

}

@Override// 用户按下屏幕,快速移动后松开(就是在屏幕上滑动)

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

velocityX = velocityX > 4000 ? 4000 : velocityX;

velocityX = velocityX < -4000 ? -4000 : velocityX;

// 根据手势的速度来计算缩放比,如果velocityX>0,放大图像,否则缩小图像。

currentScale += currentScale * velocityX / 4000.0f;

// 保证currentScale不会等于0

currentScale = currentScale > 0.01 ? currentScale : 0.01f;

// 重置Matrix

matrix.reset();

// 缩放Matrix

matrix.setScale(currentScale, currentScale, 160, 200);

BitmapDrawable tmp = (BitmapDrawable) imageView.getDrawable();

// 如果图片还未回收,先强制回收该图片

if (!tmp.getBitmap().isRecycled())

{

tmp.getBitmap().recycle();

}

// 根据原始位图和Matrix创建新图片

Bitmap bitmap2 = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);

// 显示新的位图

imageView.setImageBitmap(bitmap2);

return true;

}

}

}

代码中的GestureDetector类的实例代表了一个手势检测器,在创建它时需要传递一个GestureDetector.OnGestureListener的监听器实例,这个监听器用来对用户的手势动作进行监听。在它里面包含好几个处理事件的方法,这里重点讲解onFling()方法,它的语法为:

语法:

public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)

当用户在屏幕上拖动时触发该方法,其中velocityX、velocityY表示拖动动作在横向、纵向上的速度。

程序运行结果如下图6.1.2、6.1.3、6.1.4所示。

图6.1.2 正常显示效果

图6.1.3 放大显示效果

图6.1.4 缩小显示效果

6.1.2 创建手势文件

开发过程中,我们需要通过识别不同的手势完成不同的操作,而Android系统中也同样支持开发者自定义手势以提供个性化的操作。

在使用手势之前,需要建立一个手势库文件,在识别手势时,需要装载这个手势库文件,并通过手势库文件中的描述来识别当前手势。

在Android示例程序中自带了个名为GestureBuilder的程序,运行该程序会显示如图6.1.5所示的界面。单击【Add gesture】按钮就可以手动添加一个手势。在添加手势界面上方的文本框中输入一个手势名(在识别手势后,系统会返回该名称),然后在下方的空白处随意画一些手势轨迹,如下图6.1.6所示。

图6.1.5 Gestures Builder界面

图6.1.6 添加手势

在添加完手势,单击【Done】按钮之后,会创建一个手势,屏幕显示如下图6.1.8所示。

图6.1.7 新建手势

打开DDMS试图,我们会在模拟器的SD卡中看到多了一个名为gestures的文件,如下图6.1.7所示,这个文件就是手势库文件。

图6.1.8 SD卡手势文件

6.1.3 通过手势输入字符串

手势的一个重要应用就是通过在屏幕上简单地画几笔以实现输入复杂的内容的功能。这会在很大程序上解决小型设备输入不方便的问题。

示例6.2

通过手势输入字符串。要求当我们在屏幕上画出如下图6.1.9形后,系统会自动匹配在6.1.2节中所创建的手势。松开鼠标后,会将识别后的信息以Toast信息提示框的形式显示,如图6.1.10所示。

图6.1.9 画手势

图6.1.10 程序运行结果

首先将6.1.2节中创建的gesture文件从Eclipse中导出,然后在本项目的res目录下面创建一个名为raw的文件夹,之后将刚导出的文件放到这一目录中。本示例中装载的手势文件是放在res/raw目录下的,不过,我们也可以将手势文件放在SD卡或手机内存中。

本示例中界面上绘制手势的组件是android.gesture.GestureOverlayView,该组件不是标准的android组件,在XML布局文件中定义该组件时必须使用命名。布局文件的详细代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical" >

<android.gesture.GestureOverlayView

android:id="@+id/gestures"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:gestureStrokeType="multiple" />

</LinearLayout>

其中属性android:gestureStrokeType表示GestureOverlayView组件是否可接受多个手势。如果将该属性设置为multiple,表示可以绘制由多个不连续的图形组成的手势,比如由两个交叉斜线组成的乘号。如果将该属性设置为single,绘制手势时就只能使用一笔画了(中间不能断线)。这有些像手写输入,对于大部分汉字来说,都是由不连续的的笔画组成的(连笔子除外),就需要由多个手势来绘制一个汉字。

在Activity类代码中,需要把GestureOverlayView组件绑定一个OnGesturePerformedListener监听器,该监听器在用户手势绘制完成后触发onGetsturePerformed事件,该事件的Gesture参数代表用户输入的手势,一般在这个事件中处理手势的识别和调用对应的操作。

Activity代码如下:

public class DrawStringActivity extends Activity implements OnGesturePerformedListener {

private GestureLibrary gestureLibrary;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.drawstring);

gestureLibrary = GestureLibraries.fromRawResource(this, R.raw.gestures);

if (gestureLibrary.load()) {

setTitle("手势文件装载成功");

GestureOverlayView gestureOverlayView =

(GestureOverlayView) findViewById(R.id.gestures);

gestureOverlayView.addOnGesturePerformedListener(this);

} else {

setTitle("手势文件装载失败");

}

}

@Override

public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {

//从手势库中识别手势

ArrayList<Prediction> lists = gestureLibrary.recognize(gesture);

if (lists.size() > 0) {

StringBuilder sb = new StringBuilder();

int n = 0;

for (int i = 0; i < lists.size(); i++) {

Prediction prediction = lists.get(i); //得到当前手势

if (prediction.score > 1.0) {

//prediction.score>1.0代表该手势与输入手势匹配

sb.append("score:" + prediction.score + " \n name:"

+ prediction.name + "\n");

n++;

}

}

sb.insert(0, n + "个相匹配的手势.\n");

Toast toast =  Toast.makeText(this, sb.toString(), Toast.LENGTH_SHORT);

toast.setGravity(Gravity.CENTER_HORIZONTAL|Gravity.TOP, 0, 40);

toast.show();

}

}

}

在Activity类的onCreate()方法中我们首先完成了手势文件的装载,之后为GestureOverlayView组件指定了OnGestureListener事件。在事件处理代码中我们需要注意,在匹配信息中有一个score字段,该字段表示匹配程序,即用户绘制的手势和手势库中手势的相似性。一般当该字段的值大于1时就可认为与手势匹配。如果有多个手势可能匹配我们绘制的手势,可以提供一个选择列表,以便用户可以准确地选择匹配结果。这有些像手写输入,大多数时候都会出现一个可能匹配的列表,最终由用户决定哪个是最终的匹配结果。

6.1.4 通过手势调用程序

学过了前面的知识之后,有过智能手机使用经验的同学一定会问到,在Android中我们如何像在IPhone中一样通过手势调用自己的应用程序呢?其实我们只需要在onGesturePerformed方法中获得手势名,并按照一定的规则就可以调用其它应用程序了。下面我们通过一个具体的示例来给大家演示。

示例6.3

通过手势调用程序。通过手势来拨打电话,显示通话记录和自动输入电话号。

本示例使用6.1.2节中创建的手势文件及6.1.3节中定义的布局文件,限于篇幅,此处不再赘述。我们重点来学习在Activity类中如何通过手势调用其它应用,Activity类代码如下:

public class CallProgramActivity extends Activity implements OnGesturePerformedListener {

private GestureLibrary gestureLibrary;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.drawstring);

gestureLibrary = GestureLibraries.fromRawResource(this, R.raw.gestures);

if (gestureLibrary.load()) {

setTitle("手势文件装载成功(识别动作).");

GestureOverlayView gestureOverlayView = (GestureOverlayView) findViewById(R.id.gestures);

gestureOverlayView.addOnGesturePerformedListener(this);

} else {

setTitle("手势文件装载失败.");

}

}

@Override

public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {

ArrayList<Prediction> predictions = gestureLibrary.recognize(gesture);

if (predictions.size() > 0) {

int n = 0;

for (int i = 0; i < predictions.size(); i++) {

Prediction prediction = predictions.get(i);

if (prediction.score > 1.0) {

Intent intent = null;

Toast.makeText(this, prediction.name,

Toast.LENGTH_SHORT).show();

if ("newString".equals(prediction.name)) {

intent = new Intent(Intent.ACTION_CALL,

Uri.parse("tel:1234"));

}

if (intent != null)

startActivity(intent);

n++;

break;

}

}

if (n == 0)

Toast.makeText(this, "没有符合要求的手势.",

Toast.LENGTH_SHORT).show();

}

}

}

因为本示例使用到了系统拨打电话的应用,所以需要要功能清单文件中加入相应的权限,具体代码如下:

<uses-permission android:name="android.permission.CALL_PHONE">

运行程序,当在屏幕上输入正确的手势时,程序会在弹出Toast提示之后,打开系统拨打电话的程序。当在屏幕上输入手势不正确时,会提出一个没有匹配手势的Toast提示。

 6.2 语音朗读

在6.1节中我们学习了如何通过手势来实现快捷输入,但仅仅拥有方便的信息输入是远远不够的,如果能够让手机根据文本读出输入的内容就更人性化了。Android系统提供的TTS(Text To Speech)技术就可以完成这个工作,Android的自动朗读支持可以对指定的文本内容进行朗读,从而发出声音,不仅如此,它还可以将文本对应的音频录制成音频文件,方便以后播放。

虽然借助TTS,可以在应用程序中动态地增加音频输出,从而改善用户体验,但是遗憾的是目前TTS还没有提供对中文的支持。

TTS技术的核心是android.speech.tts.TextToSpeech类。使用TTS技术朗读文本的步骤如下:

(1) 创建TextToSpeech类的对象,创建时传入OnInitListener监听器监听创建是否成功。

(2) 设置Textrn使用的语言、国家选项,通过返回值判断TTS是否支持该语言、国家选项。

(3) 调用speak()或synthesizeToFile()方法开始朗读。

(4) 关闭TTS,回收资源。

示例6.4

创建语音读程序,能够使用TTS来朗读用户输入的文本内容。

程序最终运行效果如下图6.1.5所示,其中上方是一个id为editText的文本输入框,用户可以通过这个输入框输入想要朗读的内容;下方是一个id为button的按钮,当我们单击【朗读文本】按钮时,模拟器会将我们在EditText中输入的内容以语音的形式读出来。程序运行效果如下图6.1.11所示。

图6.1.11 TTS效果图

Activity类代码如下:

public class TTSActivity extends Activity implements TextToSpeech.OnInitListener {

private TextToSpeech tts;

private EditText editText;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.tts);

tts = new TextToSpeech(this, this);

Button button = (Button) findViewById(R.id.button);

editText = (EditText) findViewById(R.id.editText);

button.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

tts.speak(editText.getText().toString(), TextToSpeech.QUEUE_FLUSH, null);

}

});

}

@Override

public void onInit(int status) {

if (status == TextToSpeech.SUCCESS) {

//指定当前TTS系统所支持的语言及国家

int result = tts.setLanguage(Locale.US);

if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {

Toast.makeText(this, "Language is not available.",

Toast.LENGTH_SHORT).show();

}

}

}

}

本示例程序中用到了如下重要的方法:

Ø TextToSpeech(Context context, OnInitListener listener)

构造函数在创建TextToSpeech对象时,必须得提供一个OnInitListener监听器来监听TextToSpeech的初始化结果。本例使用Activity自身实现了OnInitListener这个接口。

Ø int TextToSpeech.speak(String text, int queueMode, HashMap<String, String> params)

用来把Text内容转换为音频。其中params用于指定声音转换时的参数,该参数的取值参看下表6-1-1所示

表6-1-1 params取值及含意

取值

含意

QUEUE_FLUSH

当TTS调用speak方法时,它会中断当前实例正在运行的任务。

QUEUE_ADD

当TTS调用speak方法时,会把新的发音任务添加到当前发音任务队列之后。

任务实训部分

1:通过手势打开照相和录音功能

训练技能点

通过手势调用系统程序

需求说明

照相和录音是手机中使用相当频繁的功能,利用它们可以把日常生活中的精彩部分以图片或声音的形式保留下来。本实训任务要实现的功能是当用户在手机屏幕上画出不同的图形时调用不同的应用,比如当用户画“对号”时调用照相功能、当用户画“圆形”时调用“录音”功能。

实现步骤

(1) 创建手势文件;

(2) 自行查阅调用系统相机及录音程序所要使用的Intent的详细信息;

(3) 结合6.1.4节内容编写Activity类实现手势识别;

2:读取用户接收到的短信

训练技能点

Ø TTS语音朗读

Ø 状态栏通知

需求说明

短信的收发是用户手机使用时最经常用到的功能,本实训任务要实现的功能是当用户接收到新短信后,首先给用户一个振动提示,并在状态栏显示出短信到来的信息。当用户单击短信时手机会以语音的形式将短信的内容读取出来。

实现步骤

(1) 为用户接收短信后,增加状态栏提醒功能;

(2) 为用户单击状态栏短信后添加事件响应:调用TTS完成短信信息的读取。

3:用户自定义手势 (选做)

训练技能点

建立手势文件的原理

需求说明

在本章中我们创建手势文件用的是模拟器自带的名为“gestures builder”的程序,其实这个程序的源代码在SDK中可以找到,具体位置在android-sdk\samples\android-8目录下面。本示例要求以这个项目为基础创建一个新的项目,然后研究其源代码。在创建该项目时的注意事项,如下图6.2.1和图6.2.2所示:

图6.2.1 从样例中创建项目

图6.2.2 选择GestureBuilder项目

巩固练习

一、简答题

1. 什么是手势识别技术?

2. 简述使用TTS技术朗读文本的步骤。

二、上机练习

编写一个语音朗读程序,能读取SD卡下指定文件。然后再编写一个手势识别程序,当用户在屏幕上画出某个图形时实现对上述朗读程序的调用。

android 特色输入输出相关推荐

  1. android入门基础视频教程特色输入输出

    视频课:零基础学安卓Android移动开发 学习内容 Ø Android中的手势识别 Ø Android中的语音朗读  能力目标 Ø 熟练掌握如何通过手势缩放图片 Ø 掌握手势文件的创建方法及技巧 Ø ...

  2. Android特色开发之Google MAP

    本文节选于机械工业出版社推出的<Android应用开发揭秘>一书,作者为杨丰盛.本书内容全面,详细讲解了Android框架.Android组件.用户界面开发.游戏开发.数据存储.多媒体开发 ...

  3. Android特色开发之账户管理

    本文节选于机械工业出版社推出的<Android应用开发揭秘>一书,作者为杨丰盛.本书内容全面,详细讲解了Android框架.Android组件.用户界面开发.游戏开发.数据存储.多媒体开发 ...

  4. Android学习之BottomNavigationBar实现Android特色底部导航栏

    Android底部导航栏的实现方式特别多,例如TabHost,TabLayout,或者TextView等,都可以实现底部导航栏的效果,但是却没有Google官方统一的导航栏样式,今天讲的就是Googl ...

  5. Android特色开发(3):Google Map

    Google Map(Google地图) 参考书籍:<Android应用开发揭秘>第9章 Google Map概述 Google Map是 Google 公司提供的电子地图服务,包括局部详 ...

  6. android目录结构

    Android系统原理及开发要点详解 第1章 Android系统概述 第2章 Android系统开发综述 第3章 Android的Linux内核与驱动程序 第4章 Android的底层库和程序 第5章 ...

  7. Android的API与差异化之路

              Android的API与差异化之路                 发挥Android特色:框架API和开源(开放)     Android平台就如同×××长城般,两岸的硬件业厂商 ...

  8. 第一行代码 Android (郭霖 著)

    https://github.com/guolindev/booksource 第1章 开始启程----你的第一行Android代码 (已看) 第2章 先从看得到的入手----探究活动 (已看) 第3 ...

  9. Android中文API(134) —— Account

    前言 本章内容是android.account.Account,版本为Android 4.0 r1,翻译来自"张嵩",欢迎访问他的博客:"http://xiaoy.sin ...

最新文章

  1. Android 添加菜单项
  2. 【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )
  3. mysql之 double write 浅析
  4. Paper之Algorithms:国内外Algorithms高质量论文、CUMCM分类推荐(建议收藏,持续更新)
  5. 微信公众号接口添加菜单时错误(errcode:40017 invalid button type)
  6. 分析大数据对思维方式有何影响?了解大数据的特点、来源与数据呈现方式
  7. 使用 Swift 在 iOS 10 中集成 Siri —— SiriKit 教程(Part 1)
  8. 中国四丁基尿素(TBU)市场趋势报告、技术动态创新及市场预测
  9. Linux中的Java类,Java基础入门学习-Java中类的属性
  10. c语言数字代码,小O的数字 (C语言代码)
  11. Python学习---综合练习之Craps赌博游戏
  12. FlashPaper 2 API 中文版
  13. U盘只能读,不能写,不能删,也不能格式化的处理
  14. java赵云主角兵器谱游戏_赵云赵子龙的外号有哪些?赵云的武器是什么 赵
  15. Spring事件发布机制
  16. 单电源运放滤波器设计
  17. 10年百度的T6,现在应该挣几两银子?
  18. 综述:如何构建交通领域的基于图的深度学习架构
  19. python客户端开发自行车租赁系统_Python实战—自行车租赁数据分析
  20. php拼接循环拼接字符串数组,PHP数组拼接

热门文章

  1. 在知乎引发众多分布式数据库大佬争相回答的问题
  2. logstash使用中遇到的问题
  3. 【最新合集】研究生工程伦理课程答案整理
  4. 小学计算机考查方案,宋家塘街道中心学校2020年理化生实验操作和信息技术考试方案...
  5. android手机定位
  6. 如何判断Socket连接失效
  7. android dialog 结构,Android 原生Dialog实现
  8. linux nfs 修复文件,linux nfs Read-only file system
  9. python爬虫文字全是乱码_pythone爬虫编码自适应 解决网页乱码
  10. python输入多个数据存入列表_python怎么把input的值储存到一个列表