Fermer

octobre 15, 2025

Migration de Leanback vers Jetpack Compose sur Android TV

Migration de Leanback vers Jetpack Compose sur Android TV


Lorsque Google a introduit Leanback, il a résolu un problème difficile : créer des interfaces utilisateur focalisables et conviviales pour Android TV. Mais Leanback a été construit sur des thèmes Fragments, Presenters et XML – des modèles qui ne s’adapteront pas bien en 2025.

Avec Jetpack Compose pour TV, on obtient enfin :

  • Un modèle de focus déclaratif.
  • Thèmes composables au lieu de remplacements XML.
  • Interface utilisateur réutilisable qui fonctionne sur mobile, tablette et téléviseur.

Mais migrer une application TV de production n’est pas aussi simple que de remplacer un BrowseSupportFragment. Vous avez besoin d’une stratégie.

Cet article est un guide de migration concret, et pas seulement des extraits de code.

Au lieu de tout réécrire d’un coup, pensez bottom-up + modulaire :

  • Extrayez un système de conception → une source de vérité pour les couleurs, la typographie et les dimensions.
  • Remplacez d’abord les composants de la feuille → cartes, boutons, en-têtes.
  • Introduisez Compose dans Leanback (approche hybride).
  • Migrer les écrans un par un → Parcourir → Détails → Rechercher → Lecteur.
  • Changer de couche de navigation en dernier → Fragments → Navigation-Compose.

La thématique Leanback est basée sur XML :

@style/BrowseTitleView
@style/RowHeader
« > » width=»300″ height=»78″ data-mce-src=»https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-34-58-300×78.png”>xml themeCela a fonctionné, mais il était rigide.

Dans Compose-TV, les thèmes sont pilotés par Kotlin :

@Composablefun TvAppTheme(contenu : @Composable () - data-recalc-dims= Unité) { MaterialTheme( colorScheme = darkColorScheme( primaire = Couleur(0xFFE50914), secondaire = Couleur(0xFFB81D24), arrière-plan = Couleur.Noir, surface = Couleur(0xFF121212) ), typographie = Typographie( titreLarge = TextStyle( fontSize = 22.sp, fontWeight = FontWeight.Bold, color = Color.White ), bodyLarge = TextStyle ( fontSize = 18.sp, lineHeight = 24.sp ) ), content = content ) }  » width= »300″ height= »289″ srcset= »https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-41-41-300×289.png 300w, https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-41-41-768×739.png 768w, https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-41-41-624×600.png 624w, https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-41-41-24×24.png 24w, https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-41-41.png 809w » sizes= »(max-width: 300px) 100vw, 300px »/>

composer un thème

✅ Vous contrôlez désormais le thème par écran ou par composant, et pas seulement à l’échelle de l’application.
✅ Facile à expérimenter avec des états de focus et des thèmes d’accessibilité à contraste élevé.

Disons que votre structure actuelle ressemble à ceci :

:application
├── ui (fragments Leanback, présentateurs)
├── données
├── domaine

Vous souhaitez évoluer vers :

:application
├── ui-leanback (fragments hérités, présentateurs)
├── ui-compose (nouveaux écrans Compose)
├── design-system (couleurs, typographie, dimensions)
├── domaine
└── données

  • design-system → Un module Kotlin pur contenant ColorPalette, Typographie, constantes d’espacement. XML et Compose peuvent en consommer.
  • ui-compose → Tous les nouveaux écrans TV dans Compose.
  • ui-leanback → Anciens écrans Leanback jusqu’à ce que la migration soit terminée.

De cette façon, vous pouvez :

  1. Expédier des builds hybrides (Compose + Leanback) en production.
  2. Déployez progressivement les écrans Compose (migration sécurisée).

Leanback ParcourirSupportFragment

class MainFragment : BrowseSupportFragment() { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) title = "Films" headersState = HEADERS_ENABLED val rowsAdapter = ArrayObjectAdapter (ListRowPresenter()) val listRowAdapter = ArrayObjectAdapter (CardPresenter()) listRowAdapter.add (Movie("Interstellaire")) rowsAdapter.add(ListRow(HeaderItem(0, "Science-fiction"), listRowAdapter)) adaptateur = rowsAdapter } }

Leanback ParcourirSupportFragment

Composer l’équivalent

@Composablefun MoviesScreen(films : List<Movie data-recalc-dims=) { TvLazyColumn { item { Text( text = "Science-fiction"style = MaterialTheme.typography.titleLarge, modifier = Modifier.padding(16.dp) ) } item { TvLazyRow { items(movies) { movie -> FocusableMovieCard(movie) } } } } } @Composable fun FocusableMovieCard(movie: Movie) { var focus by Remember { mutableStateOf(false) } Card( modifier = Modificateur .size(180.dp, 240.dp) .focusable() .onFocusChanged { focus = it.isFocused } .border( width = if (focused) 3.dp else 0.dp, color = if (focused) Color.Yellow else Color.Transparent ), onClick = { /* naviguer */ } ) { Text(movie.title, Modifier.padding(8.dp)) } }  » width= »303″ height= »429″ srcset= »https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-47-56-212×300.png 212w, https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-47-56-624×882.png 624w, https://www.tothenew.com/blog/wp-ttn-blog/uploads/2025/09/Screenshot-from-2025-09-09-16-47-56.png 670w » sizes= »(max-width: 303px) 100vw, 303px »/>

composer un exemple

🎯 Différence :

  1. Aucun présentateur.
  2. Aucun fragment passe-partout.
  3. Contrôle total sur l’interface utilisateur de focus.

Leanback a géré la concentration comme par magie, mais souvent de manière rigide.

Dans Compose-TV :

  • Utilisez Modifier.focusable() pour tous les éléments interactifs.
  • Regroupez les éléments avec FocusGroup pour une meilleure navigation.
  • Utilisez onPreviewKeyEvent pour les remplacements DPAD personnalisés (par exemple, sauter des sections).

Modifier.onPreviewKeyEvent { keyEvent ->
si (keyEvent.key == Key.DirectionDown) {
// Gérer la navigation personnalisée
vrai
} Sinon Faux
}

Leanback a été optimisé pour les grandes rangées. Dans Composer :

Utilisez LazyRow / LazyColumn pour la virtualisation.
Combinez avec Paging 3 pour un défilement infini :

val films = pager.collectAsLazyPagingItems()

ParesseuxRow {
éléments (films.itemCount) { index ->
films[index]?.let { MovieCard(it) }
}
}

Test sur des appareils réels : l’émulateur ne reflète pas toujours la véritable latence DPAD.

  • Conflits de thèmes : si les fragments Leanback utilisent toujours des thèmes XML, assurez-vous que les surfaces Compose sont encapsulées dans TvAppTheme.
  • Boucles de mise au point : la mise au point de la composition n’est pas 1:1 avec Leanback ; testez les « cas extrêmes » comme la navigation en fin de ligne.
  • Applications hybrides : n’essayez pas de tout réécrire en même temps. Un ComposeView dans Leanback est votre ami pendant la transition.
  • Extrayez le thème + le système de conception dans le module partagé.
  • Migrez les composants feuilles (cartes, boutons).
  • Démarrez l’hybride : utilisez ComposeView dans Leanback.
  • Remplacez les lignes/fragments écran par écran.
  • Basculez la navigation vers Composer en dernier.
  • Supprimez la dépendance Leanback.

La migration d’une application Android TV de Leanback vers Jetpack Compose n’est pas seulement une migration d’interface utilisateur. C’est une ré-architecture :

  1. Des thèmes XML → aux thèmes Composable.
  2. Du code lourd en fragments → à la navigation basée sur l’état.
  3. Des modèles rigides → aux mises en page flexibles.
  4. Faites-le progressivement, en commençant par les modules inférieurs et le système de conception partagé. À la fin, vous disposerez d’un système plus propre, moderne et évolutif.
  5. Application TV plus facile à maintenir et évolutive.

VOUS TROUVEZ CECI UTILE ? PARTAGEZ-LE






Source link