如何在Adapter中正确的使用Context_recyclerview.adapter context_Jere_Chen的博客-程序员宅基地

技术标签: Context  RecyclerView  Adapter  Android  

1. 前言

最近我在项目中,发现一个同事写 RecyclerView.Adapter 的时候都习惯定义一个全局的私有 mContext,然后在onCreateViewHolder(parent: ViewGroup, viewType: Int) 方法中进行赋值操作 mContext = parent.context

如下:

class MyAdapter(private val dataList: List<MyData>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    
    private lateinit var mContext: Context

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    
    	//在这里进行context的赋值
        mContext = parent.context
        val view = LayoutInflater.from(parent.context).inflate(R.layout.rcy_item_view, parent, false)
        return MyViewHolder(view)
    }

    override fun getItemCount(): Int {
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    }
}

WT,还可以这么操作!!第一次见到这样的写法,有点意思,由此有了这一篇文章。

2. 获取到 Context 的四种方式

很多时候我们需要在 RecyclerView.Adapter 中使用到 context,比如:利用 Glide 来加载网络图片的时候。

这时,我们该如何拿到 context 给 Glide 呢?

2.1 通过 Adapter 构造函数传入 Context

这是我之前最常用的一种方式,通过 Adapter 构造函数将当前Activity Context传进来,如下:

class MyAdapter(
    private val context: Context,
    private val dataList: List<MyData>
) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    

    class MyViewHolder(val binding: RcyItemViewBinding) :
        RecyclerView.ViewHolder(binding.root) {
    
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    
        val binding = RcyItemViewBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return MyViewHolder(binding)
    }

    override fun getItemCount(): Int = dataList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    
        val data = dataList[position]
        holder.binding.apply {
    
            Glide.with(context).load(data.imageUrl).into(holder.binding.imageIv)
            holder.binding.contentTv.text = data.content
        }
    }
}

2.2 通过 Parent.context 获取

这个也是文章开头中提到的我的同事的一种写法,如下:

class MyAdapter(private val dataList: List<MyData>) :
    RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    

    private lateinit var mContext: Context

    class MyViewHolder(val binding: RcyItemViewBinding) :
        RecyclerView.ViewHolder(binding.root) {
    

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    
        mContext = parent.context
        val binding = RcyItemViewBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return MyViewHolder(binding)
    }

    override fun getItemCount(): Int = dataList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    
        val data = dataList[position]
        holder.binding.apply {
    
            Glide.with(mContext).load(data.imageUrl).into(holder.binding.imageIv)
            holder.binding.contentTv.text = data.content
        }
    }
}

2.3 通过 onAttachedToRecyclerView() 方法获取

对于2.2 的方法,通过 parent.context 对 mContext 进行赋值,有人说,不可以这么操作!这样会导致内存泄露!!(留个疑问?你觉得2.2方法这样操作会导致内存泄漏吗?
所以你需要覆写 onAttachedToRecyclerView(recyclerView: RecyclerView) 方法,在这里对 mContext 进行赋值。如下:

class MyAdapter(private val dataList: List<MyData>) :
    RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    

    private lateinit var mContext: Context

    override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
    
        super.onAttachedToRecyclerView(recyclerView)
        mContext = recyclerView.context
    }

    class MyViewHolder(val binding: RcyItemViewBinding) :
        RecyclerView.ViewHolder(binding.root) {
    

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    
        val binding = RcyItemViewBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return MyViewHolder(binding)
    }

    override fun getItemCount(): Int = dataList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    
        val data = dataList[position]
        holder.binding.apply {
    
            Glide.with(mContext).load(data.imageUrl).into(holder.binding.imageIv)
            holder.binding.contentTv.text = data.content
        }
    }
}

2.4 通过 ImageView 获取 context

当当就我们举的这个例子,因为我们需要用到 Glide 来展示网络图片,所以我们需要传递 Context 给 Glide,其实我们可以直接通过 ImageView 来拿到 context,然后传给 Glide,如下:

class MyAdapter(private val dataList: List<MyData>) :
    RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    

    class MyViewHolder(val binding: RcyItemViewBinding) :
        RecyclerView.ViewHolder(binding.root) {
    

    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    
        val binding = RcyItemViewBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return MyViewHolder(binding)
    }

    override fun getItemCount(): Int = dataList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    
        val data = dataList[position]
        holder.binding.apply {
    
            Glide.with(imageIv.context).load(data.imageUrl).into(holder.binding.imageIv)
            holder.binding.contentTv.text = data.content
        }
    }
}

3. 问题的本质

上面介绍了四种方法来获取 Context,想必大家都想弄清楚上面几种方法有什么区别吧,那我们就打印他们获取到的 context 出来瞧瞧看吧,如下:

class MainActivity : AppCompatActivity() {
    

    private lateinit var binding: ActivityMainBinding
    
    ···
    
    override fun onCreate(savedInstanceState: Bundle?) {
    
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        Log.e("jctest", "onCreate: this::class.java = ${
      this::class.java}")
        Log.e("jctest", "onCreate: binding.recyclerView.context::class.java = ${
      binding.recyclerView.context::class.java}")
        val adapter = MyAdapter(listData)
        binding.recyclerView.adapter = adapter

    }

    class MyAdapter(private val dataList: List<MyData>) :
        RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    

        class MyViewHolder(val binding: RcyItemViewBinding) :
            RecyclerView.ViewHolder(binding.root) {
    

        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    
            Log.e("jctest", "onCreateViewHolder: parent::class.java = ${
      parent::class.java}")
            Log.e("jctest", "onCreateViewHolder: parent.context::class.java = ${
      parent.context::class.java}")
            val binding = RcyItemViewBinding.inflate(
                LayoutInflater.from(parent.context),
                parent,
                false
            )
            return MyViewHolder(binding)
        }

        override fun getItemCount(): Int = dataList.size

        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    
            val data = dataList[position]
            holder.binding.apply {
    
                Log.e("jctest", "onBindViewHolder: imageIv.context::class.java = ${
      imageIv.context::class.java}")
                Glide.with(imageIv.context).load(data.imageUrl).into(holder.binding.imageIv)
                holder.binding.contentTv.text = data.content
            }
        }

        override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
    
            super.onAttachedToRecyclerView(recyclerView)
            Log.e("jctest", "onAttachedToRecyclerView: recyclerView.context::class.java = ${
      recyclerView.context::class.java}")
        }

    }

}

打印出来的 Log 如下:

E/jctest: onCreate: this::class.java = class com.jc.test.MainActivity
E/jctest: onCreate: binding.recyclerView.context::class.java = class com.jc.test.MainActivity
E/jctest: onAttachedToRecyclerView: recyclerView.context::class.java = class com.jc.test.MainActivity
E/jctest: onCreateViewHolder: parent::class.java = class androidx.recyclerview.widget.RecyclerView
E/jctest: onCreateViewHolder: parent.context::class.java = class com.jc.test.MainActivity
E/jctest: onBindViewHolder: imageIv.context::class.java = class com.jc.test.MainActivity

通过 Log,真的就一目了然了,这四种方法虽然看着写法很不一样,但是其获取到的 context 其实都是同一个,那就是 MainActivity。

我们再来看看这四个方法:

  • 2.1 通过 Adapter 构造函数传入 this,这很明显,就是 MainActivity。
  • 2.2 通过 Parent.context 获取,通过 Log 我们可以知道,parent 就是 recylerView,所以获取的是 recyclerView 运行于的上下文,也就是 MainActivity。
  • 2.3 通过 onAttachedToRecyclerView() 获取,也是获取 recyclerView 运行于的上下文,即 MainActivity。
  • 2.4 通过 ImageView 获取,获取的是 ImageView 运行于的上下文,ImageView 是运行于 recyclerView 上的,所以获取的也是 MainActivity。

3.1 到底应该怎么用呢?

刚刚,我们通过打印 Log 知道了这四种方法获取到的 Context 其实都是同一个,那~~,这四种方法又该如何来取舍呢?到底用哪一种方法比较好呢?

文章开头中,我说 2.1 是我之前最常用的一个方法,那我现在为什么不用它了呢??

原因很简单,那就是因为之前我不知道可以直接在 Adapter 内部直接获取到 Context ,既然可以在类内部直接获取到的参数,完全就没有必要再写一个参数从外部导入,这样会让代码看着更加的简洁。

还记得文章中留的那个疑问吗 -> 有人说通过 2.2 parent.context 方法获取 context 会导致内存泄漏,你认同吗?

该写法不会导致内存泄漏,但是我也不推荐这样的写法,毕竟 onCreateViewHolder() 方法的执行次数是由 itemCount 所决定的,所以也就意味着 mContext = parent.context 也会执行 itemCount 次,明明一次就行,其它多余的操作,会造成不必要的开销。

那通过 onAttachedToRecyclerView() 方法,只会进行一次赋值操作,很OK啊,但毕竟他又是定义全局变量,又是覆写方法,太麻烦了,我们可以在简单一点。

3.2 我选择的方式

好像我把前面介绍的方法都给否认了,那我现在是怎么用的呢?还是以上面的例子,如下:

class MyAdapter(private val dataList: List<MyData>) :
    RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
    

    class MyViewHolder(
        private val context: Context,
        private val binding: RcyItemViewBinding
    ) :
        RecyclerView.ViewHolder(binding.root) {
    

        fun bind(data: MyData) {
    
            Glide.with(context).load(data.imageUrl).into(binding.imageIv)
            binding.contentTv.text = data.content
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    
        val binding = RcyItemViewBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return MyViewHolder(parent.context, binding)
    }

    override fun getItemCount(): Int = dataList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    
        holder.bind(dataList[position])
    }
}

我选择在 onCreateViewHolder() 通过 parent.context 方法获取 context,但是,不是赋值给全部变量 mContext,而是直接传给 ViewHolder作为一个私有变量供其使用。

4. 总结

文章中的看法都是我自己的个人观点,毕竟对于一个方法的好坏,就看你站在什么角度看待它,我并没有认为我的方法是最好的最值得推荐的,从而要求大家也这么写。相反,写这篇文章,纯粹就是觉得很有意思,因为文章中的介绍的这几种方法都是我身边出现的,我特想知道大家是用什么方式的呢?欢迎大家留言与我一起讨论。


OK,文章到此也就结束啦,

其实分享文章的最大目的正是等待着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。 另外,如果你觉得文章不错,对你有所帮助,请给我点个赞,就当鼓励,谢谢~Peace~!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jerechen/article/details/113086906

智能推荐

java 性能最高的队列_高性能队列disruptor浅析_田渊栋的博客-程序员宅基地

最近翻看技术文章,发现一个叫做disruptor的高性能内存队列占据了头版头条,好奇其高性能的秘密,于是对该框架进行了一些简单的研究。disruptor并非新出,而是从2013年到现在经历了3个大版本的迭代。本文有如下几个部分:disruptor如何使用?disruptor高性能的秘密?如何使用核心概念:EventFactory:用于生成一个事件提供消费者消费EventHandler:消费者处理器..._java中eventfactory

Spring bean注解后取对象-程序员宅基地

1、被注解的类 @Autowired private TfaAlarmDao tfaAlarmDao;2、取注解类ApplicationContext app = new FileSystemXmlApplicationContext("classpath:appContext-service-mockalarm.xml"); tfaAlarm

ElasticSearch学习---- Java操作ElasticSearch_水原千鹤的狗的博客-程序员宅基地

Java操作ElasticSearch①. 创建Java项目导入maven依赖②. 创建索引和类型1. 创建客户端操作对象2. 创建索引3. 删除索引4. 创建索引和类型③. 索引中创建文档④. 更新一条索引⑤. 删除一条索引⑥. 批量操作⑦. 查询操作1. 查询所有并排序2. term查询3. rang查询4. 分页查询5. 查询返回指定字段6. prefix 前缀查询7. wildcardQuery 通配符查询8. ids 多id查询9. fuzzy 模糊查询10. bool 布尔查询11. hi

视频制作的步骤详解_视频制作的详细步骤-程序员宅基地

想要把自己旅行的照片,或者宝宝成长的照片,即将结婚的婚纱照,再或者年会上要用的员工活动照,做成类似电子相册的视频出来,给领导同事家人朋友们分享,但是却苦于视频剪辑软件的繁琐复杂,不知如何下手吗,今天就给大家分享一个小网站,99mv,只需简单的三步,零技术要求,即可做成自己的视频大片,效果或酷炫或唯美,各种风格,绝对的高大上。制作前准备:当然是照片要全部挑选好(一般的_视频制作的详细步骤

把微软代码搬到BSP下调试的方法(作者 gooogleman foxmail com )-程序员宅基地

把微软代码搬到BSP下调试的方法(作者 gooogleman foxmail com )

linux用户开放crontab权限,linux – / etc / crontab权限-程序员宅基地

/ etc / crontab文件具有以下权限:-rw-R – R–我知道这个文件是用于系统cron作业的,其他用户不应该有权修改它.当前权限允许所有用户读取对文件的访问权限,使他们能够查看内容.是否有必要让所有用户都能读取/ etc / crontab?我相信所有用户都不应该知道一些管理命令,所以将权限更改为-rw-r —–不是更好吗?我使用了CentOS的权限字符串,并没有在其他发行版上测..._开启crontab权限

随便推点

spring 事务 mysql 锁_Spring中的事务与数据库中的锁关系_ji fi的博客-程序员宅基地

本文只先简单的介绍下Spring中的事务与DB中锁的关系。首先总结:Spring事务的实现本质上是使用的DB中的事务,而DB中的事务实现又主要依靠DB中的锁。所以spring事务本质上使用数据库锁,开启spring事务意味着使用数据库锁。所以大家一定要厘清DB事务与DB各种锁的原理与概念。后续我也研究一下DB锁,并结合具体的生产环境监控数据来谈谈。《以下是转载部分内容。主要是Spring事务的使用..._spring事务的锁

Matlab中vpa一直在忙,matlab数据类型转换遇到问题,及解决办法,sym,double ,vpa转换..._蓝大仙人的博客-程序员宅基地

vpaVariable precision arithmetic变量精度计算SyntaxR =vpa(A)R =vpa(A, d)DescriptionR =vpa(A)uses variable-precision arithmetic (VPA) to compute each element ofAtoddecimal digits of accuracy, whered..._matlab vpa无法转换数值

修改本地管理员密码脚本-程序员宅基地

通过域计算机开机脚本统一修改客户端本地管理员密码即可:strComputer = "."Set objUser = GetObject("WinNT://" & strComputer & "/Administrator, user")objUser.SetPassword "123456789"objUser.SetInfo ...

1- 深度学习之神经网络核心原理与算法-前馈神经网络-程序员宅基地

前馈神经网络Feedforward Neural Network网络结构(一般分两种)Back Propagation Networks - 反向传播网络RBF networks - 径向基函数神经网络BP网络是所有的神经网络中结构最为简单的一种网络。一般我们习惯把网络画成左边输入右边输出层的...

Groovy基础_groovy 分号-程序员宅基地

一、概述Gradle是一款优秀的构建系统工具,它是基于Groovy的DSL语言实现,Gradle在Android中具有很重要的位置,因此在学习Gradle之前,了解Groovy是必须的。Groovy是基于JVM虚拟机的一种动态语言,与Java非常类似,因此Java学习者入门Groovy基本没有任何障碍,并且Groovy完全兼容Java。1)Groovy之字符串1、在Java中,每条..._groovy 分号

数据生成器模板-程序员宅基地

数据要好好造,特别是出题时#pragma GCC optimize(3)#include&lt;bits/stdc++.h&gt;using namespace std;#define maxn (1 &lt;&lt; 22)#define rep(i,l,r) for(register int i = l ; i &lt;= r ; i++)#define repd(i,r,l) ...