自定义控件格子布局(CellLayout)
【自定义控件格子布局(CellLayout)】格子布局,支持正方形的子view,可用来做九宫格,十六宫格,只需改变列数就行了,也支持非正方形子view.主要是可以自动分配宽度,这才是正点。看下效果吧:
文章图片
九宫格
文章图片
16宫格
文章图片
非正方形格子
二话不说上代码:
自定义三个属性
自定义ViewGroup实现格子布局
/**
*
* @ClassName:CellLayout * @PackageName:com.nevermore.squarecelllayout * @Create On 2017/6/2511:36 * @author:xuchuanting * @Copyrights 2017/6/25 nevermore All rights reserved.
*/public class CellLayout extends ViewGroup {
private static final String TAG = "CellLayout";
private int columCount = 3;
private int space = 0;
private boolean isSquareMode = false;
private SparseIntArray rowMaxHeights = new SparseIntArray();
public CellLayout(Context context) {
this(context, null);
}public CellLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}public CellLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CellLayout);
space = typedArray.getDimensionPixelSize(R.styleable.CellLayout_cellSpcing, 0);
isSquareMode = typedArray.getBoolean(R.styleable.CellLayout_isSquareCell, true);
columCount = typedArray.getInteger(R.styleable.CellLayout_columCount,3);
typedArray.recycle();
}@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//总宽度
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Log.i(TAG, "widthSize: "+widthSize);
//根据列数columCount和间隙space求出每个格子宽度
int hs = space * (columCount - 1);
Log.i(TAG, "hs: "+hs);
int cellWidth = getCellWidth();
Log.i(TAG, "cellWidth: "+cellWidth);
int childCount = getChildCount();
rowMaxHeights.clear();
int rowMaxHeight = 0;
for (int i = 0;
i < childCount;
i++) {
View child = getChildAt(i);
LayoutParams layoutParams = child.getLayoutParams();
layoutParams.width = cellWidth;
if(isSquareMode){
layoutParams.height = cellWidth;
}
//测量子view
measureChild(child, widthMeasureSpec, heightMeasureSpec);
int childMeasuredHeight = child.getMeasuredHeight();
//保存子view宽高
child.measure(getChildMeasureSpec(widthMeasureSpec, getPaddingLeft()+getPaddingRight(), cellWidth),
getChildMeasureSpec(heightMeasureSpec, 0, isSquareMode ? cellWidth : childMeasuredHeight));
//记录每一行的最大高度
if (!isSquareMode) {
if (rowMaxHeight < childMeasuredHeight) {
rowMaxHeight = childMeasuredHeight;
}
//测量到一行的最后一个child保存该行的最大高度
if ((i + 1) % columCount == 0 || i == childCount - 1) {
int height = rowMaxHeight;
rowMaxHeights.put(getRowIndex(i), height);
//换行了
rowMaxHeight = 0;
}
}
}//总行高
int totalRowHeight = 0;
int rowCount = getRowCount();
//行数
if (isSquareMode) {
totalRowHeight = rowCount * cellWidth;
} else {
for (int i = 0;
i < rowMaxHeights.size();
i++) {
totalRowHeight += rowMaxHeights.get(i);
}
}
int heightSize = space * (rowCount - 1) + totalRowHeight+getPaddingTop()+getPaddingBottom();
setMeasuredDimension(widthSize, heightSize);
}/**
* 获取行数
*/
public int getRowCount() {
return (int) Math.ceil(getChildCount() / (double) columCount);
}/**
* 获取child所在的行数
* 从第0行算
*
* @param childIndex
* @return
*/
public int getRowIndex(int childIndex) {
return (int) Math.ceil((childIndex + 1) / (double) columCount) - 1;
}/**
* 获取总列数
*
* @return
*/
public int getColumCount() {
return columCount;
}/**
* 获取每格宽度
*
* @return
*/
public int getCellWidth() {
return (getMeasuredWidth() - space * (columCount - 1)-getPaddingRight()-getPaddingRight()) / columCount;
}@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {int paddingLeft = getPaddingLeft();
int paddingTop = getPaddingTop();
int left = paddingLeft;
int top = paddingTop;
int cellWidth = getCellWidth();
for (int i = 0;
i < getChildCount();
i++) {View child = getChildAt(i);
int height = isSquareMode?cellWidth:rowMaxHeights.get(getRowIndex(i));
child.layout(left, top, left + cellWidth, top + (isSquareMode ? cellWidth : child.getMeasuredHeight()));
if ((i + 1) % columCount == 0) {//换行了
left = paddingLeft;
top += height + space;
} else {
left += cellWidth + space;
}
}
}public interface OnItemClickListener {
void onItemClick(ViewGroup parent, View view, int position, long id);
}@Override
public void removeViewAt(int index) {
super.removeViewAt(index);
if (onItemClickListener != null) {
setOnItemClickListener(onItemClickListener);
}
}private OnItemClickListener onItemClickListener;
/**
* 写个适配器简化一下添加view
* 暂不支持回收
* @param adapter
*/
public void setAdapter(BaseAdapter adapter){
if(adapter==null){
return;
}
removeAllViews();
for (int i = 0;
i < adapter.getCount();
i++) {
addView(adapter.getView(i,null,null));
}}
/**
* Item的点击事件
*
* @param listener
*/
public void setOnItemClickListener(final OnItemClickListener listener) {
if (listener == null) {
return;
}
this.onItemClickListener = listener;
for (int i = 0;
i < getChildCount();
i++) {
final View child = getChildAt(i);
final int position = i;
child.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onItemClick(CellLayout.this, child, position, child.getId());
}
});
}
}
}
gayhub地址
推荐阅读
- SpringBoot调用公共模块的自定义注解失效的解决
- python自定义封装带颜色的logging模块
- 列出所有自定义的function和view
- Spring|Spring Boot 自动配置的原理、核心注解以及利用自动配置实现了自定义 Starter 组件
- 自定义MyAdapter
- Android自定义view实现圆环进度条效果
- Flutter自定义view|Flutter自定义view —— 闯关进度条
- js保留自定义小数点
- django|django 自定义.save()方法
- 如何在Kubernetes|如何在Kubernetes 里添加自定义的 API 对象(一)