Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Kotlin Discussion :

Passer un lambda via NavController.navigate


Sujet :

Kotlin

  1. #1
    Membre régulier
    Passer un lambda via NavController.navigate
    Bonjour,

    J'ai créé un fragment générique qui se contente d'initialiser une vue (RecyclerView + Adapter). Les données de l'adapter sont récupérées via un webservice que j'attaque par l'intermédiaire de la librairie Volley.

    Sur mon activité principale, j'utilise un NavController et la méthode navigate() afin de naviguer d'une page à une autre (mais toujours au travers du fragment générique). Lors de la navigation, j'envoi un paramètre "action" et un paramètre "id" qui permettront de spécifier auprès du webservice les données à récupérer.

    Ce fonctionnement marche bien.

    Seulement maintenant, il faut que je puisse faire des traitements spécifiques selon les pages où je me trouve. Dans mon fragment, j'ai donc déclaré une méthode "setOnTouch" que j'utilise pour initialiser l'événement qui se produira lorsque l'utilisateur touchera un élément de ma RecyclerView :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class CustomAdapter() : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
        private lateinit var onTouch: (result: JSONObject) -> Unit
     
        [...]
     
        fun setOnTouch(touchEvent: (result: JSONObject) -> Unit){
            this.onTouch = touchEvent
        }
     
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val v: View = LayoutInflater.from(parent.context).inflate(mLayout, parent, false)
            v.setOnClickListener { view ->
                if (this::onTouch.isInitialized)
                    if (view.tag is JSONObject) this.onTouch(view.tag as JSONObject)
            }
            return ViewHolder(v)
        }
     
        [...]
    }


    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class DataListFragment(action: String, id: String): Fragment() {
        private var ad = CustomAdapter(customLayout)
        [...]
     
        fun setOnTouch(clickEvent: (result: JSONObject) -> Unit){
            this.ad.setOnTouch(clickEvent)
        }
        [...]
    }


    Ma question est la suivante : comment passer une méthode (lambda) via la méthode navigate() :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    val bundle = Bundle()
    bundle.putString("action","clients")
    bundle.putString("id","1234")
    bundle.put?????? {result: JSONObject ->
       //Traitement de l'enregistrement
    }
    findNavController(R.id.nav_host_fragment).navigate(R.id.nav_test, bundle)


    J'ai essayé simplement ceci :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    bundle.putSerializable { result: JSONObject ->
    } as Serializable


    Mais ça marche pas... J'ai essayé de créer une classe qui hérite de Bundle, pour créer une propriété "onTouch" mais impossible : la classe Bundle est déclarée par Google comme Final...

    J'ai ensuite créé une data classe de type Parcelable :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    @Parcelize
    data class ListDataBundle(
        var action: String,
        var id: String,
        var touchEvent: Serializable
    ) : Parcelable {}


    Puis dans mon activité :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    bundle.putParcelable("data", ListDataBundle("clients","1234",{ result: JSONObject ->
    }  as Serializable))


    Et dans mon fragment récupérer la fonction comme ceci :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    val monParcelable = this.arguments?.getParcelable("data") as ListDataBundle
    this.ad.setOnTouch(monParcelable.touchEvent as (result: JSONObject) -> Unit)


    Mais à chaque fois j'ai cette fichue erreur "IOException writing serializable object" qui, à mon avis, signifie qu'on ne peut pas sérialiser le type (result: JSONObject) -> Unit...

    Je suis un peu à court d'idées... Certainement dû au fait que je débute en Kotlin et que je suis loin de maitriser encore les principaux concepts...

    De l'aide ne serait pas de refus

  2. #2
    Membre régulier
    Bon ben je me réponds à moi-même

    J'ai trouvé une solution. En fait j'ai déclaré dans ma classe DataListFragment une interface :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    public interface DataListBundle {
      fun touchEvent (action: String, result: JSONObject)
    }


    J'ai ensuite fait hériter mon activité de cette interface afin d'y surcharger les méthodes qu'elle contient :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    override fun touchEvent(action: String, result: JSONObject) {
       if (action=="clients" ){
          //effectue un traitement dans le cas d'affichage de clients
       } else if (action=="fournisseurs" ){
          //effectue un autre type de traitement s'il s'agit de fournisseurs
       }
    }

    Et enfin, dans mon Adapter (auquel j'ai fourni le context de l'activité), j'ai juste à tester si le context hérite de mon interface DataListBundle :

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    if (mContext is DataListFragment.DataListBundle){
       (mContext as DataListFragment.DataListBundle).touchEvent(mAction,view.tag as JSONObject)
    }


    Le tout fonctionne à merveille et ne me semble ni moche ni lourd

###raw>template_hook.ano_emploi###