[JAVA] Création d'une bibliothèque de routage multifonctionnelle pour Android qui prend également en charge l'élément partagé --MoriRouter

Aperçu

Cela fait quelques années que Material Design a été annoncé et Shared Element est apparu. Pour autant que je puisse voir les applications dans le magasin, je ne vois pas beaucoup d'entre elles installées.

Je voulais le mettre dans le développement auquel je participe plusieurs fois, mais pour une raison quelconque, je suis venu ici sans le mettre.

Cette fois, alors que je développais l'application, je devenais de plus en plus désireux de créer une bibliothèque de routage dont j'étais satisfait et d'utiliser Annotation Processor, alors j'ai décidé de créer le type de routage que je pense être bon. fait.

Je n'ai pas trouvé de bibliothèque qui donne à Shared Element une belle apparence même après avoir suivi Github, alors j'ai décidé de me concentrer sur cela également cette fois.

Livrables

MoriRouter ezgif-3-5ae226e28e.gif

https://github.com/chuross/mori-router

Une bibliothèque qui prend en charge le développement de transitions d'écran à l'aide de ** Fragment ** à l'aide de la génération automatique à l'aide d'annotations.

traits

Comment utiliser

Download --Ajoutez JitPack aux référentiels de build.gradle

repositories {
    maven { url "https://jitpack.io" }
}

--Ajouter aux dépendances

dependencies {
    implementation 'com.github.chuross.mori-router:annotation:x.x.x'
    annotationProcessor 'com.github.chuross.mori-router:compiler:x.x.x' // or kpt
}

De base

Ajoutez simplement l'annotation @ RouterPath au fragment que vous souhaitez utiliser comme transition d'écran

Le nom de @ RouterPath et @ Argument est le nom de la méthode du routeur.

@RouterPath(name = "main")
class MainScreenFragment : Fragment() {

    @Argument
    lateinit var param1: String

    @Argument(name = "hoge", required = false)
    var param3: ArrayList<String> = arrayListOf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        MoriBinder.bind(this) // @Vous pouvez mettre une valeur dans chaque champ d'argument
    }
    ....
}

Lorsque vous construisez ceci, une classe appelée MoriRouter est automatiquement générée et une méthode pour démarrer cet écran y est ajoutée.

Après cela, générez et utilisez un routeur autour de l'activité de base.

val transitionFactory = DefaultTransitionFactory { Fade() } // android.support.transition ou android.Animation de transition

val options = MoriRouterOptions.Builder(R.id.container) //FrameLayout id pour dessiner des transitions d'écran
                .setEnterTransitionFactory(transitionFactory) //Animation commune au démarrage de l'écran
                .setExitTransitionFactory(transitionFactory) //Animation commune à la fin de l'écran
                .build()

val router = MoriRouter(supportFragmentManager, options)

//Une méthode de transition d'écran est automatiquement générée
router.main("required1", 1000) // main(String param1, Integer param2)
    .hoge(arrayListOf("fuga")) // optional value
    .launch() //Lancez MainScreenFragment

router.pop() //Appelez ceci en revenant à l'écran précédent

Le contenu défini dans l'annotation précédemment est généré en tant que nom de méthode tel quel.

Après cela, si nécessaire, passez les paramètres requis pour la transition d'écran et appelez launch à la fin pour exécuter la transition d'écran dans Layout of R.id.container. Pratique: smiley :: v:

Fragment Builder En utilisant @ RouterPath, il est devenu pratique de générer des transitions d'écran. Cependant, en réalité, l'écran peut également être composé de fragments, donc je veux en profiter aussi.

Dans un tel cas, la classe Builder peut être générée automatiquement en ajoutant @ WithArguments au lieu de @ RouterPath.

@WithArguments
class HogeFragment : Fragment() {

    @Argument
    lateinit var hogeName: String
    ....
}

Si vous faites cela, la classe HogeFragmentBuilder sera automatiquement générée, vous pouvez donc la gérer comme suit.

val fragment: HogeFragment = HogeFragmentBuilder(hogeName).build()

Pratique: smiley :: v:

Il n'y a pas de problème même si vous utilisez FragmentArgs ou ʻAutoBundle` car il y a un mérite que vous puissiez unifier la description ici.

Remplacement de l'animation de transition

La transition Entrée / Sortie passée au moment de l'initialisation de MoriRouter est commune à tous les écrans et est utilisée au moment de la transition.

Cependant, il existe des cas où vous souhaitez spécifier une animation dédiée sur un écran spécifique.

Dans un tel cas, il peut être défini en spécifiant ʻoverrideEnterTransitionFactory et ʻoverrideExitTransitionFactory dans @ RouterPath.

@RouterPath(
    name = "main",
    overrideEnterTransitionFactory = MainScreenTransitionFactory::class,
    overrideExitTransitionFactory = MainScreenTransitionFactory::class
)
class MainScreenFragment : Fragment() {

Transition de l'URL vers un écran spécifique

En spécifiant le format de l'url dans @ RouterPath, vous pouvez lancer un écran spécifique via l'URL.

Étant donné que plusieurs formats cibles peuvent être spécifiés, il peut être utilisé correctement avec un schéma personnalisé ou http / https.

@RouterPath(
  name = "second",
  uris = [
    "example://hoge/{hoge_id}/{fuga}",
    "https://example.com/hoge/{hoge_id}/{fuga}"
  ]
)
class SecondScreenFragment : Fragment() {

    @UriArgument(name = "hoge_id")
    var hogeId: Int

    @UriArgument
    var fuga: String

    // @Lors de l'utilisation d'UriArgument@Argument(required=true)Ne peut pas être utilisé
    @Argument(required = false)
    var piyo: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        MoriBinder.bind(this)
    }
}

En utilisant {} dans le format, la valeur peut être obtenue en l'associant au champ de la classe View.

Après cela, il y a une méthode appelée dispatch dans MoriRouter, donc si vous passez Uri là-bas, vous pourrez faire la transition de l'écran.

router.dispatch(Uri.parse("example://hoge/123/test")) // launch SecondScreenFragment (hogeId = 123, fuga=test)
router.dispatch(Uri.parse("https://example.com/hoge/123/test")) // launch SecondScreenFragment (hogeId = 123, fuga=test)

Pratique: smiley :: v:

Prise en charge des éléments partagés

L'implémentation de Shared Element s'animera bien si vous spécifiez le même nom de transition pour la source de transition et la destination de la transition ... J'ai eu un moment où je le pensais.

S'il s'agit d'un modèle simple, cela fonctionnera toujours, mais en réalité, il existe de nombreux modèles qui ne sont pas si faciles.

Dans cette bibliothèque, je voudrais l'introduire car elle est simplifiée au maximum et conçue pour être facile à implémenter.

De base

Tout d'abord, définissez TransitionName sur la source de transition à partir de XML ou de code. ** Assurez-vous que View possède un identifiant **

<YourLayout
    android:id="@+id/your_id" <!-- must have view id -->
    android:transitionName="your_transition_name" />

--Code

ViewCompat.setTransitionName(yourView, "your_transition_name");

Ensuite, définissez la classe de destination de la transition afin qu'elle reçoive SharedElement.

** Assurez-vous de définir l'animation de l'élément partagé dans sharedEnterTransitionFactory et sharedExitTransitionFactory **

Après cela, passez l'ID de la vue de la destination de la transition au bindElement de MoriBinder et vous avez terminé. Assurez-vous que les vues source et destination de la transition ont le même ID.

@RouterPath(
    name = "third",
    sharedEnterTransitionFactory = ThirdScreenSharedTransitionFactory::class,
    sharedExitTransitionFactory = ThirdScreenSharedTransitionFactory::class
)
class ThirdScreenFragment : Fragment() {
   ....
   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        //Spécifiez le même ID que celui spécifié pour l'élément Vue que vous souhaitez partager dans la source de transition
        //Les vues source et destination de la transition ont le même ID
        MoriBinder.bindElement(this, R.id.your_id)
   }
}

Une fois que vous l'avez défini jusqu'à présent, il ne vous reste plus qu'à ajouter l'élément Vue que vous souhaitez partager au moment de la transition d'écran.

router.third()
       .addSharedElement(yourView) //Définissez l'élément de vue que vous souhaitez partager
       .launch()

Après cela, il s'animera en utilisant la TransitionFactory qui est bien spécifiée au moment de la transition.

Lors de son utilisation avec RecyclerView ou ViewPager, il sera décrit en détail dans les notes ci-dessous. Notez que ** TransitionName doit être unique **

Lorsque l'élément partagé change dynamiquement

La destination de la transition peut être un élément partagé vers ViewPager, et une autre vue peut être renvoyée en tant qu'élément partagé à la fin de l'écran.

Dans ces cas, ʻaddSharedElement` ne peut pas être utilisé et doit être mappé manuellement. Cependant, cette bibliothèque génère également automatiquement des classes qui prennent en charge le mappage manuel, c'est donc une victoire facile.

Tout d'abord, définissez le même TransitionName pour les vues source et destination de la transition. Contrairement à la précédente, dans le cas du mappage manuel, il est également nécessaire de savoir comment générer le TransitionName de la source de transition.

Je suis comme ça J'avais envie d'obtenir un préfixe de la source de transition.

ViewCompat.setTransitionName(yourView, "your_transition_name");

Après cela, définissez manualSharedViewNames dans @ RouterPath de la vue de destination de la transition. Il est utilisé comme un nom qui connecte la source et la destination de la transition séparément du nom de la transition. (Un nom différent de TransitionName est bon)

@RouterPath(
    name = "third",
    manualSharedViewNames = ["shared_view_image"],
    sharedEnterTransitionFactory = ThirdScreenSharedTransitionFactory::class,
    sharedExitTransitionFactory = ThirdScreenSharedTransitionFactory::class
)
class ThirdScreenFragment : Fragment() {
   ....

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       val callback = ThirdSharedElementCallBack()
                          .sharedViewImage({ /*Obtenez le fragment actuel à partir du ViewPager et renvoyez la vue dans laquelle vous voulez le SharedElement*/ })

       setEnterSharedElementCallback(callback)
   }
}

Après cela, définissez setEnterSharedElementCallback pour terminer la destination de la transition.

ThirdSharedElementCallBack est un code généré automatiquement qui simplifie le mappage manuel en créant un rappel via celui-ci.

Ensuite, nous définirons la source de la transition

@RouterPath(
    name = "second"
)
class SecondScreenFragment : Fragment() {
   ....

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)

       val callback = ThirdSharedElementCallBack()
                        .sharedViewImage({ /*Processus pour obtenir une vue depuis RecyclerView*/ })

       setExitSharedElementCallback(callback)
   }

   ....

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ....

        // call manualSharedMapping
        router.third().manualSharedMapping(context).launch()
   }
}

Maintenant, définissez setExitSharedElementCallback. SharedElementCallback est le même que le précédent.

Après cela, lors de l'appel du processus de transition, vous pouvez appeler manualSharedMapping au lieu de ʻaddSharedElement`.

Pratique: smiley :: v:

Remarques sur l'élément partagé

Le nom de la transition doit être ** unique en principe ** (je suis accro)

Ceci s'applique bien sûr également à RecyclerView et ViewPager, Même si vous utilisez à nouveau la même vue, si vous utilisez également le nom de la transition, elle ne sera pas animée.

Par exemple, dans le cas de RecyclerView, il est nécessaire de spécifier un TransitionName différent pour chaque position comme transition_view_image_0 transition_view_image_1.

Si vous souhaitez utiliser le même fragment avec RecyclerView dans ViewPager, vous devez le diviser par index ViewPager + index RecyclerView comme transition_view_image_1_1. (Mendo)

Épilogue

Je pense qu'il y a encore beaucoup de choses à modifier, mais je pense que le SharedElement et le code de routage seront plus propres.

J'ai l'intention de continuer à le maintenir, donc je veux continuer à l'améliorer. Je pense qu'il y a encore un bon moyen de le faire, alors je vais peut-être essayer une approche différente.

Si vous lisez l'exemple, vous pourrez peut-être comprendre quelque chose comme «Comment mettre en œuvre ceci?: Penser:».

https://github.com/chuross/mori-router/tree/master/app

Recommended Posts

Création d'une bibliothèque de routage multifonctionnelle pour Android qui prend également en charge l'élément partagé --MoriRouter
Création d'une bibliothèque qui facilite la gestion des préférences partagées Android
LazyBLE Wrapper (a créé une bibliothèque qui rend Android BLE super simple) v0.14
J'ai créé une bibliothèque pour afficher des tutoriels sur Android.
Trouver une bibliothèque Java pour un éventuel réseau Basian