越来越多的功能需要图片上传,所以封装了一个图片选取,展示的九宫格控件.包含了添加,删除,以及预览(为方便自定义,预览只提供点击方式外部自定义预览实现)的功能
效果.gif思路:自定义ViewGroup 包含RecycleView 实现九宫格自定义属性 设置展示的行数,最大添加的个数设置AddItem 图片的展示样式,以及设置关闭按钮和错误页展示的央视利用RecycleView 的 GridLayoutManager 设置九宫格展示根据逻辑需要在RecyclerView.Adapter中动态添加图片和Add页面的两种不同他的Item根据添加数据有没有AddItem类型动态处理数据实现:1.attrs.xml自定义属性
2.九宫格RecycleView的Adapter
package com.wu.addimport android.content.Contextimport android.util.DisplayMetricsimport android.view.LayoutInflaterimport android.view.Viewimport android.view.ViewGroupimport android.view.WindowManagerimport android.widget.RelativeLayoutimport androidx.databinding.DataBindingUtilimport androidx.recyclerview.widget.RecyclerViewimport com.bumptech.glide.Glideimport com.bumptech.glide.request.RequestOptionsimport com.wkq.lib_base.adapter.AddImagesViewHolderimport com.wu.add.databinding.ItemAddImagesBinding/** * * 作者:吴奎庆 * * 时间:2021/12/30 * * 用途: */class AddImagesAdapter(mContext: Context,limtNum: Int,addImgs: Int,addCloseImgs: Int,addErrImgs: Int,addLimitNums: Int) :RecyclerView.Adapter() {var mContext: Contextvar limtNum = 5var addImgs = -1var addCloseImgs = -1var addErrImgs = -1var addLimitNums = 9var addImages = ArrayList()init {this.mContext = mContextthis.limtNum = limtNumthis.addCloseImgs = addCloseImgsthis.addImgs = addImgsthis.addErrImgs = addErrImgsthis.addLimitNums = addLimitNums}var listener: OnAddClickListener? = nullfun 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.densityreturn (dp * density + 0.5).toInt()}private fun getScreenWidth(context: Context): Int {val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManagerval 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 = layoutvar dataBindingViewHolder: AddImagesViewHolder = AddImagesViewHolder(viedataBinding.root)dataBindingViewHolder.setBinding(viewBinding = viedataBinding)return dataBindingViewHolder}override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {var ktHolder = holder as? AddImagesViewHoldervar binding = ktHolder!!.getBinding() as ItemAddImagesBindingktHolder?.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.VISIBLEbinding.rlClose.visibility = View.VISIBLEbinding.rlAdd.visibility = View.GONEif (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.GONEbinding.rlAdd.visibility = View.VISIBLEbinding.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) {processData(lists)}fun addItem(info: AddImagesInfo) {addImages.add(info)notifyDataSetChanged()}fun getItems(): ArrayList? {if (this.addImages == null) this.addImages = ArrayList()return this.addImages}private fun processData(addDatas: List) {if (addDatas == null) returnvar newList = processEnd(getItems()!!)if (newList.size >= limtNum) returnif (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): ArrayList {if (addDatas == null) return addDatasvar 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.addimport android.content.Contextimport android.util.AttributeSetimport android.view.LayoutInflaterimport android.view.Viewimport android.widget.FrameLayoutimport androidx.databinding.DataBindingUtilimport androidx.recyclerview.widget.GridLayoutManagerimport 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 = 9private var mAdapter: AddImagesAdapter? = nullinit {this.mContext = mContextval 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? = nullfun setAddImagesViewListener(listener: AddImagesViewListener) {this.listener = listener}//初始换数据private fun initView() {var binding = DataBindingUtil.inflate(LayoutInflater.from(mContext),R.layout.layout_add_images,this,false)addView(binding.root)if (columnNums>5)columnNums=5binding.rvContent.layoutManager = GridLayoutManager(mContext, columnNums)mAdapter = AddImagesAdapter(mContext, columnNums,addImgs,addCloseImgs,addErrImgs,add_limit_nums)binding.rvContent.adapter = mAdaptermAdapter!!.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) {mAdapter!!.addItems(lists)}interface AddImagesViewListener {fun onAdd()fun onPreview(item:AddImagesInfo?,lists: List?)}}总结:自定义ViewGroup 中间的布局可以设置成动态添加RecycleView,图片预览功能可以根据项目需要封装在库中,在此没做单独设置(需要封装通用传递的数据),用到的小伙伴可以根据自己需要再单独扩展
引用:1.源码地址2.用到的相册项目地址