美文网首页Android开发Android技术知识Android开发经验谈
Android九宫格控件-可在ListView和Recycler

Android九宫格控件-可在ListView和Recycler

作者: 三好码农 | 来源:发表于2016-01-10 21:08 被阅读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. _!!!

    相关文章

      网友评论

        本文标题:Android九宫格控件-可在ListView和Recycler

        本文链接:https://www.haomeiwen.com/subject/unjqkttx.html