装饰器模式,如果伙伴们对于这个概念不熟悉的话,但你们一定在日常的工作中用到过,例如
val inputStream = BufferedInputStream(FileInputStream("abc.txt"))
我们常用的文件读写流,经常会用多个流相互包裹最终生成我们想要的流,这种设计模式其实就是装饰器模式。那么对于装饰器模式的概念,就是不通过继承的方式,来扩展某个对象的功能,达到我们想要实现的效果。
1 装饰器模式引入
首先,我们通过一个简单的示例,来了解装饰器设计模式的精妙之处。
interface ICar {
fun run()
}
这是一个经典的车类接口,其中Car是一个基础车类,功能比较单一
class Car : ICar {
override fun run() {
Log.e("TAG","car can run")
}
}
另外几个Car,功能逐渐多了起来
class BenzCar : ICar{
override fun run() {
Log.e("TAG","car can run")
Log.e("TAG","car has fly")
}
}
class AudiCar : ICar {
override fun run() {
Log.e("TAG","car can run")
Log.e("TAG","car can fly")
Log.e("TAG","car can swim")
}
}
如果我们想要使用某个Car,可以直接实例化
val car = BenzCar()
car.run()
伙伴们想一想,如果想要在原来的基础上拓展功能,岂不是每次都需要创建一个实例类,并实现其中的功能,如此一来会写很多模版代码,那么装饰器设计模式恰好能够解决这个问题。
装饰器模式,我们可以认为是锦上添花,在原来的基础上扩展某个对象的能力。例如AudiCar是在BenzCar的基础上做了扩展,那么就可以将BenzCar作为参数传入到AudiCar的构造方法中
class AudiCar : ICar {
private var car: ICar? = null
constructor(car: BenzCar) {
this.car = car
}
override fun run() {
car?.run()
Log.e("TAG", "car can swim")
}
}
在最终调用的时候,就可以创建BenzCar实例,传入AudiCar构造方法中,在执行run方法时,会兼并执行BenzCar的run方法。
val car = BenzCar()
val audiCar = AudiCar(car)
audiCar.run()
2 RecyclerView添加头部和尾部
与ListView不同的是,RecyclerView并不支持添加头部和尾部,但是RecyclerView使用的场景是比ListView要多的多,因此在实际的开发中,我们经常需要添加尾部,例如列表滑动到底部的时候,需要提示用户列表已经滑到了底部不能再滑动了。
2.1 设计思路
我们在使用RecyclerView的时候,经常使用RecyclerView.Adapter,它其实是展示的就是我们请求到的列表数据,但是并不包含头部和尾部。
因此,我们就可以在RecyclerView.Adapter的基础上做两个功能扩展,给RecyclerView加上头部和尾部
2.2 源码实现
2.2.1 基础框架搭建
看下上图,因为是要在RecyclerView.Adapter的基础上做装饰,因此在WrapperListAdapter的构造方法中传入原始的RecyclerView Adapter。
/**
* 该类为RecyclerView的装饰器Aadpter,可添加头部与尾部
* 支持添加多个头部与尾部
* 支持删除头部与尾部
*
* @param realAdapter 该Adapter仅展示数据,具体添加头尾部操作在WrapperListAdapter中完成
*/
class WrapperListAdapter(
val realAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IWrapperList {
/**存储头部View的集合*/
private val headerList: MutableList<View> by lazy {
mutableListOf()
}
/**存储尾部View的集合*/
private val footerList: MutableList<View> by lazy {
mutableListOf()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
}
override fun getItemCount(): Int {
return realAdapter.itemCount + headerList.size + footerList.size
}
override fun addHeaderView(header: View) {
if (!headerList.contains(header)) {
headerList.add(header)
refreshList()
}
}
override fun removeHeaderView(header: View) {
if (headerList.contains(header)) {
headerList.remove(header)
refreshList()
}
}
override fun addFooterView(foot: View) {
if (!footerList.contains(foot)) {
footerList.add(foot)
refreshList()
}
}
override fun removeFooterView(foot: View) {
if (footerList.contains(foot)) {
footerList.remove(foot)
refreshList()
}
}
override fun refreshList() {
notifyDataSetChanged()
}
}
因为我们支持添加多个头部和尾部,因此可以存储在List集合中,最终getItemCount返回的Item个数就是原始适配器的itemCount(网络请求获取到的列表数据个数)+ headerList的个数 + footerList的个数。
2.2.2 核心逻辑处理
假设当前的场景,相当于一共有3种样式,其中position 0-2是头部样式,3-200是主体样式,201为尾部样式,因此可以通过position区分不同的样式,从而在onCreateViewHolder中返回。
override fun onCreateViewHolder(parent: ViewGroup, position: Int): RecyclerView.ViewHolder {
/**判断头部样式展示*/
if (headerList.isNotEmpty() && position in 0 until headerList.size) {
return createHeaderFooterViewHolder(headerList[position])
}
/**判断主体样式展示*/
val startPosition = if (headerList.isNotEmpty()) headerList.size else 0
val endPosition =
if (headerList.isNotEmpty()) headerList.size + realAdapter.itemCount else realAdapter.itemCount
if (position in startPosition until endPosition) {
return realAdapter.onCreateViewHolder(parent, position)
}
/**判断尾部样式展示,eg 如果能走到这里,说明尾部还有view没有展示*/
return createHeaderFooterViewHolder(footerList[position - endPosition]) /**注意这里的取值*/
}
/**
* 创建头部或者尾部的ViewHolder
*/
private fun createHeaderFooterViewHolder(view: View): RecyclerView.ViewHolder {
return HeaderFooterViewHolder(view)
}
其实RecyclerView.Adapter的onCreateViewHolder方法中第二个参数为viewType类型,而不是position,这个需要做一次转换,将每个ItemView的索引作为ItemViewType返回。
override fun getItemViewType(position: Int): Int {
return position
}
那么我们先创建一个初级的Adapter,通过加载后展示效果
class SimpleAdapter : RecyclerView.Adapter<SimpleItemHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SimpleItemHolder {
return SimpleItemHolder(
LayoutSimpleItemBinding.bind(
LayoutInflater.from(parent.context)
.inflate(R.layout.layout_simple_item, parent, false)
)
)
}
override fun onBindViewHolder(holder: SimpleItemHolder, position: Int) {
holder.binding.itemView.text = "position==$position"
}
override fun getItemCount(): Int {
return 100
}
}
class SimpleItemHolder(
val binding: LayoutSimpleItemBinding
) : RecyclerView.ViewHolder(binding.root)
使用WrapperListAdapter包裹,然后就可以添加头部和尾部
rv_wrapper = findViewById(R.id.rv_wrapper)
val wrapperAdapter = WrapperListAdapter(SimpleAdapter())
rv_wrapper.adapter = wrapperAdapter
rv_wrapper.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
wrapperAdapter.addHeaderView(
LayoutInflater.from(this).inflate(R.layout.layout_header, rv_wrapper, false)
)
这里我们看到,虽然头部已经添加上了,但是主体列表的数据没有展示,这就是因为没有实现onBindViewHolder。
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (headerList.isNotEmpty() && position in 0 until headerList.size) {
return
}
/**主体数据展示*/
val startPosition = if (headerList.isNotEmpty()) headerList.size else 0
val endPosition =
if (headerList.isNotEmpty()) headerList.size + realAdapter!!.itemCount else realAdapter!!.itemCount
if (position in startPosition until endPosition) {
realAdapter?.onBindViewHolder(holder, position)
}
}
通过上图我们看到,数据已经展示出来了,但是还有一个问题就是,因为主体列表中,加载数据时还是会从请求的列表中加载数据,因此我们在传入position的时候,需要考虑是否存在头部,不然会导致数组越界。
val startPosition = if (headerList.isNotEmpty()) headerList.size else 0
val endPosition =
if (headerList.isNotEmpty()) headerList.size + realAdapter.itemCount else realAdapter.itemCount
if (position in startPosition until endPosition) {
realAdapter.onBindViewHolder(holder, position - headerList.size) /**注意这里的position*/
}
2.2.3 RecyclerView重新改造
既然我们现在已经封装好了可装饰头部和尾部的Adapter,是不是也可以像ListView那样,作为一个控件来提供支持添加头部和尾部
class WrapperRecyclerView : RecyclerView {
private var wrapperListAdapter: WrapperListAdapter? = null
@JvmOverloads
constructor(context: Context, attributes: AttributeSet? = null) : super(context, attributes)
override fun setAdapter(adapter: Adapter<ViewHolder>?) {
wrapperListAdapter = WrapperListAdapter(adapter!!)
super.setAdapter(wrapperListAdapter)
}
fun addHeaderView(header: View) {
wrapperListAdapter?.addHeaderView(header)
}
fun removeHeaderView(header: View) {
wrapperListAdapter?.removeHeaderView(header)
}
fun addFooterView(foot: View) {
wrapperListAdapter?.addFooterView(foot)
}
fun removeFooterView(foot: View) {
wrapperListAdapter?.removeFooterView(foot)
}
}
这样在调用setAdapter的时候,在这个方法内部做了Adapter的装饰处理,最终我们在调用的时候,只需要关系主体列表的实现,与之前使用RecyclerView并无二异,但是却支持了添加头部和尾部。
rv_wrapper = findViewById(R.id.rv_wrapper)
rv_wrapper.adapter = SimpleAdapter()
rv_wrapper.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
rv_wrapper.addHeaderView(
LayoutInflater.from(this).inflate(R.layout.layout_header, rv_wrapper, false)
)
rv_wrapper.addFooterView(
LayoutInflater.from(this).inflate(R.layout.layout_footer, rv_wrapper, false)
)
3 总结
其实这里只是简单地实现一个装饰器模式案例,在实际的场景中并不止这些,其实还需要配合分页来实现,如果有这些需求的伙伴可以评论下方留言,我可以整理后上传github。