Desarrollo en Android T14 - Utilizar TextoToSpeech TTS
El sistema operativo Android cuenta con una opción de accesibilidad llamada TTS por sus siglas en ingles TextToSpeech, que permite a los usuarios “leer” los textos de pantalla, ya sea para utilizarlos en los lectores de libros para convertirlos en audio-libros, o leer los mensajes que llegan al sistema, o ayudar a las personas con problemas de visión a poder acceder de manera más fácil a las opciones del smartphone.
Este sistema tiene características especiales, empezando por la entonación al momento de leer, es decir un texto en ingles se pronunciará de forma extraña si el teléfono esta configurado en español o en frances, así que una de las configuraciones esenciales es el idioma en que se quieren leer los textos.
Otra configuración importante es identificar si el sistema tiene instalado los paquetes de voz para trabajar “offline” es decir sin necesidad de conectarse a Internet, o en caso de no tenerlos descargados poder conectarse a Internet para reproducir el texto.
1. Para este tutorial se crea un nuevo proyecto utilizando la plantilla Blank.
2. La interfaz será similar a la mostrada en la siguiente imagen.
3.- En la interfaz se agrega una TextView y un EditText, que será donde se escribirá el texto que se desea leer, el código se verá como el siguiente.
xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="mx.blogspot.salvadorhm.leertexto.MainActivity"
tools:showIn="@layout/activity_main">
<TextView
android:id="@+id/tvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Escribe un texto" />
<EditText
android:id="@+id/etText"
android:inputType="textAutoCorrect"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="mx.blogspot.salvadorhm.leertexto.MainActivity"
tools:showIn="@layout/activity_main">
<TextView
android:id="@+id/tvText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Escribe un texto" />
<EditText
android:id="@+id/etText"
android:inputType="textAutoCorrect"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
4. Para hacer uso de TextToSpeech se importa la librería del mismo nombre, con lo que se puede acceder a la función de accesibilidad de Android.
import android.speech.tts.TextToSpeech;
5. Una vez importada la librería se crea un objeto que será el encargado de iniciar el TTS y reproducir el sonido.
private TextToSpeech tts;
6. Adicional se crea un objeto para el EditText, para el FloatingActionButton que se encargará de ejecutar la acción de convertir el texto a sonido, y un Toolbar para mostrar un ActionBar.
public class MainActivity extends AppCompatActivity{
private TextToSpeech tts;
private EditText etText;
private FloatingActionButton fab;
private Toolbar toolbar;
private TextToSpeech tts;
private EditText etText;
private FloatingActionButton fab;
private Toolbar toolbar;
7. En el método onCreate del Activity se inician los objetos antes creados, teniendo atención especial en el objeto tts con el que se crea un TextToSpeech y se le asigna un listener llamado onInitListener que se creará en el siguiente punto.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TextToSpeech(this,onInitListener);
etText = (EditText)findViewById(R.id.etText);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(onClickListener);
}
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TextToSpeech(this,onInitListener);
etText = (EditText)findViewById(R.id.etText);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(onClickListener);
}
8. Para crear el OnInitListener se usa la función de Android Studio para completar código Alt+Enter, con lo que se creará el listener onInitListener, que se pasa como parámetro al objeto tts creado anteriormente, este listener tiene como función iniciar el TTS y configurar el idioma deseado, para esto se compara la variable status, para verificar que está función esta habilitada y funcional en el smartphone.
Una ves que se realizo la verificación se configura el idioma para la entonación utilizando el método setLanguage, en este caso se utiliza Local.getDefault, para usar como base el idioma en el que esta configurado el smartphone, aunque es posible especificar el idioma que se desea usar.
TextToSpeech.OnInitListener onInitListener = new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS){
int result = tts.setLanguage(Locale.getDefault());
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED){
Log.d("TTS", "Idimoa no soportado");
}else{
Log.d("TTS", "TTS configurado correctamente");
}
}else{
Log.d("TTS","Voz no inicializada");
}
}
};
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS){
int result = tts.setLanguage(Locale.getDefault());
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED){
Log.d("TTS", "Idimoa no soportado");
}else{
Log.d("TTS", "TTS configurado correctamente");
}
}else{
Log.d("TTS","Voz no inicializada");
}
}
};
9. De la misma forma que el punto anterior se sobrescribe el método onDestroy, este método se encarga se detener la reproducción de audio y “apagar” el sintetizador al cerrar la aplicación, o en caso de alguna falla.
@Override
protected void onDestroy() {
if(tts != null){
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
protected void onDestroy() {
if(tts != null){
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
10. A continuación se crea el método speak(), en el que se recibe el texto que el usuario escribió en el EditText de la interfaz gráfica, y utilizando el método speak se convierte el texto a audio, este método recibe 3 parámetros, el primero es el texto, el segundo es el método par reproducir el audio, en este caso la opción QUEQUE_FLUSH cancela cualquier reproducción en curso y reproduce el nuevo texto.
Este método es obsoleto a partir de la API 21, pero sigue siendo funcional con estos parámetros.
private void speak(){
String text = etText.getText().toString();
tts.speak(text,TextToSpeech.QUEUE_FLUSH,null);
}
String text = etText.getText().toString();
tts.speak(text,TextToSpeech.QUEUE_FLUSH,null);
}
11. En el método onClickListener se atrapa el evento click y cuando este evento lo produce el botón flotante llama al método speak creado en el paso anterior y reproduce el texto.
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if(v == fab)
speak();
}
};
@Override
public void onClick(View v) {
if(v == fab)
speak();
}
};
12. El código completo queda como se muestra a continuación:
package mx.blogspot.salvadorhm.leertexto;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import java.util.Locale;
public class MainActivity extends AppCompatActivity{
private TextToSpeech tts;
private EditText etText;
private FloatingActionButton fab;
private Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TextToSpeech(this,onInitListener);
etText = (EditText)findViewById(R.id.etText);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(onClickListener);
}
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if(v == fab)
speak();
}
};
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
import java.util.Locale;
public class MainActivity extends AppCompatActivity{
private TextToSpeech tts;
private EditText etText;
private FloatingActionButton fab;
private Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tts = new TextToSpeech(this,onInitListener);
etText = (EditText)findViewById(R.id.etText);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(onClickListener);
}
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if(v == fab)
speak();
}
};
private void speak(){
String text = etText.getText().toString();
tts.speak(text,TextToSpeech.QUEUE_FLUSH,null);
}
TextToSpeech.OnInitListener onInitListener = new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS){
int result = tts.setLanguage(Locale.getDefault());
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED){
Log.d("TTS", "Idimoa no soportado");
}else{
Log.d("TTS", "TTS configurado correctamente");
}
}else{
Log.d("TTS","Voz no inicializada");
}
}
};
@Override
protected void onDestroy() {
if(tts != null){
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
String text = etText.getText().toString();
tts.speak(text,TextToSpeech.QUEUE_FLUSH,null);
}
TextToSpeech.OnInitListener onInitListener = new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS){
int result = tts.setLanguage(Locale.getDefault());
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED){
Log.d("TTS", "Idimoa no soportado");
}else{
Log.d("TTS", "TTS configurado correctamente");
}
}else{
Log.d("TTS","Voz no inicializada");
}
}
};
@Override
protected void onDestroy() {
if(tts != null){
tts.stop();
tts.shutdown();
}
super.onDestroy();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Comentarios