登录 立即注册
安币:

使用Kotlin封装RecycleView和重构 [复制链接]

2017-8-11 10:49
hellokenken 阅读:1982 评论:0 赞:0
Tag:  

今天,是对RecycleView.Adapter进行封装,形成通用的ApapterRecycleView概念和用途,以为为什么要用RecycleView而不用Listview,这些我就不重复了,一搜一大把,写的都比我好,理解的也比我深入,这里,我要实现的就是使用Kotlin实现通用Adapter;为什么要实现通用Adapter,无非是我们的项目中,会出现各式各样的List展示,于是乎,就出现了无数个Adapter,虽然ctrl+cctrl+v很容易完成,但是,毕竟多了那么多类,对ctrl键的伤害也大,而且我是懒人,希望能够一次书写,n次运行,于是便有了这个想法,今天刚好不算忙,就实现了下,下面让我们来看代码:

先看看之前的Adapter的代码:

MainAdapter

package com.vslimit.kotlindemo.adapter

/**
 * Created by vslimit on 16/1/15.
 */
class MainAdapter(val items: List<String>, val itemClick: (String) -> Unit) :
        RecyclerView.Adapter<MainAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = parent.ctx.layoutInflater.inflate(R.layout.item_list_main, parent, false)
        return ViewHolder(view, itemClick)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bindForecast(items[position])
    }

    override fun getItemCount() = items.size

    class ViewHolder(view: View, val itemClick: (String) -> Unit) : RecyclerView.ViewHolder(view) {

        fun bindForecast(item: String) {
            with(item) {
                itemView.item_name.text = item
                itemView.item_text.text = item
                itemView.onClick { itemClick(item) }
            }
        }
    }
}

我们仔细看看代码,发现,主要就是ViewHolder的实现,其他的代码大同小异,那么这时,想法来了,可不可以只写一个Adapter,多个ViewHolder呢,说写就写,就有了下面的代码:

BaseAdapter

package com.vslimit.kotlindemo.adapter

import android.support.v7.widget.RecyclerView
import android.view.ViewGroup
import com.vslimit.kotlindemo.extensions.ctx
import org.jetbrains.anko.layoutInflater

/**  
 * Created by vslimit on 16/1/15.
 */
class BaseAdapter<T, K : ViewHolder<T>>(val layoutResourceId: Int, val items: List<T>, val itemClick: (T) -> Unit) :
        RecyclerView.Adapter<K>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): K {
        val view = parent.ctx.layoutInflater.inflate(layoutResourceId, parent, false)
        return ViewHolder<T>(view, itemClick) as K
    }

    override fun onBindViewHolder(holder: K, position: Int) {
        holder.bindForecast(items[position])
    }

    override fun getItemCount() = items.size

}

当然了,这就需要先定义一个ViewHolder<T>,每个自定义的ViewHolder继承,然后实现自己的布局,写到这的时候,忽然又觉得,这样不是很好,这样封装,到最后,变成了n个ViewHolder,类还多了,而且,也不通用,因为,如果RecycleView需要对某一行进行长按操作或者其他操作,那又要重新定义一个BaseAdapter,还不如封装前直接写省事些,因此这个方案被否掉了。于是换了个思路,我只替换掉这个类的bindForecast方法里的实现,应该就可以实现通用,我们直接来看这一版的代码:

BaseAdapter

package com.vslimit.kotlindemo.adapter

import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
import com.vslimit.kotlindemo.extensions.ctx
import org.jetbrains.anko.layoutInflater

/**
 * Created by vslimit on 16/1/15.
 */
class BaseAdapter<T>(val layoutResourceId: Int, val items: List<T>, val init: (View, T) -> Unit) :
        RecyclerView.Adapter<BaseAdapter.ViewHolder<T>>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder<T> {
        val view = parent.ctx.layoutInflater.inflate(layoutResourceId, parent, false)
        return ViewHolder(view, init)
    }

    override fun onBindViewHolder(holder: ViewHolder<T>, position: Int) {
        holder.bindForecast(items[position])
    }

    override fun getItemCount() = items.size

    class ViewHolder<in T>(view: View, val init: (View, T) -> Unit) : RecyclerView.ViewHolder(view) {
        fun bindForecast(item: T) {
            with(item) {
                init(itemView, item)
            }
        }
    }
}

定义了一个init(View,T)的函数来实现,那么我们在初始化BaseAdapter只需要实现init(View,T)方法即可,下面我们来看看具体的应用,这里我用了干货中的api接口,来实现:

GankListResult.kt

package com.vslimit.kotlindemo.model

import java.util.*

/**
 * Created by vslimit on 16/11/26.
 */
open class GankListResult {
    var count: Int = 0
    var error: Boolean = false
    var results: ArrayList<Gank> = ArrayList()
}

class Gank {
    var desc = ""
    var ganhuo_id = ""
    var publishedAt = ""
    var readability = ""
    var type = ""
    var url = ""
    var who = ""
}

布局文件

fragment_ganks.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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.support.v7.widget.RecyclerView
            android:id="@+id/gankListRv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@android:color/white" />
</RelativeLayout>

item_list_gank.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp">

    <RelativeLayout
        android:id="@+id/gank_rl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/item_gank_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/item_gank_who"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/item_gank_title"
            android:layout_marginTop="5dp"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/item_gank_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_alignTop="@+id/item_gank_who"
            android:layout_marginRight="5dp"
            android:textSize="14sp" />

    </RelativeLayout>


    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_below="@id/gank_rl"
        android:layout_marginTop="5dp"
        android:background="@android:color/darker_gray" />

</RelativeLayout>

GankListFragment

package com.vslimit.kotlindemo.fragment

import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.util.Log
import android.view.View
import com.vslimit.kotlindemo.App
import com.vslimit.kotlindemo.R
import com.vslimit.kotlindemo.adapter.BaseAdapter
import com.vslimit.kotlindemo.event.BaseEvent
import com.vslimit.kotlindemo.extensions.loading
import com.vslimit.kotlindemo.model.Gank
import com.vslimit.kotlindemo.model.GankListResult
import com.vslimit.kotlindemo.util.Bus
import com.vslimit.kotlindemo.util.Const
import com.vslimit.kotlindemo.util.NetworkUtil
import com.vslimit.kotlindemo.util.net.volley.Listener
import com.vslimit.kotlindemo.util.net.volley.add
import com.vslimit.kotlindemo.util.net.volley.toString
import kotlinx.android.synthetic.main.fragment_ganks.*
import kotlinx.android.synthetic.main.item_list_gank.view.*
import org.jetbrains.anko.onClick
import org.jetbrains.anko.onLongClick
import org.jetbrains.anko.support.v4.act
import org.jetbrains.anko.support.v4.alert
import org.jetbrains.anko.support.v4.toast

/**
 * Created by vslimit on 16/12/31.
 */
class GankListFragment : BaseFragment() {
    override val layoutResourceId: Int = R.layout.fragment_ganks
    var result: GankListResult? = null
    var adapter: BaseAdapter<Gank>? = null

    companion object {
        fun getInstance(): GankListFragment {
            return GankListFragment()
        }
    }

    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val layoutManager: LinearLayoutManager = LinearLayoutManager(act)
        gankListRv.layoutManager = layoutManager
    }

    private fun itemLongClick(item: Gank): Boolean {
        toast(item.desc)
        return true
    }

    override fun onResume() {
        super.onResume()
        init()
    }

    fun init() {
        if (NetworkUtil.isNetwork(act)) {
            val listener = Listener<GankListResult> { e, r ->
                e?.let { Bus.post(BaseEvent<GankListResult>(error = e)) }
                r?.let { Bus.post(BaseEvent(response = r)) }
            }
            val url = resources.getString(R.string.gank_list)
            Log.d("Url:::", url)
            App.queue!!.add(listener, url)
            loading(Const.SHOW)
        } else {
            alert("网络错误", "网络未连接,请检查网络")
        }
    }

   fun onEventMainThread(event: BaseEvent<GankListResult>) {
        loading(Const.HIDE)
        val error = event.error
        result = event.response
        if (error != null) {
            toast(error.toString(resources))
        } else {
            if (result!!.error) {
                alert("网络错误", "网络未连接,请检查网络")
            } else {
                adapter = BaseAdapter(R.layout.item_list_gank, result!!.results) { view: View, item: Gank ->
                    view.item_gank_title.text = item.desc
                    view.item_gank_who.text = item.who
                    view.item_gank_date.text = item.publishedAt
                    view.onClick {
                        toast("onclick")
                    }
                    view.onLongClick {
                        itemLongClick(item)
                    }
                }
                gankListRv.adapter = adapter
                adapter!!.notifyDataSetChanged()
            }
        }
    }



    override fun onDestroy() {
        super.onDestroy()
    }
}

GankListFragment中可以看出来,我们在使用时只需要实现布局即可,如果有点击事件或长按点击事件,一样轻松实现;

adapter = BaseAdapter(R.layout.item_list_gank, result!!.results) { view: View, item: Gank ->
                    view.item_gank_title.text = item.desc
                    view.item_gank_who.text = item.who
                    view.item_gank_date.text = item.publishedAt
                    view.onClick {
                        toast("onclick")
                    }
                    view.onLongClick {
                        itemLongClick(item)
                    }
                }

当然了,如果页面复杂,这段代码的实现肯定也要复杂了,不过,以后我们只要使用这一个类即可;至此,跨年作品终于写完了,本篇的废话也比较大,大家还是多支持下,也可以去git下star下,算是鼓励吧。

还是看看效果图,虽然有点丑:



分享到:
我来说两句
facelist
您需要登录后才可以评论 登录 | 立即注册
所有评论(0)

站长推荐

通过邮件订阅最新安卓weekly信息
上一条 /4 下一条

下载安卓巴士客户端

全国最大的安卓开发者社区

广告投放| 下载客户端|申请友链|手机版|站点统计|安卓巴士 ( 粤ICP备15117877号 )

返回顶部