越来越多的功能需要图片上传,所以封装了一个图片选取,展示的九宫格控件.包含了添加,删除,以及预览(为方便自定义,预览只提供点击方式外部自定义预览实现)的功能
效果.gif
思路:
- 自定义ViewGroup 包含RecycleView 实现九宫格
- 自定义属性 设置展示的行数,最大添加的个数设置AddItem 图片的展示样式,以及设置关闭按钮和错误页展示的央视
- 利用RecycleView 的 GridLayoutManager 设置九宫格展示
- 根据逻辑需要在RecyclerView.Adapter中动态添加图片和Add页面的两种不同他的Item
- 根据添加数据有没有AddItem类型动态处理数据
实现:
1.attrs.xml自定义属性
<declare-styleable name="AddImagesStyle">
<!--限制每行展示的个数-->
<attr name="column_nums" format="integer"/>
<!--限制添加的个数-->
<attr name="add_limit_nums" format="integer"/>
<!--添加Item图片资源-->
<attr name="add_imgs" format="reference"/>
<!--删除按钮的图片资源-->
<attr name="add_close_imgs" format="reference"/>
<!--错误展示的图片资源-->
<attr name="add_err_imgs" format="reference"/>
</declare-styleable>
2.九宫格RecycleView的Adapter
package com.wu.add
import android.content.Context
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.RelativeLayout
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.wkq.lib_base.adapter.AddImagesViewHolder
import com.wu.add.databinding.ItemAddImagesBinding
/**
*
* 作者:吴奎庆
*
* 时间:2021/12/30
*
* 用途:
*/
class AddImagesAdapter(
mContext: Context,
limtNum: Int,
addImgs: Int,
addCloseImgs: Int,
addErrImgs: Int,
addLimitNums: Int
) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var mContext: Context
var limtNum = 5
var addImgs = -1
var addCloseImgs = -1
var addErrImgs = -1
var addLimitNums = 9
var addImages = ArrayList<AddImagesInfo>()
init {
this.mContext = mContext
this.limtNum = limtNum
this.addCloseImgs = addCloseImgs
this.addImgs = addImgs
this.addErrImgs = addErrImgs
this.addLimitNums = addLimitNums
}
var listener: OnAddClickListener? = null
fun setOnAddListener(listener: OnAddClickListener) {
this.listener = listener
}
fun getHeight(): Int {
return (getScreenWidth(mContext)) / 5
}
private fun dip2px(context: Context, dp: Int): Int {
val density = context.resources.displayMetrics.density
return (dp * density + 0.5).toInt()
}
private fun getScreenWidth(context: Context): Int {
val wm = context
.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val outMetrics = DisplayMetrics()
wm.defaultDisplay.getMetrics(outMetrics)
return outMetrics.widthPixels
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
var viedataBinding: ItemAddImagesBinding =
DataBindingUtil.inflate(
LayoutInflater.from(mContext),
R.layout.item_add_images,
parent,
false
)
var layout = RelativeLayout.LayoutParams(getHeight(), getHeight())
layout.setMargins(dip2px(mContext,1), dip2px(mContext,1), dip2px(mContext,1), dip2px(mContext,1))
viedataBinding.root.layoutParams = layout
var dataBindingViewHolder: AddImagesViewHolder = AddImagesViewHolder(viedataBinding.root)
dataBindingViewHolder.setBinding(viewBinding = viedataBinding)
return dataBindingViewHolder
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
var ktHolder = holder as? AddImagesViewHolder
var binding = ktHolder!!.getBinding() as ItemAddImagesBinding
ktHolder?.getBinding()?.executePendingBindings()
if (addImgs!=-1){
binding!!.ivAdd.setBackgroundResource(addImgs)
}else{
binding!!.ivAdd.setBackgroundResource(R.drawable.iv_add_images_add)
}
if (addCloseImgs!=-1){
binding!!.ivClose.setBackgroundResource(addCloseImgs)
}else{
binding!!.ivClose.setBackgroundResource(R.drawable.iv_add_img_close)
}
binding.rlClose.setOnClickListener {
processDelete(getItem(position))
}
binding.rlAdd.setOnClickListener {
if (listener != null) listener!!.onAddClick(binding.rlAdd, getItem(position))
}
binding.ivContent.setOnClickListener {
if (listener != null) listener!!.onAddClick(binding.ivContent, getItem(position))
}
if (getItem(position)!!.type.equals("2")) {
binding.ivContent.visibility = View.VISIBLE
binding.rlClose.visibility = View.VISIBLE
binding.rlAdd.visibility = View.GONE
if (addErrImgs!=-1){
var requestOptions= RequestOptions().error(addErrImgs)
Glide.with(mContext).load(getItem(position)!!.imgUrl).apply(requestOptions).into(binding.ivContent)
}else{
var requestOptions= RequestOptions().error(R.drawable.iv_add_err)
Glide.with(mContext).load(getItem(position)!!.imgUrl).apply(requestOptions).into(binding.ivContent)
}
} else {
binding.ivContent.visibility = View.GONE
binding.rlAdd.visibility = View.VISIBLE
binding.rlClose.visibility = View.GONE
}
}
private fun processDelete(item: AddImagesInfo?) {
addImages.remove(item)
if (!hasAdd()) {
addItem(AddImagesInfo(R.drawable.iv_add_images_add, "1"))
} else {
notifyDataSetChanged()
}
}
fun addItems(lists: List<AddImagesInfo>) {
processData(lists)
}
fun addItem(info: AddImagesInfo) {
addImages.add(info)
notifyDataSetChanged()
}
fun getItems(): ArrayList<AddImagesInfo>? {
if (this.addImages == null) this.addImages = ArrayList()
return this.addImages
}
private fun processData(addDatas: List<AddImagesInfo>) {
if (addDatas == null) return
var newList = processEnd(getItems()!!)
if (newList.size >= limtNum) return
if (newList!!.size + addDatas.size < addLimitNums) {
newList!!.addAll(addDatas)
newList!!.add(AddImagesInfo("", "1"))
} else {
newList!!.addAll(addDatas.subList(0, addLimitNums - newList.size))
}
notifyDataSetChanged()
}
private fun processEnd(addDatas: ArrayList<AddImagesInfo>): ArrayList<AddImagesInfo> {
if (addDatas == null) return addDatas
var iterator = addDatas.iterator()
while (iterator.hasNext()) {
var info = iterator.next()
if (info.type.equals("1")) iterator.remove()
}
return addDatas
}
private fun hasAdd(): Boolean {
getItems()!!.forEach {
if (it.type.equals("1")) {
return true
}
}
return false
}
override fun getItemCount(): Int {
return addImages.size
}
fun getItem(position: Int): AddImagesInfo? {
if (addImages != null && position < addImages!!.size) {
return this.addImages!!.get(position)
}
return null
}
interface OnAddClickListener {
fun onAddClick(view: View?, item: AddImagesInfo?)
}
}
3.自定义ViewGroup
package com.wu.add
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.FrameLayout
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.GridLayoutManager
import com.wu.add.databinding.LayoutAddImagesBinding
/**
*
* 作者:吴奎庆
*
* 时间:2021/12/30
*
* 用途: 九宫格动态添加图片
*/
class AddImagesView @JvmOverloads constructor(
mContext: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(mContext, attrs, defStyleAttr) {
private var mContext: Context
// 添加的图片资源
private var addImgs = -1
//关闭的图片资源
private var addCloseImgs = -1
//错误页的图片资源
private var addErrImgs = -1
//列数
private var columnNums = 5
//最大图片数
private var add_limit_nums = 9
private var mAdapter: AddImagesAdapter? = null
init {
this.mContext = mContext
val tapeArray = mContext.obtainStyledAttributes(attrs, R.styleable.AddImagesStyle)
addImgs = tapeArray.getResourceId(R.styleable.AddImagesStyle_add_imgs, -1)
addCloseImgs = tapeArray.getResourceId(R.styleable.AddImagesStyle_add_close_imgs, -1)
addErrImgs = tapeArray.getResourceId(R.styleable.AddImagesStyle_add_err_imgs, -1)
columnNums = tapeArray.getInt(R.styleable.AddImagesStyle_column_nums, 5)
add_limit_nums = tapeArray.getInt(R.styleable.AddImagesStyle_add_limit_nums, 9)
initView()
}
var listener: AddImagesViewListener? = null
fun setAddImagesViewListener(listener: AddImagesViewListener) {
this.listener = listener
}
//初始换数据
private fun initView() {
var binding = DataBindingUtil.inflate<LayoutAddImagesBinding>(
LayoutInflater.from(mContext),
R.layout.layout_add_images,
this,
false
)
addView(binding.root)
if (columnNums>5)columnNums=5
binding.rvContent.layoutManager = GridLayoutManager(mContext, columnNums)
mAdapter = AddImagesAdapter(mContext, columnNums,addImgs,addCloseImgs,addErrImgs,add_limit_nums)
binding.rvContent.adapter = mAdapter
mAdapter!!.addItem(AddImagesInfo("", "1"))
mAdapter!!.setOnAddListener(object : AddImagesAdapter.OnAddClickListener {
override fun onAddClick(view: View?, item: AddImagesInfo?) {
when (view!!.id) {
R.id.rl_add -> {
if (listener!=null)
listener!!.onAdd()
}
R.id.iv_content -> {
if (listener!=null)
listener!!.onPreview(item,mAdapter!!.getItems())
}
}
}
})
}
//添加数据
fun addAddImages(lists: List<AddImagesInfo>) {
mAdapter!!.addItems(lists)
}
interface AddImagesViewListener {
fun onAdd()
fun onPreview(item:AddImagesInfo?,lists: List<AddImagesInfo>?)
}
}
总结:
自定义ViewGroup 中间的布局可以设置成动态添加RecycleView,图片预览功能可以根据项目需要封装在库中,在此没做单独设置(需要封装通用传递的数据),用到的小伙伴可以根据自己需要再单独扩展
网友评论