有一年没发表文章了,语言都从java换为kotlin了。最近还是做了不少东西的,后面再慢慢更新吧,还是代码能带给人快乐,感情什么的滚一边去吧
1. 效果图
![](https://img.haomeiwen.com/i11096522/4c8034ddf75dd362.gif)
2. 实现思路
最终目标:
- 没有数据时,显示加号布局,选择图片达到最大值时,加号布局隐藏,当删掉一张图片后,加号布局又显示出来
里面用到的图片选择框架是知乎的matisse,图片加载是glide,权限申请是permissionx,具体的使用就不详细说明了,若有需要日后再单独出篇文章。
这里主要讲图片选择后recyclerview里加号布局的显示与隐藏,这里用到了recyclerview多布局实现
实现的大体步骤:
-
重写Adapter的
getItemCount()
方法,返回值为data.size+1
,这里的data是所选图片集合,这样就可以无论有没有数据,都给加号布局留下位置。 -
重写
getItemViewType()
方法,当position+1=getItemCount()
时,返回加号布局,否则返回图片布局。利用recyclerview的多布局,来实现加号布局和图片布局的切换。实现原理:因为position是从0开始计数,而
getItemCount()
因为我们返回data.size+1
,所以是从1开始计数,当data没有数据时,position为0,而getItemCount()
是1,此时position+1=getItemCount()
,所以显示加号布局。当data有数据,假设为2张时,getItemCount()
为3,position即下标为0,1时,显示图片布局,而position为2时,因为2+1=3,所以显示加号布局 -
重写
onBindViewHolder()
方法,当holder是加号的Holder时,判断data.size,当它>=最大值时,holder.itemView设置隐藏,否则就显示,这样就可以实现加号布局的显示隐藏效果
4. 具体实现
4.1 创建适配器
class MyCommonAdapter(private val data: MutableList<String>,private val maxNum: Int) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
//加号布局
val ADD_ITEM = 1
//图片布局
val PIC_ITEM = 2
}
4.2 创建多布局viewHolder
//加号布局
inner class AddViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
//普通布局
inner class PicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val pic: ImageView = itemView.findViewById(R.id.ivImage)
val del: ImageView = itemView.findViewById(R.id.ivDelete)
}
4.3 重写getItemViewType和getItemCount方法
/**
* 如果当前位置+1=itemCount,则代表它是最后一个,因为位置是从0计数的,而itemCount是从1计数
*/
override fun getItemViewType(position: Int): Int {
return if (position + 1 == itemCount) {
ADD_ITEM
} else {
PIC_ITEM
}
}
/**
* 返回的数量+1,为了给加号布局添加位置
*/
override fun getItemCount(): Int {
return data.size + 1
}
4.4 创建和绑定布局
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
ADD_ITEM -> {
var view =
LayoutInflater.from(parent.context).inflate(R.layout.add_item, parent, false)
return AddViewHolder(view)
}
else -> {
var view = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
return PicViewHolder(view)
}
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
//加号的布局
if (holder is AddViewHolder) {
//增加的布局
if (data.size >= maxNum) {
holder.itemView.visibility = View.GONE
} else {
holder.itemView.visibility = View.VISIBLE
holder.itemView.setOnClickListener {
onItemClickListener?.onItemAddClick(position)
}
}
}
//加载图片的布局
else {
Glide.with(holder.itemView.context).load(data[position])
.into((holder as PicViewHolder).pic)
holder.pic.setOnClickListener {
onItemClickListener?.onItemPicClick(position)
}
holder.del.setOnClickListener {
onItemClickListener?.onItemDelClick(position)
}
}
}
interface OnItemClickListener {
//点击增加按键
fun onItemAddClick(position: Int)
//点击删除按键
fun onItemDelClick(position: Int)
//点击图片
fun onItemPicClick(position: Int)
}
4.5 出现的问题
完成后效果如下
![](https://img.haomeiwen.com/i11096522/445735f5e7096691.gif)
你仔细观察,会出现当选完6个后,确实加号布局隐藏掉了,但是,由于getItemCount()
返回的是data.size()+1
,尽管隐藏掉了,但是还是会多出来一个加号的布局位置,如同所示,那该如何解决这个问题呢
解决方法:
-
getItemCount()
方法,当data.size()达到最大值时,返回data.size(),当没有达到最大值时,返回data.size()+1
-
getItemCount()
方法,当data.size()达到最大值时,getItemViewType()
返回图片布局,当没有达到时,按照原先的判断逻辑如果position+1=getItemCount()
则返回添加布局,否则返回图片布局 -
如此,就可以取消掉
onBindViewHolder()
里面当data.size达到最大值后对加号布局隐藏的操作逻辑了
更改完的效果如下
![](https://img.haomeiwen.com/i11096522/4c8034ddf75dd362.gif)
4.6 完整代码
class MyCommonAdapter(private val data: MutableList<String>,private val maxNum:Int) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val ADD_ITEM = 1
val PIC_ITEM = 2
private var onItemClickListener: OnItemClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
ADD_ITEM -> {
var view =
LayoutInflater.from(parent.context).inflate(R.layout.add_item, parent, false)
return AddViewHolder(view)
}
else -> {
var view = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
return PicViewHolder(view)
}
}
}
/**
* 当数量小于最大值时,返回的数量+1,为了给加号布局添加位置
* 否则就返回正常的数据大小(达到最大值后,不用给加号布局添加位置)
*/
override fun getItemCount(): Int {
return if(data.size<maxNum){
data.size+1
}else{
data.size
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
//加号的布局
if (holder is AddViewHolder) {
holder.itemView.setOnClickListener {
onItemClickListener?.onItemAddClick(position)
}
}
//加载图片的布局
else {
Glide.with(holder.itemView.context).load(data[position])
.into((holder as PicViewHolder).pic)
holder.pic.setOnClickListener {
onItemClickListener?.onItemPicClick(position)
}
holder.del.setOnClickListener {
onItemClickListener?.onItemDelClick(position)
}
}
}
/**
* 当数量达到最大值时,返回图片布局
* 否则,如果当前位置+1=itemCount,则代表它是最后一个,因为位置是从0计数的,而itemCount是从1计数
*/
override fun getItemViewType(position: Int): Int {
if(data.size==maxNum){
return PIC_ITEM
}else{
return if (position + 1 == itemCount) {
ADD_ITEM
} else {
PIC_ITEM
}
}
}
//加号布局
inner class AddViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
//普通布局
inner class PicViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val pic: ImageView = itemView.findViewById(R.id.ivImage)
val del: ImageView = itemView.findViewById(R.id.ivDelete)
}
//设置接口回调来实现点击功能
fun setOnMyClickListener(onClickListener: OnItemClickListener?) {
onItemClickListener = onClickListener
}
interface OnItemClickListener {
//点击增加按键
fun onItemAddClick(position: Int)
//点击删除按键
fun onItemDelClick(position: Int)
//点击图片
fun onItemPicClick(position: Int)
}
}
5. 总结
注意事项:
- 因为android10及以上开始使用分区存储,简单的动态申请读取权限,可能会出现选择完照片后recyclerview里加载不出图片,因为获取不到,简单的做法是manifest中加入
android:requestLegacyExternalStorage="true"
属性,但是在android11上就已经失效,需要对matisse进行更改,具体做法可参考这篇文章:Matisse 图片库在 Android10 上拍照,预览问题
做下来想想过程,其实还算是比较简单的,搞清楚里面的逻辑,其实就是用到了Recyclerview的Adapter的多布局,有了基础功能,图片的点击或者长按删除等效果都可以做。
Github地址,如果对你有帮助的话还麻烦给个start
网友评论