[kotlin] Passer un lambda via NavController.navigate

Passer un lambda via NavController.navigate [kotlin] - Android - Programmation

Marsh Posté le 29-10-2020 à 11:26:52    

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 :
  1. class CustomAdapter() : RecyclerView.Adapter<CustomAdapter.ViewHolder>() {
  2.     private lateinit var onTouch: (result: JSONObject) -> Unit
  3.     [...]
  4.     fun setOnTouch(touchEvent: (result: JSONObject) -> Unit){
  5.         this.onTouch = touchEvent
  6.     }
  7.     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
  8.         val v: View = LayoutInflater.from(parent.context).inflate(mLayout, parent, false)
  9.         v.setOnClickListener { view ->
  10.             if (this::onTouch.isInitialized)
  11.                 if (view.tag is JSONObject) this.onTouch(view.tag as JSONObject)
  12.         }
  13.         return ViewHolder(v)
  14.     }
  15.     [...]
  16. }


 

Code :
  1. class DataListFragment(action: String, id: String): Fragment() {
  2.     private var ad = CustomAdapter(customLayout)
  3.     [...]
  4.     fun setOnTouch(clickEvent: (result: JSONObject) -> Unit){
  5.         this.ad.setOnTouch(clickEvent)
  6.     }
  7.     [...]
  8. }


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

Code :
  1. val bundle = Bundle()
  2. bundle.putString("action","clients" )
  3. bundle.putString("id","1234" )
  4. bundle.put?????? {result: JSONObject ->
  5.    //Traitement de l'enregistrement
  6. }
  7. findNavController(R.id.nav_host_fragment).navigate(R.id.nav_test, bundle)


 
J'ai essayé simplement ceci :
 

Code :
  1. bundle.putSerializable { result: JSONObject ->
  2. } 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...  :heink:  
 
J'ai ensuite créé une data classe de type Parcelable :
 

Code :
  1. @Parcelize
  2. data class ListDataBundle(
  3.     var action: String,
  4.     var id: String,
  5.     var touchEvent: Serializable
  6. ) : Parcelable {}


 
Puis dans mon activité :
 

Code :
  1. bundle.putParcelable("data", ListDataBundle("clients","1234",{ result: JSONObject ->
  2. }  as Serializable))


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

Code :
  1. val monParcelable = this.arguments?.getParcelable("data" ) as ListDataBundle
  2. this.ad.setOnTouch(monParcelable.touchEvent as (result: JSONObject) -> Unit)


 
Mais à chaque fois j'ai cette fichue erreur "IOException writing serializable object"  :kaola:  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  :cry:

Reply

Marsh Posté le 29-10-2020 à 11:26:52   

Reply

Marsh Posté le 03-11-2020 à 08:49:56    

Salut,
 
Continuant à analyser mon problème, j'ai cru comprendre que c'était l'argument de mon lambda (à savoir JSONObject) qui posait problème. Lorsque j'utilise :

Code :
  1. bundle.putParcelable(monDataListFragment)


Il va me sérialiser l'ensemble de mon objet et, lorsqu'il tombe sur mon type JSONObject, il ne sait pas faire car JSONObject n'est pas sérialisable...  :kaola:  
 
Sur le net, certains parlent de convertir le JSONObject en string dans l'objet, puis de le reconvertir en JSONObject par la suite. Mais je trouve ce fonctionnement particulièrement lourd.
 
De plus, mon objet utilise aussi des arguments de type View, et là : impossible de convertir en string...  :cry:

Reply

Marsh Posté le 03-11-2020 à 15:07:18    

Bon ben je me réponds encore à moi-même  :D  
 
J'ai trouvé une solution. En fait j'ai déclaré dans ma classe DataListFragment une interface :

Code :
  1. public interface DataListBundle {
  2.   fun touchEvent (action: String, result: JSONObject)
  3. }


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

Code :
  1. override fun touchEvent(action: String, result: JSONObject) {
  2.    if (action=="clients" ){
  3.       //effectue un traitement dans le cas d'affichage de clients
  4.    } else if (action=="fournisseurs" ){
  5.       //effectue un autre type de traitement s'il s'agit de fournisseurs
  6.    }
  7. }


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 :
  1. if (mContext is DataListFragment.DataListBundle){
  2.    (mContext as DataListFragment.DataListBundle).touchEvent(mAction,view.tag as JSONObject)
  3. }


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

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed