Kotlin+Retrofit+Recyclerview的使用以及示例代码

原文地址:Kotlin+Retrofit+Recyclerview的使用以及示例代码

大家好,我是小黑,一个还没秃头的程序员~~~

你必须特别努力,才能显得毫不费力。

今天的文章是为了前段时间学习kotlin所进行的接口对接,使用的网络数据传输框架为Retrofit,使用的接口为玩Android的开放接口,项目地址:https://gitee.com/fjjxxy/kotlin_retrofit.git

知识体系下的文章
https://www.wanandroid.com/article/list/0/json?cid=60
方法:GET
参数:cid 分类的id,上述二级目录的id
      页码:拼接在链接上,从0开始。
      
获取公众号列表
https://wanandroid.com/wxarticle/chapters/json  
方法:GET

需要申请的权限

<uses-permission android:name="android.permission.INTERNET" />

需要添加的依赖

def retrofit_version = '2.6.1'
//----------------- retrofit begin -----------------
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
implementation "com.squareup.retrofit2:converter-gson:$retrofit_version"
implementation "com.squareup.retrofit2:converter-scalars:$retrofit_version"
// ---- okhttp ----
implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
// ---- okhttp ----
//----------------- retrofit end   -----------------
implementation 'androidx.recyclerview:recyclerview:1.0.0'
​
image

效果图中由两个列表组成,上个列表对接第一个接口,下个列表对接第二个接口,好了,接下来是代码展示

(一)定义一个实体类Result,用来接收接口返回的数据

class Result<T> {
    var errorCode: String? = null
    var errorMsg: String? = null
    var data: T? = null
}

(二)定义一个接口Api,使用注解的方式为retrofit提供Restful风格的Api,各种注解的使用方式这里就不一一列举了,请大家自行百度

interface Api {
    /**
     * 微信公众号列表
     */
    @GET("wxarticle/chapters/json")
    fun getwxarticle(): Call<Result<List<User>>>
​
    /**
     * 文章列表
     */
    @GET("article/list/{page}/json")
    fun getArticle(@Path("page") page: String, @Query("cid") cid: String): Call<Result<Article>>
}

(三)封装接口请求工具类HttpHelper,获取 retrofit对象以及Api服务,设置 拦截器以及处理日志 ,代码如下

/**
 * 接口请求工具类
 */
class HttpHelper {
    companion object {
        private val BASE_URL = "https://www.wanandroid.com/" //服务器地址
        private var sHttpClient: OkHttpClient? = null
        private var sApi: Api? = null
​
        /**
         * 添加拦截器,对返回的数据或者日志进行处理,对请求添加统一头部
         */
        fun getHttpClient(): OkHttpClient? {
            if (sHttpClient == null) {
                synchronized(HttpHelper::class.java) {
                    if (sHttpClient == null) {
                        val builder = OkHttpClient.Builder()
                        //添加拦截器
                        var interceptor = HttpLoggingInterceptor(
                            HttpLoggingInterceptor.Logger { message ->
                                //对日志进行处理
                                Log.e("Tag", message)
                            }
                        )
                        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY)
                        builder.addInterceptor(interceptor)
                        builder.addInterceptor { chain ->
                            val request = chain.request()
                            val compressedRequest =
                                request.newBuilder()
                                    //添加头部
                                    .addHeader(
                                        "User-Agent",
                                        "Android"
                                    )
                                    .build()
                            chain.proceed(compressedRequest)
                        }
                        sHttpClient = builder
                            .callTimeout(40, TimeUnit.SECONDS)
                            .connectTimeout(40, TimeUnit.SECONDS)
                            .readTimeout(40, TimeUnit.SECONDS)
                            .writeTimeout(40, TimeUnit.SECONDS)
                            .build()
                    }
                }
            }
            return sHttpClient
        }
​
        /**
         * 获取接口服务
         */
        fun getApi(): Api? {
            if (sApi == null) {
                synchronized(HttpHelper::class.java) {
                    if (sApi == null) {
                        val retrofit = createRetrofit(BASE_URL)
                        sApi = retrofit.create(Api::class.java)
                    }
                }
            }
            return sApi
        }
​
        /**
         * 获取Retrofit对象,定义返回类型的转换器
         */
        private fun createRetrofit(baseUrl: String): Retrofit {
            val gsonBuilder = GsonBuilder()
            return Retrofit.Builder()
                .client(getHttpClient())
                .baseUrl(baseUrl)
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
                .build()
        }
    }
}

(四)封装接口回调类SingleCallback,处理数据返回成功和失败状态的数据

/**
 * 接口请求基本回调
 */
abstract class SingleCallback<T : Result<*>?> :
    Callback<T> {
    //接口请求成功
    override fun onResponse(
        call: Call<T>,
        response: Response<T>
    ) {
        val result = response.body()
        if (result == null) {
            onFailure("-99", "请求异常", null)
        } else {
            if (apiFailure(result)) {
                onFailure(result.errorCode, result.errorMsg ?: "", result)
            } else {
                onSuccess(result)
            }
        }
    }
​
    override fun onFailure(call: Call<T>, t: Throwable) {
        Log.e("SingleCallback", "onFailure: " + t.message, t)
    }
​
    fun successCode(): String {
        return "0"
    }
​
    fun apiFailure(result: T?): Boolean {
        return result == null || successCode() != result.errorCode
    }
​
    /**
     * 失败回调。默认已经将失败信息提示出来
     */
    fun onFailure(code: String?, message: String, response: T?) {
        Toast.makeText(App.getContext(), message, Toast.LENGTH_SHORT).show()
    }
​
    abstract fun onSuccess(response: T)
​
}

(五)设计列表界面以及子项布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
​
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_article_data"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
​
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_user_data"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

item_article.xml
···
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff">

<TextView
android:id="@+id/tv_article"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text=""
android:textSize="14dp" />

</LinearLayout>
···
item_user.xml
···
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff">

<TextView
android:id="@+id/tv_user"
android:layout_width="match_parent"
android:layout_height="40dp"
android:text=""
android:textSize="14dp" />
</LinearLayout>
···
(六)实体类

Article实体类

class Article {
   var datas: List<ArticleData>? = null
}

ArticleData实体类

class ArticleData{
    val title: String? = null
}

User实体类

class User{
    val name: String? = null
}

(七)逻辑以及数据适配器代码编写

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private val mUserList: MutableList<User> = ArrayList()
    private var mArticleList: MutableList<ArticleData> = ArrayList();
​
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //或者这里可以不用findViewById,直接用布局中的id作为变量名也可以
        var rvArticleData: RecyclerView = findViewById(R.id.rv_article_data)
        var rvUserData: RecyclerView = findViewById(R.id.rv_user_data)
        rvArticleData.layoutManager = LinearLayoutManager(this)
        rvUserData.layoutManager = LinearLayoutManager(this)
        rvArticleData.adapter = ArticleAdapter(this, mArticleList)
        var userAdapter = UserAdapter(mUserList)
        userAdapter.setOnUserClickListener(object : UserAdapter.OnUserClickListener {
            override fun click(text: String?) {
                Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
            }
        })
        rvUserData.adapter = userAdapter
       //请求微信公众号接口
        var api = HttpHelper.getApi()
        api!!.getwxarticle()!!.enqueue(object : SingleCallback<Result<List<User>>>() {
            override fun onSuccess(response: Result<List<User>>) {
                if (response.data != null && (response.data?.size ?: 0 > 0)) {
                    mUserList.addAll(response.data!!)
                    rvUserData.adapter?.notifyDataSetChanged()
                }
            }
        })
        //请求文章接口
        var api1 = HttpHelper.getApi()
        api1!!.getArticle("1", "60")!!.enqueue(object : SingleCallback<Result<Article>>() {
            override fun onSuccess(response: Result<Article>) {
                if (response.data != null && response.data?.datas != null && response.data?.datas?.size ?: 0 > 0) {
                    mArticleList.addAll(response.data?.datas!!)
                    rvArticleData.adapter?.notifyDataSetChanged()
                }
            }
        })
    }
}

ArticleAdapter.kt

  class ArticleAdapter(var mContext: Context, var mList: List<ArticleData>) :
    RecyclerView.Adapter<ArticleAdapter.ViewHolder>() {
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        var mTvArticle: TextView = view.tv_article
    }
​
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.item_article,
                parent,
                false
            )
        )
    }
​
    override fun getItemCount(): Int {
        return mList.size
    }
​
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.mTvArticle.text = mList[position].title
        //设置点击事件,Lambda 表达式用着真的很舒服
        holder.mTvArticle.setOnClickListener {
            Toast.makeText(
                mContext,
                mList[position].title,
                Toast.LENGTH_SHORT
            ).show()
        }
    }

UserAdapter.kt

class UserAdapter(var mList: List<User>) :
    RecyclerView.Adapter<UserAdapter.ViewHolder>() {
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        var mTvUser: TextView = view.tv_user
    }
​
    private var mUserClickListener: OnUserClickListener? = null
    //设置点击事件回调,具体的实现在回调中定义
    interface OnUserClickListener {
        fun click(text: String?)
    }
​
    fun setOnUserClickListener(onUserClickListener: OnUserClickListener) {
        this.mUserClickListener = onUserClickListener
    }
​
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        return ViewHolder(
            LayoutInflater.from(parent.context).inflate(
                R.layout.item_user,
                parent,
                false
            )
        )
    }
​
    override fun getItemCount(): Int {
        return mList.size
    }
​
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.mTvUser.text = mList[position].name
        holder.mTvUser.setOnClickListener {
            mUserClickListener!!.click(mList[position].name)
        }
    }

到此为止,Kotlin+Retrofit+Recyclerview的用法就介绍到这里了,功能的效果图如开头展示,代码已上传到码云,地址:https://gitee.com/fjjxxy/kotlin_retrofit.git,有疑惑的欢迎底下评论,我们共同进步!最后,祝大家身体健康,万事如意,谢谢大家的支持与阅读!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容