2 votes

Comment fournir des données à un adaptateur RecyclerView.Adapter nécessitant une liste de films ?

Je suis un développeur débutant qui essaie de construire une application de base de données de films en utilisant l'API tmdb et Kotlin. Jusqu'à présent, j'ai suivi un tutoriel pour obtenir l'idée de base, mais comme mon application est différente, je dois mettre en œuvre la logique moi-même.

Dans mon adaptateur onBindViewHolder() je concatène la chaîne base_URL avec mon poster_path et je passe cela dans Picasso pour remplir mon RecyclerView avec des images provenant des données JSON. Voilà, c'est fait :

override fun onBindViewHolder(holder: PosterHolder, position: Int) {
        Picasso
                .get()
                .load("" + R.string.base_URL + "" + movieData.moviePoster)
                .into(holder.imageView)

        holder.view.movie_poster?.scaleType = ImageView.ScaleType.FIT_CENTER
    }

Comme vous pouvez le voir, le chemin d'affichage est appelé à partir de la classe de données ( movieData.moviePoster ). Je peux le faire parce que c'est le constructeur de mon adaptateur :

class PosterAdapter(val movieData: Movies) : RecyclerView.Adapter<PosterHolder>(){...}

mais maintenant, dans l'activité principale de mon activité onCreate() l'appel pour attacher l'adaptateur au RecyclerView nécessite le même constructeur.

class MainActivity() : AppCompatActivity(), LoaderManager.LoaderCallbacks<List<Movies>> {
    ...

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val recyclerView = findViewById<RecyclerView>(R.id.recycler_view);
        recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerView.layoutManager = GridLayoutManager(this, 3);
        recyclerView.adapter = PosterAdapter() <- this is it

        runLoaders()
    }
...
}

J'ai essayé d'appeler le même Movies dans le constructeur de mon activité principale, mais une erreur s'est produite car le constructeur de l'activité principale devait être vide.

Comment créer un objet à passer dans les paramètres de mon adaptateur dans la MainActivity ? J'ai essayé d'en créer un globalement mais cela m'a obligé à l'initialiser en tant que null .

Movies.kt (par demande)

data class Movies(val movieTitle: String,
                  val moviePoster: String,
                  val overview: String,
                  val ratings: Int,
                  val releaseDate: String)

PosterAdapter.kt

class PosterAdapter(val movieData: Movies) : RecyclerView.Adapter<PosterHolder>(){
    val movieList = mutableListOf<Movies>()

    override fun getItemCount(): Int { return movieList.size }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PosterHolder{
        val layoutInflater = LayoutInflater.from(parent?.context)
        val listItem = layoutInflater.inflate(R.layout.list_item, parent, false)
        return PosterHolder(listItem)
    }

    override fun onBindViewHolder(holder: PosterHolder, position: Int) {
        Picasso
                .get()
                .load("" + R.string.base_URL + "" + movieData.moviePoster)
                .into(holder.imageView)

        holder.view.movie_poster?.scaleType = ImageView.ScaleType.FIT_CENTER
    }
}

class PosterHolder(val view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
    var imageView: ImageView? = null

    fun PosterHolder(view: View){ this.imageView = view.findViewById<View>(R.id.movie_poster) as ImageView }

    override fun onClick(p0: View?) {}
}

QueryUtils.kt

object QueryUtils {

    private val logTag = QueryUtils::class.java.simpleName

    fun fetchMovieData(requestURL: String): List<Movies>? {
        val url = createURL(requestURL)

        var jsonResponse : String? = null

        try{
            jsonResponse = getResponseFromHttpURL(url)
        }catch (e: IOException){
            Log.e(logTag, "Problem making the HTTP request", e)
        }
        return extractFeatureFromJSON(jsonResponse)
    }

    private fun createURL(requestURL: String): URL? {
        var url : URL? = null

        try{
            url = URL(requestURL)
        }catch (e: MalformedURLException){
            Log.e(logTag, "Problem building the URL", e)
        }
        return url
    }

    @Throws(IOException::class)
    private fun getResponseFromHttpURL(url: URL?): String? {
        //first, open the connection using the url object that was created in createURL method.
        //the "as" keyword casts "urlConnection" as HttpURLConnection.
        val urlConnection = url?.openConnection() as HttpURLConnection?

        try {
            if(urlConnection?.responseCode == HttpURLConnection.HTTP_OK){
                //The if statement runs if the connection is successful.

                //We're retrieving all the data from the input stream after the connection.
                val inputStream = urlConnection.inputStream

                val scanner = Scanner(inputStream)
                scanner.useDelimiter("\\A")

                if(scanner.hasNext()){
                    return scanner.next()
                }

            }else{
                //Throw a log message in case of a faulty response code.
                Log.e(logTag, "Error response code: " + urlConnection?.responseCode)
            }

        }finally {
            //end the connection to reclaim the resources to prevent memory leaks
            urlConnection?.disconnect()
        }

        return null
    }

    private fun extractFeatureFromJSON(jsonResponse: String?): List<Movies>? {
        if(jsonResponse == null || jsonResponse.isEmpty()){
            return null
        }

        val movies = mutableListOf<Movies>()

        try{
            val baseJSONresponse = JSONObject(jsonResponse)
            val titleJSON = baseJSONresponse.getString("original_title")
            val posterJSON = baseJSONresponse.getString("poster_path")
            val overviewJSON = baseJSONresponse.getString("overview")
            val ratingsJSON = baseJSONresponse.getInt("vote_average")
            val releaseDateJSON = baseJSONresponse.getString("release_date")

            movies.add(Movies(titleJSON, posterJSON, overviewJSON, ratingsJSON, releaseDateJSON))

        }catch (e: JSONException){
            Log.e(logTag, "Problem parsing JSON results", e)
        }

        return movies
    }
}

1voto

Luksprog Points 52767

J'ai essayé d'appeler le même objet Movies dans le constructeur de mon fichier MainActivity mais une erreur s'est produite parce qu'il s'attendait à un constructeur vide vide pour la MainActivity.

L'activité n'a rien à voir avec ce que vous voulez faire. Vous avez déclaré votre adaptateur comme ceci :

class PosterAdapter(val movieData: Movies) : RecyclerView.Adapter<PosterHolder>(){

ce qui signifie que lorsque vous créez une instance, vous devez passer un objet Movies dans le constructeur. Cet objet Movies peut être récupéré sur le web (dans votre cas, l'API des films), sur un fichier disque, dans une base de données ou vous pouvez même le créer manuellement :

val dummyMovie = Movies("Dummy title", "Dummy movie poster", "Overview", 5, "Release date")
recyclerView.adapter = PosterAdapter(dummyMovie)

Comment créer un objet liste à passer en paramètre de l'adaptateur en Kotlin

Changez votre adaptateur pour utiliser une liste de films :

// the adapter will need a Context so we are going to pass one when we create the adapter
class PosterAdapter(private val context: Context) : RecyclerView.Adapter<PosterHolder>() {
    private val inflater: LayoutInflater by lazy { LayoutInflater.from(context) }
    private var movies = listOf<Movies>()// there's no need to expose this list 

    override fun getItemCount(): Int = movies.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PosterHolder = PosterHolder(inflater.inflate(R.layout.list_item, parent, false))      

    override fun onBindViewHolder(holder: PosterHolder, position: Int) {
        //make sure this results in a proper string

Picasso.get()
 .load("${context.getString(R.string.base_URL)}${movies[position].moviePoster}")
            .into(holder.imageView)
        holder.imageView.scaleType = ImageView.ScaleType.FIT_CENTER
    }

   // because fetching data from an API takes time, we will use this method to update the adapter with data when we actually get it
   fun update(movies: List<Movies>) {
      this.movies = movies
      notifyDataSetChanged()
   }
}

class PosterHolder(view: View) : RecyclerView.ViewHolder(view), View.OnClickListener {
    val imageView: ImageView = view.findViewById(R.id.movie_poster)

    override fun onClick(p0: View?) {}
}

alors dans votre MainActivity.onCreate() vous aurez :

recyclerView.adapter = PosterAdapter(this)
// fetch data from API and call update() on the adapter

Pour obtenir les données, je vous recommande d'utiliser Rétrofit il y a beaucoup de guides sur la façon d'utiliser la bibliothèque.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X