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

源码

源码双手奉上

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

推荐阅读更多精彩内容