Kotlin:如何从Web服务获取数据并在应用程序中显示?

时间:2019-08-18 10:34:48

标签: api kotlin android-recyclerview retrofit2

您好:)我正在开发一款应用,该应用可以显示热门电影以及每部电影的一些详细信息。我创建了一个RecyclerView,其中应显示信息。我一直在获取和显示数据。我正在使用https://www.themoviedb.org/页的api。

我正在按照以下步骤操作:http://imakeanapp.com/make-a-movies-app-using-tmdb-api-part-4-networking-using-retrofit-library/,但是它是用Java编写的,我需要用Kotlin编写代码。我自己转换了部分代码,这就是我的经验:

在Movie.kt

data class Movie (
    @SerializedName("id") val id: Int,
    @SerializedName("title") val title: String,
    @SerializedName("poster_path") val posterPath: String,
    @SerializedName("release_date") val releaseDate: String,
    @SerializedName("vote_average") val rating: Float
)

在MoviesResponse.kt

data class MoviesResponse (
    @SerializedName("page") val page: Int,
    @SerializedName("total_results") val totalResults: Int,
    @SerializedName("results") val movies: List<Movie>,
    @SerializedName("total_pages") val totalPages: Int
)

在TMDbApi.kt

interface TMDbApi {
    @GET("movie/popular")

    fun getPopularMovies (
        @Query("api_key") apiKey: String,
        @Query("language") language: String,
        @Query("page") page: Int
    ): Call<List<MoviesResponse>>
}

在OnGetMoviesCallback.kt

interface OnGetMoviesCallback {
    fun onSuccess(movies: List<Movie>)
    fun onError()
}

在MainAdapter.kt

class MainAdapter: RecyclerView.Adapter<CustomHolder>(){

    var movies: List<Movie> = listOf()

    fun MainAdapter(movies: List<Movie>) {
        this.movies = movies
    }

    override fun getItemCount(): Int {
        return movies.size
    }
    override fun onBindViewHolder(holder: CustomHolder, position: Int) {
        holder.bind(movies.get(position))
        holder?.setOnClick()
    }
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHolder {
        val layoutInflater = LayoutInflater.from(parent?.context)
        val cellForRow =  layoutInflater.inflate(R.layout.movie_row, parent, false)
        return CustomHolder(cellForRow)
    }
}
class CustomHolder(view: View): RecyclerView.ViewHolder(view){
    fun bind(result: Movie){
        itemView.title.text = result.title
        itemView.release_date.text = result.releaseDate
    }
}

在MoviesRepository.kt

val BASE_URL: String = "https://api.themoviedb.org/3/"
val LANGUAGE: String = "en-US"

data class MoviesRepository (
    val repositroy: MoviesRepository,
    val api: TMDbApi
)
object getInstance{
    val retrofit: TMDbApi = Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create())
        .client(OkHttpClient().newBuilder().build())
        .baseUrl(BASE_URL)
        .build()
        .create(TMDbApi::class.java)
}

在AllMoviesActivity.kt

class AllMoviesActivity : AppCompatActivity(), Callback<List<MoviesResponse>> {

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

        rw_main.layoutManager = LinearLayoutManager(this)
        rw_main.adapter = MainAdapter()

        getMovies()
    }

    private fun getMovies() {
        getInstance.retrofit.getPopularMovies("2B0b0e8d104f0d6130a4fc67848f89e107", LANGUAGE, 1).enqueue(this)
    }
    override fun onResponse(call: retrofit2.Call<List<MoviesResponse>>, response: Response<List<MoviesResponse>>) {
        val moviesResponse = response.body() ?: listOf()
        Log.d("Results", moviesResponse.toString())
    }
    override fun onFailure(call: retrofit2.Call<List<MoviesResponse>>, t: Throwable) {
        Toast.makeText(this, "Failed", Toast.LENGTH_SHORT).show()
    }
}

我已经在清单文件中添加了Internet权限。运行此命令时,出现“失败”的吐司,但看不到RecyclerView。你能告诉我我想念的是什么吗?我一直在搜索Internet解决方案,但没有结果。希望你能帮助我。谢谢!

1 个答案:

答案 0 :(得分:0)

好像您缺少一些小东西。在邮递员上调用API后,您在响应正文中的期望如下:

{
"page": 1,
"total_results": 10000,
"total_pages": 500,
"results": [...
}

TMDbApi.kt 中显示了这一点,让您摘要了一下,您已经指出,您期望的响应是List<MoviesResponse>,它与您收到的响应是JSON冲突类型为MoviesResponse的类型, 修复:

 ...): Call<List<MoviesResponse>>
}

收件人:

    ): Call<MoviesResponse>
}

其余的都是一些小补丁,可以调整您的响应处理。

转到 AllMoviesActivity.kt 并更改以下内容:

class AllMoviesActivity : AppCompatActivity(), Callback<List<MoviesResponse>> {

class MainActivity : AppCompatActivity(), Callback<MoviesResponse>

然后在回调函数上,如下更改签名:

 override fun onResponse(call: retrofit2.Call<List<MoviesResponse>>, response: Response<List<MoviesResponse>>) {
            val moviesResponse = response.body() ?: listOf() ...

收件人:

override fun onResponse(call: Call<MoviesResponse>, response:Response<MoviesResponse>
     ) {
       val moviesResponse = response.body()//type will be inferred ...

最后,

override fun onFailure(call: retrofit2.Call<List<MoviesResponse>>, t: Throwable) {

收件人:

override fun onFailure(call: Call<MoviesResponse>, t: Throwable) {

这是获得响应并使 Failure 吐司保持沉默的基本条件。

离别镜头

将日志设为您的朋友,他们可以显示混乱且最小的未命中