最近我在项目中,发现一个同事写 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,还可以这么操作!!第一次见到这样的写法,有点意思,由此有了这一篇文章。
很多时候我们需要在 RecyclerView.Adapter 中使用到 context,比如:利用 Glide 来加载网络图片的时候。
这时,我们该如何拿到 context 给 Glide 呢?
这是我之前最常用的一种方式,通过 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
}
}
}
这个也是文章开头中提到的我的同事的一种写法,如下:
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.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
}
}
}
当当就我们举的这个例子,因为我们需要用到 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
}
}
}
上面介绍了四种方法来获取 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。
我们再来看看这四个方法:
刚刚,我们通过打印 Log 知道了这四种方法获取到的 Context 其实都是同一个,那~~,这四种方法又该如何来取舍呢?到底用哪一种方法比较好呢?
文章开头中,我说 2.1 是我之前最常用的一个方法,那我现在为什么不用它了呢??
原因很简单,那就是因为之前我不知道可以直接在 Adapter 内部直接获取到 Context ,既然可以在类内部直接获取到的参数,完全就没有必要再写一个参数从外部导入,这样会让代码看着更加的简洁。
还记得文章中留的那个疑问吗 -> 有人说通过 2.2 parent.context 方法获取 context 会导致内存泄漏,你认同吗?
该写法不会导致内存泄漏,但是我也不推荐这样的写法,毕竟
onCreateViewHolder()
方法的执行次数是由 itemCount 所决定的,所以也就意味着mContext = parent.context
也会执行 itemCount 次,明明一次就行,其它多余的操作,会造成不必要的开销。
那通过 onAttachedToRecyclerView() 方法,只会进行一次赋值操作,很OK啊,但毕竟他又是定义全局变量,又是覆写方法,太麻烦了,我们可以在简单一点。
好像我把前面介绍的方法都给否认了,那我现在是怎么用的呢?还是以上面的例子,如下:
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
作为一个私有变量供其使用。
文章中的看法都是我自己的个人观点,毕竟对于一个方法的好坏,就看你站在什么角度看待它,我并没有认为我的方法是最好的最值得推荐的,从而要求大家也这么写。相反,写这篇文章,纯粹就是觉得很有意思,因为文章中的介绍的这几种方法都是我身边出现的,我特想知道大家是用什么方式的呢?欢迎大家留言与我一起讨论。
其实分享文章的最大目的正是等待着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。 另外,如果你觉得文章不错,对你有所帮助,请给我点个赞,就当鼓励,谢谢~Peace~!