本节内容
1.数据模块布局
2.确定数据源
3.设置item
4.监听item点击事件
一、数据模块布局
1.先从containers里面拖动一个RecycleView布局进来,并设置一下id为mRecycler
2.在MainActivity里面配置RecyclerView 的样式。第三个参数reverseLayout,要不要反着显示数据,比如说QQ空间,最新也是最后发布的消息一般显示在最前面。但是我们这里不需要反着显示。
mRecycler.layoutManager = LinearLayoutManager(
this,
LinearLayoutManager.VERTICAL,
false
)
3.样式搞定之后开始设置适配器(提供具体显示的数据),那么新建一个类来实现Adapter。
class NewsAdapter :RecyclerView.Adapter() {
inner class NewsViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView){
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder { }
override fun getItemViewType(position: Int): Int {}
override fun getItemCount(): Int {}
override fun onBindViewHolder(holder: NewsViewHolder, position: Int) {}
}
4.因为要向NewsAdapter这个类传递数据,但是如果都交给主类的话,主类的压力就很大。所以我们设置一个中转站仓库,让它来判断数据是来自网络、数据库还是本地。对于中转站来说,只要提供一个加载数据的方法即可。
-
新建一个名为Repository的类,并设置单例设计模式。
class Repository private constructor(){
companion object{
private var instance = Repository()
fun getInstance() = instance
}
}
-
在这个仓库类里面,还要有获取数据的接口和更改数据的接口
private fun loadData(){
}
5.显示新闻的版式有两种,一种左边是标题,右边是图片,下边是点赞数。还有一种上边是标题,下边是图片和点赞数。这三个控件都是分散的,所以我们要把它们封装为一个整体。
所以我们提供一个类,里面包括标题、图片、点赞数。(因为在这里面什么也不做,所以为数据类)
-
图片和标题都是不可变的,点赞数是可变的。最后一个参数是版面的类型,我们提供的版式有两种,0表示一种,1表示一种。
data class NewsModel(val title:String,
val imageId:Int,
var like:Int,
val type:Int
)
6.创建一个接口,在里面提供一个 getData()方法。判断数据是从哪个地方加载的(本地/数据库/网络)返回值为数组。
interface DataInterface {
fun getData():ArrayList
}
7.创建一个LocalUtil类,这个类继承自DataInterface ,在这个类里面加载数据。这里我们自己模拟一下,所以自己写一点死的数据。
class LocalUtil :DataInterface{
override fun getData(): ArrayList {
//制造数据
val d1 = NewsModel("安卓开发",R.drawable.cute2,2021,0)
val d2 = NewsModel("ios开发",R.drawable.cute1,1193,1)
val d3 = NewsModel("python开发",R.drawable.cute1,3000,0)
val d4 = NewsModel("Web开发",R.drawable.cute2,150,1)
return arrayListOf(d1,d2,d3,d4)
}
}
二、确定数据源
1.实现Repository类里面的loadData()方法。获取数据之后,我们要将其保存起来。所以我们在实现这个方法之前要先定义一个变量来保存接收的数据。
-
它是一个可变数据源,所以要私有化,不能让外部轻易访问。然后封装为不可变的提供给外部使用。
private val mdatas = mutableListOf()
var datas:List = listOf()
get() {
if(field.isEmpty()){
//如果没有就加载一次,有就直接返回
loadData()
field = mdatas.toList()
}
return field
}
2.然后再实现loadData()方法
private fun loadData(){
//当前通过localUtil获取数据
val dataSource:DataInterface = LocalUtil()
//将获取的数据添加到数据源中
mdatas.addAll( dataSource.getData())
}
3.在NewsAdapter里面可以实现 getItemCount()方法了。
override fun getItemCount(): Int {
//访问数据中心获取数据的条数
return Repository.getInstance().datas.size
}
三、设置item
1.在layout里面设置两种xml。直接用约束布局,可以自己任意设置,这个不是重点。我设置的如下图所示。
2.整好之后,我们就需要在NewsAdapter类里面的onCreateViewHolder()方法里面创建一个item,使用LayoutInflater 将xml转化为view。在解析之前,我们要先判断它是哪种类型,所以我们要先写一个方法来设置position对应的那个item类型。
override fun getItemViewType(position: Int): Int {
//获取position对应的模型数据
val model= Repository.getInstance().datas[position]
return model.type
}
3.然后就可以在onCreateViewHolder里面解析了。因为我们写了前面那个函数,所以下面这个方法中的参数viewType对应的就是我们前面返回的type。中间的过程系统会自动完成,不用想太多。
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder {
val lf = LayoutInflater.from(parent.context)
val view:View
//判断当前这个item的类型
if(viewType == 0){
view= lf.inflate(R.layout.news_lent,parent,false)
}else{
view= lf.inflate(R.layout.news_port,parent,false)
}
return NewsViewHolder(view)
}
4.在NewsViewHolder方法里面,获取每个控件。mTitle是给TextView设置的id,后面两个也一样。
inner class NewsViewHolder(itemView: View) :RecyclerView.ViewHolder(itemView){
//从当前view中 获取每个控件
val titleTextView = itemView.findViewById(R.id.mTitle)
val iconImageView = itemView.findViewById(R.id.mIcon)
val likeTextView = itemView.findViewById(R.id.mLike)
}
5.然后在onBinderView方法里面,把它们都解析出来。
override fun onBindViewHolder(holder: NewsViewHolder, position: Int) {
//获取position对应的模型数据
val model = Repository.getInstance().datas[position]
holder.titleTextView.text = model.title
holder.iconImageView.setImageResource(model.imageId)
holder.likeTextView.text = model.like.toString()
}
}
6.在MainActivity里面设置一下适配器
mRecycler.adapter = NewsAdapter()
7.然后运行一下,得到如下结果。
-
出现这种情况,我们只要把约束布局的高度改为wrap_content即可(两个xml的约束布局高度都要改),然后得到了以下结果。这个是可以往下滑动的。
四、监听item点击事件
1.在newsAdapter类里面的onBindViewHolder方法里面,给itemView设置一个点击事件。
holder.itemView.setOnClickListener{
}
2.如果在响应点击事件之后需要进行界面跳转,那么要把事件传给MainActivity然后再调用startActivity方法,这两个类之间就存在一个回调。
-
所以我们先在newsAdaper类里面定义一个回调参数。
var callBack:((Int)->Unit)? = null
callBack?.let {
it(position)
}
3.在MainActivity里面再重新设置一下适配器,并实现回调。
val adapter = NewsAdapter()
adapter.callBack = {
startActivity(Intent(this,news2::class.java))
}
mRecycler.adapter = adapter
4.如果觉得这样很麻烦的话,可以在NewsViewHolder类里面定义一个变量来记录位置
var mPosition = 0
-
然后在onBindViewHolder类里面把position赋值给它
holder.mPosition = position
-
最后回到NewsViewHolder类里面,设置一下监听器。
init {
itemView.setOnClickListener{
callBack?.let {
it(mPosition)
}
}
}