需求场景
熟悉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'
最后,“Please feel free to use!!!”, 欢迎各位同学Pull request. _!!!