Android(Kotlin)类似微博的九宫格图片显示控件
前言
在微博浏览的时候,我们可以看到一个类似下图的九宫格图片显示控件,类似的效果在微信朋友圈里面也有遇到。当只有一张图片的时候就显示一张图片占满布局宽度;如果有2-4张图片,则显示两列;如果有5-9张图片泽显示三列。
由于工作项目的原因,我也需要实现一个类似这样的图片显示控件,通过百度发现,现在有两种方式进行制作,一个是自定义View的方式,另一个是通过ViewGroup。在这里,我采用的是ViewGroup的方式实现(参考w4lle大神的源码)。
现在Android的官方语言已经变为了Kotlin,所以这里我使用的是Kotlin编写,直接上代码吧!
NineGridlayout
NineGridlayout
是图片显示的自定义ViewGroup,在layout布局文件里面直接使用就可以了
class NineGridlayout : ViewGroup {
private val TAG = "NineGridlayout"
private var adapter: NineGridAdapter? = null
private var onItemClickListerner: OnItemClickListener? = null
/**
* 默认图片间隔
*/
private val ITEM_GAP = 3
private val DEFAULT_WIDTH = 140
/**
* 图片之间的间隔
*/
var gap: Int = 0
private var columns: Int = 0//
private var rows: Int = 0//
private var totalWidth: Int = 0
internal var singleWidth = 0
internal var singleHeight = 0
private var defaultWidth: Int = 0
private var defaultHeight: Int = 0
private var oldCount: Int = 0
private var isFirstLayout = true
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
gap = dip2px(context, ITEM_GAP.toFloat())
defaultHeight = dip2px(context, DEFAULT_WIDTH.toFloat())
defaultWidth = defaultHeight
}
fun setDefaultWidth(defaultWidth: Int) {
this.defaultWidth = defaultWidth
}
fun setDefaultHeight(defaultHeight: Int) {
this.defaultHeight = defaultHeight
}
fun setAdapter(adapter: NineGridAdapter?) {
this.adapter = adapter
if (adapter == null) {
return
}
//初始化布局形状
generateChildrenLayout(adapter.getCount())
removeAllViews()
for (i in 0..adapter.getCount() - 1) {
val itemView = adapter.getView(i)
addView(itemView, generateDefaultLayoutParams())
}
oldCount = adapter.getCount()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
Log.e(TAG, "onMeasure")
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthMode = View.MeasureSpec.getMode(widthMeasureSpec)
val heightMode = View.MeasureSpec.getMode(heightMeasureSpec)
val sizeWidth = View.MeasureSpec.getSize(widthMeasureSpec)
val sizeHeight = View.MeasureSpec.getSize(heightMeasureSpec)
totalWidth = sizeWidth - paddingLeft - paddingRight
if (adapter != null && adapter!!.getCount() > 0) {
val measureHeight: Int
//计算单个图片的大小
singleWidth = (totalWidth - gap * (columns - 1)) / columns
singleHeight = singleWidth
measureChildren(View.MeasureSpec.makeMeasureSpec(singleWidth, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(singleHeight, View.MeasureSpec.EXACTLY))
measureHeight = singleHeight * rows + gap * (rows - 1) + paddingTop + paddingBottom
setMeasuredDimension(sizeWidth, measureHeight)
}
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
if (!isFirstLayout)
layoutChildrenView()
isFirstLayout = false
}
private fun layoutChildrenView() {
Log.e(TAG, "layoutChildrenView")
if (adapter == null || adapter!!.getCount() == 0) {
return
}
val childrenCount = adapter!!.getCount()
for (i in 0..childrenCount - 1) {
val position = findPosition(i)
val left = (singleWidth + gap) * position[1] + paddingLeft
val top = (singleHeight + gap) * position[0] + paddingTop
val right = left + singleWidth
val bottom = top + singleHeight
val childrenView = getChildAt(i) as ImageView
if (childrenCount == 1) {
//只有一张图片
childrenView.scaleType = ImageView.ScaleType.FIT_CENTER
} else {
childrenView.scaleType = ImageView.ScaleType.CENTER_CROP
}
val itemPosition = i
childrenView.setOnClickListener {
if (onItemClickListerner != null) {
onItemClickListerner!!.onClick(itemPosition)
}
}
childrenView.layout(left, top, right, bottom)
}
}
private fun findPosition(childNum: Int): IntArray {
val position = IntArray(2)
for (i in 0..rows - 1) {
for (j in 0..columns - 1) {
if (i * columns + j == childNum) {
position[0] = i//行
position[1] = j//列
break
}
}
}
return position
}
/**
* 根据图片个数确定行列数量
* 对应关系如下
* num row column
* 1 1 1
* 2 1 2
* 3-4 2 2
* 5-6 2 3
* 7-9 3 3
* @param length
*/
private fun generateChildrenLayout(length: Int) {
if (length == 1) {
rows = 1
columns = 1
} else if (length <= 4) {
rows = 2
columns = 2
} else if (length <= 6) {
rows = 2
columns = 3
} else {
rows = 3
columns = 3
}
singleWidth = (totalWidth - gap * (columns - 1)) / columns
singleHeight = singleWidth
}
fun setOnItemClickListener(onItemClickListerner: OnItemClickListener) {
this.onItemClickListerner = onItemClickListerner
}
companion object {
/**
* dp to px
*/
fun dip2px(context: Context, dpValue: Float): Int {
val scale = context.resources.displayMetrics.density
return (dpValue * scale + 0.5f).toInt()
}
}
}
NineGridAdapter
NineGridAdapter
图片的数据适配器
abstract class NineGridAdapter(protected var context: Context, protected var list: List) {
abstract fun getCount(): Int
abstract fun getUrl(positopn: Int): String
abstract fun getItemId(position: Int): Long
abstract fun getView(i: Int): View
}
使用方法
首先继承
NineGridAdapter
class NineGridsAdapter(context: Context, list: MutableList<String>) : NineGridAdapter(context, list) { override fun getCount(): Int { return if (list == null) 0 else list.size } override fun getUrl(positopn: Int): String { return list?.get(positopn) } override fun getItemId(position: Int): Long { return position.toLong() } override fun getView(i: Int): View { val iv = ImageView(context) iv.setScaleType(ImageView.ScaleType.CENTER_CROP) iv.setBackgroundColor(context.resources.getColor(R.color.color_gray_light)) Glide.with(context).load(getUrl(i)).into(iv) return iv }
}
设置Adapter显示数据,添加点击响应事件
val imageAdapter = NineGridsAdapter(mContext!!, imageList) mMvpView?.picLayout?.setAdapter(imageAdapter) mMvpView?.picLayout?.setOnItemClickListener(object : OnItemClickListener { override fun onClick(vararg args: Int) { clickImage(args[0]) } })
在此,我们的九宫格图片显示控件就制作完毕了,Enjoy It! 最后我们的实际显示效果图如下:
<h3>附:点击回掉接口</h3>
文中使用的OnItemClickListener
是一个自定义的点击回掉接口,附上代码
interface OnItemClickListener {
fun onClick(vararg args: Int)
}