Android开发Android技术知识Android开发经验谈

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'
最后,“Please feel free to use!!!”, 欢迎各位同学Pull request. _!!!
上一篇下一篇

猜你喜欢

热点阅读