如何为需要电影列表的RecyclerView.Adapter提供数据?

时间:2018-05-13 04:15:54

标签: android android-recyclerview kotlin recycler-adapter

我是一名初学者,正在尝试使用tmdb API和Kotlin构建电影数据库应用。到目前为止,我一直在按照教程来获取基本想法,但由于我的应用程序不同,我需要自己实现逻辑。

在我的适配器onBindViewHolder()方法中,我将base_URL字符串与我的poster_path连接并将其传递给Picasso,以使用来自JSON数据的图像填充我的RecyclerView。就是这样:

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
    }

如您所见,海报路径是从数据类(movieData.moviePoster)调用的。我能够这样做,因为这是我的适配器的构造函数:

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

但现在在我的MainActivity onCreate()方法中,将适配器附加到RecyclerView的调用需要相同的构造函数。

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()
    }
...
}

我尝试在MainActivity的构造函数中调用相同的Movies对象,但它抛出了一个错误,因为它期望MainActivity的一个空构造函数。

如何创建一个对象以传递到MainActivity中的适配器参数?我尝试在全局创建一个,但它强迫我将其初始化为null

Movies.kt(根据要求)

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
    }
}

1 个答案:

答案 0 :(得分:1)

  

我尝试在我的构造函数中调用相同的Movies对象   MainActivity但它因为期待空而引发错误   MainActivity的构造函数。

活动与您想要做的事情无关。你声明你的适配器是这样的:

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

这意味着当您创建实例时,它需要您在构造函数中传递Movies对象。可以从Web(在您的情况下是电影API),某些磁盘文件,数据库中获取此Movies对象,或者您甚至可以手动创建它:

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

如何创建列表对象以传递到Kotlin中的Adapter参数

更改适配器以使用电影列表:

// 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?) {}
}

然后在您的MainActivity.onCreate()中,您将拥有:

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

要获取数据,我建议您使用Retrofit,有很多关于如何使用该库的指南。