Android九宫格控件-可在ListView和RecyclerView中使用

需求场景

熟悉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. _!!!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏项勇

[Android学习整理]之监控并控制SystemUi(状态栏)的显示与隐藏

65130
来自专栏Android干货园

Android--仿淘宝商品详情(继续拖动查看详情)及标题栏渐变

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/52...

24410
来自专栏郭霖

Android自定义View的实现方法,带你一步步深入了解View(四)

不知不觉中,带你一步步深入了解View系列的文章已经写到第四篇了,回顾一下,我们一共学习了LayoutInflater的原理分析、视图的绘制流程、视图的状态及重...

30890
来自专栏分享达人秀

ViewPager轻松完成TabHost效果

上一期学习了ViewPager的简单使用,本期一起来学习ViewPager的更多用法。 ? 相信很多同学都使用过今日头条APP吧,一打开主界面就...

31870
来自专栏向治洪

android 仿音悦台页面交互效果

概述 新版的音悦台 APP 播放页面交互非常有意思,可以把播放器往下拖动,然后在底部悬浮一个小框,还可以左右拖动,然后回弹的时候也会有相应的效果,这种交互效果在...

26270
来自专栏向治洪

滑动开关按钮SlideSwich

iphone上有开关控件,很漂亮,其实android4.0以后也有switch控件,但是只能用在4.0以后的系统中,这就失去了其使用价值,而且我觉得它的界面也...

392100
来自专栏Android干货

Android开发实战(二十一):浅谈android:clipChildren属性

31530
来自专栏Android开发小工

自定义View基础(二)View的滑动

在移动设备上,滑动基本是基础特性。不管是用的最多的下拉刷新还是ViewPager,他们的基础都是滑动。View的滑动实现方法也是绚丽的自定义View的基础知识。

7320
来自专栏Android知识点总结

Android控件之ImageView

张风捷特烈个人网站,编程笔记请访问:http://www.toly1994.com

9400
来自专栏非著名程序员

简单好用的阴影库 ShadowLayout

在开发过程中常会遇见带阴影效果的控件,通过 SDK 提供的 CardView 和 android:elevation可以实现,也可以通过 .9 图实现。但是使用...

49150

扫码关注云+社区

领取腾讯云代金券