Android九宫格控件-可在ListView和Recycler
2016-01-10 本文已影响5293人
三好码农
需求场景
熟悉Android App开发的同学,肯定都清楚,如果要显示多张图片,类似九宫格,可以用GridView或者GridLayout来做,但是如果需求要求在ListView或者recyclerView 的每个item中都显示这样一个九宫格,那么GridView就不适用了,GridLayout可以实现,但是不是那么优雅,我们需要在item每次重绘时,加入添加或者删除逻辑。既然框架没有提供满足需求的控件,我们只能自己实现。
思路
我们需要显示多张图片,那么肯定选择ViewGroup无疑,其实也就是一个简单的自定义ViewGroup——SquareGridView。
自定义属性
1.我们需要图片之间的水平间距horizontalSpacing和垂直间距verticalSpacing。
2.我们需要图片的长宽比ratio,默认我们1。
3.我们需要一行显示的列数numColumns。
4.我们需要图片显示的最大总数maxSize,默认为9。
SquareGridView实现
自定义属性初始化
比较简单,纯为了充字数!_
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SquareGridView);
numColumns = typedArray.getInteger(R.styleable.SquareGridView_numColumns, DEFAULT_COLUMN_NUM);
maxSize = typedArray.getInteger(R.styleable.SquareGridView_maxSize, DEFAULT_MAX_SIZE);
horizontalSpacing = typedArray.getDimensionPixelSize(R.styleable.SquareGridView_horizontalSpacing, DEFAULT_HORIZONTAL_SPACE);
verticalSpacing = typedArray.getDimensionPixelSize(R.styleable.SquareGridView_verticalSpacing, DEFAULT_VERTICAL_SPACE);
ratio = typedArray.getFloat(R.styleable.SquareGridView_ratio, DEFAULT_RATIO);
if (typedArray != null) {
typedArray.recycle();
}
onMeasure实现
我们这里并不需要对SpeceMode进行特殊处理,只需要根据image数量计算宽度和高度。
int width, height;
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
width = widthSpecSize;
height = heightSpecSize;
//实际显示的image数量,不大于最大限制
int count = getRealCount();
//计算行数
float rowCount = (count + 0f) / numColumns;
int realRow = (int) Math.ceil(rowCount);
//计算每个image的宽度
childrenWidth = (width - getPaddingLeft() - getPaddingRight()
- (numColumns - 1) * horizontalSpacing) / numColumns;
//计算每个image的高度
childrenHeight = (int) (childrenWidth * ratio);
height = getPaddingTop() + getPaddingBottom() + realRow * childrenHeight
+ (realRow - 1) * verticalSpacing;
setMeasuredDimension(width, height);
onLayout实现
依次对image进行layout,也没什么好说的!
int count = getRealCount();
for (int i = 0; i < count; i ++) {
int row = i / numColumns;
int column = i % numColumns;
int left = getPaddingLeft() + column * horizontalSpacing + column * childrenWidth;
int top = getPaddingTop() + row * verticalSpacing + row * childrenHeight;
View childView = getChildAt(i);
childView.layout(left, top, left + childrenWidth, top + childrenHeight);
}
为SquareGridView定义数据源接口
我们可以定义一个接口,控件需要的数据,直接通过接口获取,接口定义如下!
public interface SquareViewAdapter<T> {
int getCount();
T getItem(int position);
String getImageUrl(int position);
void onItemClick(View view, int index, T t);
}
接口中定义的方法都很好理解,大家有疑问可以看源码,Github地址最后会给出。
设置数据源
public void setAdapter(final SquareViewAdapter adapter) {
this.squareViewAdapter = adapter;
int count = getRealCount();
int childCount = getChildCount();
int shortCount = count - childCount;
//判断现有的subviews,数量是否足够,不足的继续add,足够的话,多余的状态设为Gone即可。
if (shortCount > 0) {
//we need add new subview.
for (int i = 0;i < shortCount; i++) {
SimpleDraweeView simpleDraweeView = new SimpleDraweeView(getContext());
GenericDraweeHierarchyBuilder builder =
new GenericDraweeHierarchyBuilder(getResources());
builder.setPlaceholderImage(ContextCompat.getDrawable(getContext(),
R.drawable.default_load_failed_image),
ScalingUtils.ScaleType.FIT_XY);
simpleDraweeView.setHierarchy(builder.build());
simpleDraweeView.setTag(i + childCount);
ViewGroup.LayoutParams vlp = new ViewGroup.LayoutParams(
childrenWidth, childrenHeight);
this.addView(simpleDraweeView, vlp);
}
}else if(shortCount < 0){
for (int i = 0;i < Math.abs(shortCount); i ++) {
SimpleDraweeView simpleDraweeView = (SimpleDraweeView) getChildAt(i + count);
simpleDraweeView.setVisibility(View.GONE);
}
}
for (int i = 0;i < count; i++) {
final int index = i;
final SimpleDraweeView simpleDraweeView = (SimpleDraweeView) getChildAt(i);
simpleDraweeView.setVisibility(View.VISIBLE);
simpleDraweeView.setImageURI(Uri.parse(squareViewAdapter.getImageUrl(i)));
simpleDraweeView.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if (adapter != null) {
adapter.onItemClick(simpleDraweeView, index, adapter.getItem(index));
}
}
});
}
}
调用setAdapter即可刷新数据,更详细的用法参见我github项目里面的demo,_!最后实现的效果在listview 快速滚动时,非常流畅!!!
最后给出Github地址
https://github.com/aliouswang/SquareGridView
Gradle引用
compile 'com.aliouswang:library:1.0.0'