Perfecte — et dono una guia pas a pas (en català) per crear una app Android (no Compose) anomenada tts_cat que fa servir TextToSpeech. La UI serà basada en XML i tindrà:
- Un contenidor (usaré
ConstraintLayout) amb un camp de text (EditText) amb text precarregat en català. - 2 botons: Parlar i Parar.
- Lògica a
MainActivity.ktper inicialitzar TTS, parlar el text i aturar la parla. - Maneig de la vida del TTS (alliberament a
onDestroy) i comprovació si falta la veu catalana (i acció per instal·lar dades de TTS).
Segueix aquests passos i copia/enganxa els fitxers.
1) Crear el projecte a Android Studio
File > New > New Project.- Escull Empty Activity (no Compose).
- Nom:
tts_cat - Language: Kotlin
- Minimum SDK: API 21+ (recomanat)
- Finalitza i deixa que Android Studio generi el projecte.
2) build.gradle (Module: app)
Assegura’t que tens dependències bàsiques (Android Studio normalment ja les posa). Exemple rellevant:
android {
compileSdk 34
defaultConfig {
applicationId "com.example.tts_cat"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
}
// no cal activar Compose
}
dependencies {
implementation "androidx.core:core-ktx:1.10.1"
implementation "androidx.appcompat:appcompat:1.6.1"
implementation "com.google.android.material:material:1.9.0"
}
(Si Android Studio usa versions diferents, deixa les que proposa; el més important: no fer servir dependències de Compose.)
3) Layout XML: res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/rootLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:padding="16dp">
<!-- Contenidor (equivalent "div") -->
<LinearLayout
android:id="@+id/container"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="12dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="?attr/selectableItemBackground">
<!-- EditText amb text precarregat en català -->
<EditText
android:id="@+id/etText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Introdueix text..."
android:inputType="textMultiLine"
android:minLines="3"
android:gravity="top|start"
android:text="Hola! Aquest és un missatge de prova en català." />
<!-- Botons en fila -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="12dp">
<Button
android:id="@+id/btnSpeak"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Parlar" />
<Button
android:id="@+id/btnStop"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:text="Parar" />
</LinearLayout>
</LinearLayout>
<!-- Opcional: text d'estat a la part de baix -->
<TextView
android:id="@+id/tvStatus"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="Estat: inactiu"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Nota: he posat un
LinearLayoutdinsConstraintLayoutper crear el “contenidor” que demanaves (equivalent d’un<div>). L’EditTextarriba amb text precarregat.
4) MainActivity.kt (Kotlin) — lògica completa TTS
Copia aquest fitxer (modifica el package per adaptar-lo a applicationId si cal):
package com.example.tts_cat
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.speech.tts.TextToSpeech
import android.speech.tts.TextToSpeech.Engine
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.util.Locale
class MainActivity : AppCompatActivity(), TextToSpeech.OnInitListener {
private var tts: TextToSpeech? = null
private lateinit var etText: EditText
private lateinit var btnSpeak: Button
private lateinit var btnStop: Button
private lateinit var tvStatus: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// bind views
etText = findViewById(R.id.etText)
btnSpeak = findViewById(R.id.btnSpeak)
btnStop = findViewById(R.id.btnStop)
tvStatus = findViewById(R.id.tvStatus)
// Inicialitza TextToSpeech
tts = TextToSpeech(this, this)
btnSpeak.setOnClickListener {
val text = etText.text.toString().ifBlank { "Hola! Aquest és un missatge de prova en català." }
speakText(text)
}
btnStop.setOnClickListener {
stopSpeaking()
}
}
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
// Intentem establir català (Catalonia, Espanya)
val localeCa = Locale("ca", "ES")
val result = tts?.setLanguage(localeCa)
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
// La veu catalana no està disponible
tvStatus.text = "Estat: Català no disponible, s'utilitza idioma per defecte."
Toast.makeText(this, "Veu catalana no disponible. Pots instal·lar dades TTS.", Toast.LENGTH_LONG).show()
// Prova d'obrir instal·lador de dades TTS (pot permetre descarregar veus)
try {
val installIntent = Intent(Engine.ACTION_INSTALL_TTS_DATA)
startActivity(installIntent)
} catch (e: Exception) {
// Si no és suportat, només fem servir l'idioma per defecte
tts?.language = Locale.getDefault()
}
} else {
// Tot OK: català disponible
tvStatus.text = "Estat: TTS inicialitzat (català)."
// Opcional: parla automàticament en obrir l'app
speakText("Hola Android! Benvingut a l'aplicació.")
}
} else {
tvStatus.text = "Estat: Error al inicialitzar TTS."
Toast.makeText(this, "No s'ha pogut inicialitzar TextToSpeech.", Toast.LENGTH_SHORT).show()
}
}
private fun speakText(text: String) {
if (tts == null) return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// id d'utt
tts?.speak(text, TextToSpeech.QUEUE_FLUSH, null, "UTTERANCE_ID")
} else {
@Suppress("DEPRECATION")
tts?.speak(text, TextToSpeech.QUEUE_FLUSH, null)
}
tvStatus.text = "Estat: parlant..."
}
private fun stopSpeaking() {
tts?.stop()
tvStatus.text = "Estat: aturat"
}
override fun onDestroy() {
tts?.stop()
tts?.shutdown()
super.onDestroy()
}
}
Explicació ràpida del codi
- La
MainActivityimplementaTextToSpeech.OnInitListener. tts = TextToSpeech(this, this)inicialitza el motor.onInitcomprova si el català (Locale("ca","ES")) està suportat. Si no hi és, intenta obrir l’instal·lador de dades TTS (Engine.ACTION_INSTALL_TTS_DATA).speakText(...)cridatts.speak(...). Per API >= 21 s’usa el paràmetreutteranceId.stopSpeaking()atura la parla.- A
onDestroytanquem i alliberem recursos.
5) Strings (opcional)
Pots posar els textos a res/values/strings.xml si vols:
<resources>
<string name="app_name">tts_cat</string>
<string name="hello_default">Hola! Aquest és un missatge de prova en català.</string>
<string name="btn_speak">Parlar</string>
<string name="btn_stop">Parar</string>
</resources>
I a l’XML utilitzar @string/….
6) Provar l’app
- Executa l’aplicació en un dispositiu físic (recomanat) o un emulador amb suport TTS.
- Si no s’escolta català, Android normalment et deixarà descarregar paquets de veu a Configuració → Accessibilitat / Entrada i Idioma → Sortida de text a veu (pot variar segons fabricant). També el codi obrirà el flux d’instal·lació de dades TTS.
- El botó Parlar llegirà el text del
EditText. El botó Parar aturarà la parla.
7) Millores opcionals (per si vols ampliar)
- Afegir un spinner per triar idioma (ca, es, en) dins la mateixa Activity.
- Guardar l’últim text escrit amb
SharedPreferences. - Afegir controls de velocitat (
setSpeechRate) i to (setPitch) per l’usuari. - Gestionar esdeveniments d’
UtteranceProgressListenerper actualitzar UI quan acaba la parla.
Exemple ràpid per ajustar velocitat:
tts?.setSpeechRate(1.0f) // 1.0 és normal, 0.5 més lent, 2.0 més ràpid tts?.setPitch(1.0f) // 1.0 normal
Si vols, ara:
- Et faig el codi amb selector d’idiomes dins la mateixa
MainActivity.kt; o - Et dono un fitxer
activity_main.xmlamb una mica més d’estil; o - T’explico com instal·lar la veu catalana des del dispositiu pas a pas.
Quina d’aquestes prefereixes que fem ara? (si vols, faig directament el selector d’idiomes dins la mateixa Activity)