博客
关于我
第14章 使用Kotlin 进行 Android 开发(2)
阅读量:269 次
发布时间:2019-03-01

本文共 12902 字,大约阅读时间需要 43 分钟。

14.2.3 实现后端 API 的接入及其数据展示的逻辑

在本节中我们将实现后端 API 的接入及其数据展示的逻辑。

新建领域对象类 Movie

data class Movie(val id: String, val title: String, val overview: String, val posterPath: String) {    override fun toString(): String {        return "Movie(id='$id', title='$title', overview='$overview', posterPath='$posterPath')"    }}

API 返回的数据结构与解析

我们调用的 API 是

val VOTE_AVERAGE_API = "http://api.themoviedb.org//3/discover/movie?certification_country=US&certification=R&sort_by=vote_average.desc&api_key=7e55a88ece9f03408b895a96c1487979"

它的数据返回是

{  "page": 1,  "total_results": 10350,  "total_pages": 518,  "results": [    {      "vote_count": 28,      "id": 138878,      "video": false,      "vote_average": 10,      "title": "Fatal Mission",      "popularity": 3.721883,      "poster_path": "/u351Rsqu5nd36ZpbWxIpd3CpbJW.jpg",      "original_language": "en",      "original_title": "Fatal Mission",      "genre_ids": [        10752,        28,        12      ],      "backdrop_path": "/wNq5uqVDT7a5G1b97ffYf4hxzYz.jpg",      "adult": false,      "overview": "A CIA Agent must rely on reluctant help from a female spy in the North Vietnam jungle in order to pass through enemy lines.",      "release_date": "1990-07-25"    },    ...    ]}

我们使用 fastjson 来解析这个数据。在 app 下面的 build.gradle 中添加依赖

dependencies {    ...    // https://mvnrepository.com/artifact/com.alibaba/fastjson    compile group: 'com.alibaba', name: 'fastjson', version: '1.2.39'}

解析代码如下

val jsonstr = URL(VOTE_AVERAGE_API).readText(Charset.defaultCharset())try {    val obj = JSON.parse(jsonstr) as Map<*, *>    val dataArray = obj.get("results") as JSONArray    }} catch (ex: Exception) {}

然后我们把这个 dataArray 放到我们的 MovieContent 对象中

dataArray.forEachIndexed { index, it ->        val title = (it as Map<*, *>).get("title") as String        val overview = it.get("overview") as String        val poster_path = it.get("poster_path") as String        addMovie(Movie(index.toString(), title, overview, getPosterUrl(poster_path)))}

其中,addMovie 的代码是

object MovieContent {val MOVIES: MutableList           = ArrayList()val MOVIE_MAP: MutableMap             = HashMap()...private fun addMovie(movie: Movie) {    MOVIES.add(movie)    MOVIE_MAP.put(movie.id, movie)}}

电影列表页面

MovieListActivity 是电影列表页面的 Activity,代码如下

package com.easy.kotlinimport android.content.Intentimport android.os.Bundleimport android.support.v7.app.AppCompatActivityimport android.support.v7.widget.RecyclerViewimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroupimport android.widget.ImageViewimport android.widget.TextViewimport com.easy.kotlin.bean.MovieContentimport com.easy.kotlin.util.HttpUtilimport kotlinx.android.synthetic.layout.activity_movie_detail.*import kotlinx.android.synthetic.layout.activity_movie_list.*import kotlinx.android.synthetic.layout.movie_list.*import kotlinx.android.synthetic.layout.movie_list_content.view.*class MovieListActivity : AppCompatActivity() {    private var mTwoPane: Boolean = false    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_movie_list)        setSupportActionBar(toolbar)        toolbar.title = title        if (movie_detail_container != null) {            mTwoPane = true        }        setupRecyclerView(movie_list)    }    private fun setupRecyclerView(recyclerView: RecyclerView) {        recyclerView.adapter = SimpleItemRecyclerViewAdapter(this, MovieContent.MOVIES, mTwoPane)    }    class SimpleItemRecyclerViewAdapter(private val mParentActivity: MovieListActivity,                                        private val mValues: List          ,                                        private val mTwoPane: Boolean) : RecyclerView.Adapter            () {        private val mOnClickListener: View.OnClickListener        init {            mOnClickListener = View.OnClickListener { v ->                val item = v.tag as MovieContent.Movie                if (mTwoPane) {                    val fragment = MovieDetailFragment().apply {                        arguments = Bundle()                        arguments.putString(MovieDetailFragment.ARG_MOVIE_ID, item.id)                    }                    mParentActivity.supportFragmentManager                            .beginTransaction()                            .replace(R.id.movie_detail_container, fragment)                            .commit()                } else {                    val intent = Intent(v.context, MovieDetailActivity::class.java).apply {                        putExtra(MovieDetailFragment.ARG_MOVIE_ID, item.id)                    }                    v.context.startActivity(intent)                }            }        }        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {            val view =                    LayoutInflater                            .from(parent.context)                            .inflate(R.layout.movie_list_content, parent, false)            return ViewHolder(view)        }        override fun onBindViewHolder(holder: ViewHolder, position: Int) {            val item = mValues[position]            holder.mIdView.text = item.id            holder.mTitle.text = item.title            holder.mMoviePosterImageView.setImageBitmap(HttpUtil.getBitmapFromURL(item.posterPath))            with(holder.itemView) {                tag = item                setOnClickListener(mOnClickListener)            }        }        override fun getItemCount(): Int {            return mValues.size        }        inner class ViewHolder(mView: View) : RecyclerView.ViewHolder(mView) {            val mIdView: TextView = mView.id_text            val mTitle: TextView = mView.title            val mMoviePosterImageView: ImageView = mView.movie_poster_image        }    }}

对应的布局文件如下

activity_movie_list.xml

 

movie_list.xml

 

movie_list_content.xml

 

视图数据适配器 ViewAdapter

我们在创建 MovieListActivity 过程中需要展示响应的数据,这些数据由 ViewAdapter 来承载,对应的代码如下

override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_movie_list)        setSupportActionBar(toolbar)        toolbar.title = title        if (movie_detail_container != null) {            mTwoPane = true        }        setupRecyclerView(movie_list)    }    private fun setupRecyclerView(recyclerView: RecyclerView) {        recyclerView.adapter = SimpleItemRecyclerViewAdapter(this, MovieContent.MOVIES, mTwoPane)    }

视图中图像的展示

其中,在函数 SimpleItemRecyclerViewAdapter.onBindViewHolder 中,我们设置 View 组件与Model 数据的绑定。其中的电影海报是图片,所以我们的布局文件中使用了 ImageView,对应的布局文件是 movie_list_content.xml ,代码如下

 

我们这里是根据图片的 URL 来展示图片,ImageView 类有个setImageBitmap 方法,可以直接设置 Bitmap 图片数据

holder.mMoviePosterImageView.setImageBitmap(HttpUtil.getBitmapFromURL(item.posterPath))

而通过 url 获取Bitmap 图片数据的代码是

object HttpUtil {    fun getBitmapFromURL(src: String): Bitmap? {        try {            val url = URL(src)            val input = url.openStream()            val myBitmap = BitmapFactory.decodeStream(input)            return myBitmap        } catch (e: Exception) {            e.printStackTrace()            return null        }    }}

电影详情页面

MovieDetailActivity 是电影详情页面,代码如下

package com.easy.kotlinimport android.content.Intentimport android.os.Bundleimport android.support.design.widget.Snackbarimport android.support.v7.app.AppCompatActivityimport android.view.MenuItemimport kotlinx.android.synthetic.layout.activity_movie_detail.*class MovieDetailActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_movie_detail)        setSupportActionBar(detail_toolbar)        fab.setOnClickListener { view ->            Snackbar.make(view, "Replace with your own detail action", Snackbar.LENGTH_LONG)                    .setAction("Action", null).show()        }        supportActionBar?.setDisplayHomeAsUpEnabled(true)        if (savedInstanceState == null) {            val arguments = Bundle()            arguments.putString(MovieDetailFragment.ARG_MOVIE_ID,                    intent.getStringExtra(MovieDetailFragment.ARG_MOVIE_ID))            val fragment = MovieDetailFragment()            fragment.arguments = arguments            supportFragmentManager.beginTransaction()                    .add(R.id.movie_detail_container, fragment)                    .commit()        }    }    override fun onOptionsItemSelected(item: MenuItem) =            when (item.itemId) {                android.R.id.home -> {                    navigateUpTo(Intent(this, MovieListActivity::class.java))                    true                }                else -> super.onOptionsItemSelected(item)            }}

其中的详情页的布局 XML 文件是activity_item_detail.xml, 代码如下

 

我们把电影详情的 Fragment 的展示放到 NestedScrollView 中

 

电影详情的 Fragment 代码是 MovieDetailFragment

package com.easy.kotlinimport android.os.Bundleimport android.support.v4.app.Fragmentimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroupimport com.easy.kotlin.bean.MovieContentimport com.easy.kotlin.util.HttpUtilimport kotlinx.android.synthetic.layout.activity_movie_detail.*import kotlinx.android.synthetic.layout.movie_detail.view.*class MovieDetailFragment : Fragment() {    private var mItem: MovieContent.Movie? = null    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        if (arguments.containsKey(ARG_MOVIE_ID)) {            mItem = MovieContent.MOVIE_MAP[arguments.getString(ARG_MOVIE_ID)]            mItem?.let {                activity.toolbar_layout?.title = it.title            }        }    }    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,                              savedInstanceState: Bundle?): View? {        // 绑定 movieDetailView        val movieDetailView = inflater.inflate(R.layout.movie_detail, container, false)        mItem?.let {            movieDetailView.movie_poster_image.setImageBitmap(HttpUtil.getBitmapFromURL(it.posterPath))            movieDetailView.movie_overview.text = "影片简介: ${it.overview}"            movieDetailView.movie_vote_count.text = "打分次数:${it.vote_count}"            movieDetailView.movie_vote_average.text = "评分:${it.vote_average}"            movieDetailView.movie_release_date.text = "发行日期:${it.release_date}"        }        return movieDetailView    }    companion object {        const val ARG_MOVIE_ID = "movie_id"    }}

其中的 R.layout.movie_detail 布局文件 movie_detail.xml 如下

 

电影源数据的获取

我们定义了一个 MovieContent 对象类来存储从 API 获取到的数据,代码如下

package com.easy.kotlin.beanimport android.os.StrictModeimport com.alibaba.fastjson.JSONimport com.alibaba.fastjson.JSONArrayimport java.net.URLimport java.nio.charset.Charsetimport java.util.*object MovieContent {    val MOVIES: MutableList           = ArrayList()    val MOVIE_MAP: MutableMap             = HashMap()    val VOTE_AVERAGE_API = "http://api.themoviedb.org//3/discover/movie?sort_by=popularity.desc&api_key=7e55a88ece9f03408b895a96c1487979&page=1"    init {        val policy = StrictMode.ThreadPolicy.Builder().permitAll().build()        StrictMode.setThreadPolicy(policy)        initMovieListData()    }    private fun initMovieListData() {        val jsonstr = URL(VOTE_AVERAGE_API).readText(Charset.defaultCharset())        try {            val obj = JSON.parse(jsonstr) as Map<*, *>            val dataArray = obj.get("results") as JSONArray            dataArray.forEachIndexed { index, it ->                val title = (it as Map<*, *>).get("title") as String                val overview = it.get("overview") as String                val poster_path = it.get("poster_path") as String                val vote_count = it.get("vote_count").toString()                val vote_average = it.get("vote_average").toString()                val release_date = it.get("release_date").toString()                addMovie(Movie(id = index.toString(),                        title = title,                        overview = overview,                        vote_count = vote_count,                        vote_average = vote_average,                        release_date = release_date,                        posterPath = getPosterUrl(poster_path)))            }        } catch (ex: Exception) {            ex.printStackTrace()        }    }    private fun addMovie(movie: Movie) {        MOVIES.add(movie)        MOVIE_MAP.put(movie.id, movie)    }    fun getPosterUrl(posterPath: String): String {        return "https://image.tmdb.org/t/p/w185_and_h278_bestv2$posterPath"    }    data class Movie(val id: String,                     val title: String,                     val overview: String,                     val vote_count: String,                     val vote_average: String,                     val release_date: String,                     val posterPath: String)}

配置 AndroidManifest.xml

最后,我们配置 AndroidManifest.xml文件内容如下

...

因为我们要访问网络,所以需要添加该行配置

 

转载地址:http://erha.baihongyu.com/

你可能感兴趣的文章
Objective-C实现上传文件到FTP服务器(附完整源码)
查看>>
Objective-C实现不重复字符的最长子串算法(附完整源码)
查看>>
Objective-C实现两个字符串由相同的字母组成但排列方式不同(字符串字谜)算法(附完整源码)
查看>>
Objective-C实现两个栈实现队列算法(附完整源码)
查看>>
Objective-C实现两个队列实现栈算法(附完整源码)
查看>>
Objective-C实现两数之和问题(附完整源码)
查看>>
Objective-C实现中介者模式(附完整源码)
查看>>
Objective-C实现中值滤波(附完整源码)
查看>>
Objective-C实现中国剩余定理(附完整源码)
查看>>
Objective-C实现中国剩余定理(附完整源码)
查看>>
Objective-C实现中文模糊查询(附完整源码)
查看>>
Objective-C实现串口通讯(附完整源码)
查看>>
Objective-C实现串逐位和(附完整源码)
查看>>
Objective-C实现串链式存储简单匹配(附完整源码)
查看>>
Objective-C实现主存储器空间的分配和回收(附完整源码)
查看>>
Objective-C实现乘方运算---m的n次方(附完整源码)
查看>>
Objective-C实现乘法持续性multiplicative persistence算法(附完整源码)
查看>>
Objective-C实现二分查找最接近的数值m(附完整源码)
查看>>
Objective-C实现二分查找最接近的数值m(附完整源码)
查看>>
Objective-C实现二叉搜索树算法(附完整源码)
查看>>