简介
学习完RecyclerView的使用,这就写一个Demo来用一用,本人平常就在QQ看点里面看看,这里就是RecyclerView实现的一块功能。
先上个效果图看看:
详解
知识储备
-
单例模式
设置数据中心,统一管理数据
-
MVC模式
视图数据进行分离,详情可参考Android架构设计之MVC
-
CardView卡片式布局
系统默认提供的布局管理器,实现卡片式的效果
实战代码
如此文所述步骤相同
1、在xml中使用RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/mRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2、在MainActivity代码中配置RecyclerView的相关属性
界面跳转回调至MainActivity中,便于界面跳转(看完下面的代码再看此回调部分)
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//配置recyclerView
mRecyclerView.layoutManager = LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false)
//设置适配器 显示数据
NewsAdapter().also {
mRecyclerView.adapter = it
//点击事件回调
it.callBack = {position->
val intent = Intent(this,DetailActivity::class.java).apply {
putExtra("newsModel",Repository.getInstance().data[position])
}
startActivity(intent)
}
}
}
}
3、在配置适配器之前,先解决数据来源问题,QQ看点每一个item对应一条新闻,创建一个Model类,每一个Model的实例对象对应一条新闻
//枚举类型 在此使用不太规范,其实用常量更好
enum class NewsType(i: Int) {
SINGLE_IMG(9),
THREE_IMG(1)
}
//数据模型
data class NewsModel(val title:String,
val resIds:IntArray,
var like:Int,
var author:String,
val type:NewsType
):Serializable
Model的具体样式靠xml定义,都使用CardView布局
news_item_type_one.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="4dp"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iconImageView"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/desktop1" />
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="title"
android:textColor="#000000"
android:textSize="18sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:orientation="horizontal">
<TextView
android:id="@+id/authorTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="11dp"
android:textSize="10sp"
android:text="anthor"/>
<TextView
android:id="@+id/likeTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:text="zan"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
news_item_type_two.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp"
app:cardElevation="4dp"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/imgsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/iconImageView"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/desktop1" />
<ImageView
android:id="@+id/iconImageView2"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/desktop1" />
<ImageView
android:id="@+id/iconImageView3"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/desktop1" />
</LinearLayout>
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="title"
android:textColor="#000000"
android:textSize="18sp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:orientation="horizontal">
<TextView
android:id="@+id/authorTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="anthor"
android:textSize="11sp" />
<TextView
android:id="@+id/likeTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:text="zan"
android:textSize="11sp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
4、创建数据中心类(或者说数据仓库),统一管理数据,使用单例模式
class Repository private constructor(){
private val orgDatas = mutableListOf<NewsModel>()
var data:List<NewsModel> = listOf()
get() {
if (field.isEmpty()){
loadData()
field = ArrayList(orgDatas)
}
return field
}
//伴生对象 静态资源
companion object{
private val instance = Repository()
fun getInstance() = instance
}
//获取数据的接口
fun loadData():ArrayList<NewsModel>{
//从本地获取数据
orgDatas.addAll(LocalDataLoader().getData())
return LocalDataLoader().getData()
}
//更改数据的接口
}
5、数据中心只是用于统一管理数据,具体如何获取数据得靠其他类来实现,如:从本地、网络、数据库获取数据,得分别写一个类管理具体的实现,本例只是模拟(说白了伪造数据),创建一个LocalDataLoader类管理本地数据的获取
注意:在这前更加建议写一个接口,获取数据的类都需要实现这个接口(接口的本质即向类中注入方法),这么写的好处是:无论你是从本地、网络还是数据库获取数据,其他类可以不需要知道你具体是如何实现的,因为实现了接口,数据都是通过这一个接口方法传回,实现解耦
interface DataLoadInterface {
//统一回调数据的方法
fun getData():ArrayList<NewsModel>
}
class LocalDataLoader: DataLoadInterface {
override fun getData(): ArrayList<NewsModel> {
return initData()
}
fun initData(): ArrayList<NewsModel>{
val data = ArrayList<NewsModel>()
val images = intArrayOf(R.drawable.xc,R.drawable.bzhw,R.drawable.zgl)
NewsModel("阳光露脸好运直涨!本周12星座运势详细解析", intArrayOf(R.drawable.xz),0,"星座控",NewsType.SINGLE_IMG).also { data.add(it) }
NewsModel("王者荣耀:有哪些法师根本不怕刺客?三段位移的诸葛亮不是最强", images,0,"王者荣耀论坛",NewsType.THREE_IMG).also { data.add(it) }
NewsModel("波什:科比早跟我说过会赢奥斯卡 他比别人更有远见", intArrayOf(R.drawable.kb),0,"NBA学社",NewsType.SINGLE_IMG).also { data.add(it) }
NewsModel("王者荣耀:有哪些法师根本不怕刺客?三段位移的诸葛亮不是最强", images,0,"王者荣耀论坛",NewsType.THREE_IMG).also { data.add(it) }
NewsModel("游戏宅男变身修仙大佬!《我是大神仙》动画定档!", intArrayOf(R.drawable.dm),0,"动漫吧",NewsType.SINGLE_IMG).also { data.add(it) }
return data
}
6、配置Adapter,并通过使用高阶函数实现点击事件的回调
class NewsAdapter : RecyclerView.Adapter<NewsAdapter.NewsViewHolder>() {
//回调点击事件
var callBack:((Int)->Unit) = {position->}
//创建一个Item 使用inflate解析布局
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder {
LayoutInflater.from(parent.context).apply {
val view = if (viewType == 0) inflate(R.layout.news_item_type_one,parent,false)
else inflate(R.layout.news_item_type_two,parent,false)
return NewsViewHolder(view)
}
}
override fun onBindViewHolder(holder: NewsViewHolder, position: Int) {
Repository.getInstance().data[position].apply {
//获取数据 与控件进行绑定
holder.titleTextView.text = title
holder.likeTextView.text = "${like}评论"
holder.authorTextView.text = author
if (type == NewsType.SINGLE_IMG){
holder.iconImageView?.setImageResource(resIds[0])
}else{
for ((index,resId) in resIds.withIndex()){
(holder.imageContainer.getChildAt(index) as ImageView).setImageResource(resId)
}
}
}
//点击事件
holder.itemView.setOnClickListener{
callBack(position)
}
}
//获取view的类型
override fun getItemViewType(position: Int): Int {
return Repository.getInstance().data[position].type.ordinal
}
//确认有多少行
override fun getItemCount(): Int {
return Repository.getInstance().data.size
}
//ViewHolder
class NewsViewHolder(itemView: View):RecyclerView.ViewHolder(itemView) {
val titleTextView = itemView.findViewById<TextView>(R.id.titleTextView)
val iconImageView:ImageView? = itemView.findViewById<ImageView>(R.id.iconImageView)
val authorTextView = itemView.findViewById<TextView>(R.id.authorTextView)
val likeTextView = itemView.findViewById<TextView>(R.id.likeTextView)
val imageContainer = itemView.findViewById<LinearLayout>(R.id.imgsContainer)
}
}
7、创建一个详情界面DetailActivity,点击item后跳转到具体显示界面,并将Model的数据一并传递到DetailActivity,DetailActivity接收到数据后显示出来
activity_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DetailActivity">
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:textColor="#000"
android:textSize="28sp"
app:layout_constraintStart_toStartOf="parent"
android:text="title"/>
<LinearLayout
android:id="@+id/imgsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/titleTextView">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@drawable/desktop1" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
DetailActivity
class DetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
//接收传递过来的数据模型 解析并显示
(intent.getSerializableExtra("newsModel") as NewsModel).apply {
titleTextView.text = title
if (type == NewsType.SINGLE_IMG){
(imgsContainer.getChildAt(0) as ImageView).setImageResource(resIds[0])
}else if(type == NewsType.THREE_IMG){
for ((index,resId) in resIds.withIndex()){
(imgsContainer.getChildAt(index) as ImageView).setImageResource(resId)
}
}
}
}
}