RecyclerView实战——QQ看点

简介

学习完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)
                }
            }
        }
    }
}
好的完美收工!

源码

源码双手奉上

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容