{"id":17054,"date":"2025-10-30T19:10:50","date_gmt":"2025-10-30T17:10:50","guid":{"rendered":"https:\/\/www.beseit.net\/?page_id=17054"},"modified":"2025-11-03T11:27:45","modified_gmt":"2025-11-03T09:27:45","slug":"com-substituir-mainactivity-kt-duna-app-compose-per-una-basada-en-xml-i-texttospeech","status":"publish","type":"page","link":"https:\/\/www.beseit.net\/?page_id=17054","title":{"rendered":"Com substituir MainActivity.kt d\u2019una app Compose per una basada en XML i TextToSpeech (codi)"},"content":{"rendered":"\n<p>S\u00ed, <strong>\u00e9s possible substituir<\/strong> el <code>MainActivity.kt<\/code> que et va generar Android Studio per una versi\u00f3 que <strong>no utilitzi Jetpack Compose<\/strong> i que en canvi faci servir <strong>vistes cl\u00e0ssiques (XML)<\/strong> com al codi Java que proposes \u2014 per\u00f2 caldr\u00e0 fer <strong>alguns canvis addicionals<\/strong> al projecte, perqu\u00e8 ara est\u00e0 configurat per funcionar amb <strong>Jetpack Compose<\/strong>, i el teu codi Java fa servir <strong>AppCompatActivity + XML<\/strong>.<\/p>\n\n\n\n<p>A continuaci\u00f3 t\u2019explico <strong>pas a pas<\/strong> qu\u00e8 has de fer per fer aquest canvi de forma neta i funcional.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">? Objectiu<\/h3>\n\n\n\n<p>Passar d\u2019un projecte <strong>Compose<\/strong> (modern) a un projecte <strong>basat en XML i AppCompatActivity<\/strong> (cl\u00e0ssic), per poder usar el codi Java que tens (o la seva versi\u00f3 en Kotlin).<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Passos necessaris<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. <strong>Canvia la classe base de <code>MainActivity<\/code><\/strong><\/h3>\n\n\n\n<p>El teu codi Java hereta de <code>AppCompatActivity<\/code>, per\u00f2 el projecte actual usa <code>ComponentActivity<\/code> (per Compose).<br>Aix\u00f2 est\u00e0 b\u00e9, per\u00f2 caldr\u00e0 <strong>afegir la depend\u00e8ncia d\u2019AppCompat<\/strong> si no hi \u00e9s.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. <strong>Afegeix les depend\u00e8ncies d\u2019AppCompat (si no hi s\u00f3n)<\/strong><\/h3>\n\n\n\n<p>Obre el fitxer <strong><code>app\/build.gradle.kts<\/code><\/strong> (o <code>build.gradle<\/code> si \u00e9s en Groovy) i assegura\u2019t que tens:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">dependencies {\n    implementation(\"androidx.appcompat:appcompat:1.7.0\")\n    implementation(\"com.google.android.material:material:1.12.0\")\n    \/\/ ... altres depend\u00e8ncies\n}<\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Si ja tens aquestes l\u00ednies, perfecte. Si no, afegeix-les i fes <strong>Sync Now<\/strong>.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">3. <strong>Crea el fitxer de disseny XML<\/strong><\/h3>\n\n\n\n<p>El teu codi Java fa servir <code>setContentView(R.layout.activity_main)<\/code> i <code>findViewById<\/code>, aix\u00ed que necessites un fitxer XML.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Crea <code>res\/layout\/activity_main.xml<\/code>:<\/h4>\n\n\n\n<p>Dins de <code>app\/src\/main\/res\/layout\/<\/code>, crea (o substitueix) el fitxer:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?>\n&lt;LinearLayout\n    xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:padding=\"16dp\">\n\n    &lt;EditText\n        android:id=\"@+id\/editText\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:hint=\"Escriu aqu\u00ed...\"\n        android:inputType=\"textMultiLine\"\n        android:lines=\"3\" \/>\n\n    &lt;Button\n        android:id=\"@+id\/btnSpeak\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Parla!\" \/>\n\n&lt;\/LinearLayout><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">4. <strong>Reescriu <code>MainActivity.kt<\/code> en Kotlin (versi\u00f3 moderna del teu Java)<\/strong><\/h3>\n\n\n\n<p>Pots mantenir Kotlin (recomanat) i fer el mateix que el teu Java, per\u00f2 en Kotlin:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">package com.example.mytest\n\nimport android.os.Bundle\nimport android.speech.tts.TextToSpeech\nimport android.widget.Button\nimport android.widget.EditText\nimport android.widget.Toast\nimport androidx.appcompat.app.AppCompatActivity\nimport java.util.Locale\n\nclass MainActivity : AppCompatActivity() {\n\n    private lateinit var tts: TextToSpeech\n    private lateinit var editText: EditText\n    private lateinit var btnSpeak: Button\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n\n        editText = findViewById(R.id.editText)\n        btnSpeak = findViewById(R.id.btnSpeak)\n\n        tts = TextToSpeech(this) { status ->\n            if (status == TextToSpeech.SUCCESS) {\n                val result = tts.setLanguage(Locale(\"ca\", \"ES\"))\n                if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {\n                    Toast.makeText(this, \"\u26a0\ufe0f Veu catalana no disponible\", Toast.LENGTH_LONG).show()\n                }\n            }\n        }\n\n        btnSpeak.setOnClickListener {\n            val text = editText.text.toString()\n            if (text.isNotEmpty()) {\n                tts.speak(text, TextToSpeech.QUEUE_FLUSH, null, null)\n            }\n        }\n    }\n\n    override fun onDestroy() {\n        tts.stop()\n        tts.shutdown()\n        super.onDestroy()\n    }\n}<\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>? Nota: El <code>package<\/code> ha de ser <code>com.example.mytest<\/code> (el del teu projecte), <strong>no<\/strong> <code>com.exemple.ttscatala<\/code>.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">5. <strong>Elimina o comenta el codi de Compose<\/strong><\/h3>\n\n\n\n<p>Ja no necessites:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>enableEdgeToEdge()<\/code><\/li>\n\n\n\n<li><code>setContent { ... }<\/code><\/li>\n\n\n\n<li><code>@Composable<\/code>, <code>Scaffold<\/code>, etc.<\/li>\n<\/ul>\n\n\n\n<p>El nou <code>MainActivity.kt<\/code> <strong>no fa servir res de Compose<\/strong>, aix\u00ed que pots esborrar tot el codi anterior i posar aquest de dalt.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">6. <strong>(Opcional) Elimina depend\u00e8ncies de Compose<\/strong><\/h3>\n\n\n\n<p>Si <strong>no vols usar Compose en absolut<\/strong>, pots eliminar aquestes l\u00ednies del <code>build.gradle.kts<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">implementation(\"androidx.activity:activity-compose:1.9.2\")\nimplementation(\"androidx.compose.ui:ui:1.6.8\")\nimplementation(\"androidx.compose.ui:ui-tooling-preview:1.6.8\")\nimplementation(\"androidx.compose.material3:material3:1.2.1\")<\/pre>\n\n\n\n<p>I tamb\u00e9 pots esborrar la carpeta <code>ui.theme<\/code> si no la necessites.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u26a0\ufe0f Per\u00f2 si nom\u00e9s vols provar aquesta app simple, pots deixar-les; no passa res.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Resultat<\/h3>\n\n\n\n<p>Amb aquests canvis:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Tindr\u00e0s una app cl\u00e0ssica amb <strong>EditText + Bot\u00f3<\/strong>.<\/li>\n\n\n\n<li>Parlar\u00e0 en <strong>catal\u00e0<\/strong> (si el dispositiu ho suporta).<\/li>\n\n\n\n<li>No usar\u00e0 Jetpack Compose.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p>Vols que t\u2019ajudi a convertir aquesta app perqu\u00e8 <strong>parli autom\u00e0ticament en obrir-se<\/strong> (com en la teva idea inicial de \u201cHello World\u201d que parla)?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>S\u00ed, \u00e9s possible substituir el MainActivity.kt que et va generar Android Studio per una versi\u00f3 que no utilitzi Jetpack Compose i que en canvi faci servir vistes cl\u00e0ssiques (XML) com al codi Java que proposes \u2014 per\u00f2 caldr\u00e0 fer alguns &hellip; <a href=\"https:\/\/www.beseit.net\/?page_id=17054\">Continua llegint <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":3167,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"categories":[],"class_list":["post-17054","page","type-page","status-publish","has-post-thumbnail","hentry"],"_links":{"self":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/pages\/17054","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=17054"}],"version-history":[{"count":2,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/pages\/17054\/revisions"}],"predecessor-version":[{"id":17071,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/pages\/17054\/revisions\/17071"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=\/wp\/v2\/media\/3167"}],"wp:attachment":[{"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=17054"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.beseit.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=17054"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}