Android 宫格图控件MultiImageView(RecyclerView适配器中展示不定数量的图片)

最近的github上的996ICU在IT界应该很多人听说了,不知道CSDN上什么是最受大家欢迎的,最重要的是大家开心就好,在北京 杭州 上海 广州等各地的同胞们每天记得给自己一个微笑......

实现效果如下图:

一.实现逻辑:

1.首先创建图片实体类

public class PhotoInfo
{public String url;public int w;public int h;public String getUrl(){return url;}public void setUrl(String url){this.url = url;}public int getW(){return w;}public void setW(int w){this.w = w;}public int getH(){return h;}public void setH(int h){this.h = h;}@Overridepublic String toString(){return "PhotoInfo{" +"url='" + url + '\'' +", w=" + w +", h=" + h +'}';}
}

2.这里的宽高用于处理单张图片时ImageView的宽高。

接着创建MultiImageView

package com.example.lttechdemo.view;import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.example.lttechdemo.R;
import com.example.lttechdemo.bean.PhotoInfo;
import com.example.lttechdemo.util.BaseUtils;import java.util.List;/*** Created by litong on 2018/2/23.* 宫格图控件*/public class MultiImageView extends LinearLayout
{private int pxOneMaxWandH; //单张图最大允许宽高private int pxMoreWandH = 0; //多张图的宽高private int MAX_PER_ROW_COUNT = 3; //每行显示最大数private int MAX_WIDTH = 0; //控件最大宽度private int pxImagePadding = BaseUtils.getInstance().dip2px(3); //图片间的间距private LayoutParams onePicPara; //一张图的布局参数private LayoutParams morePara, moreParaColumnFirst; //多张图的布局参数(非第一列、第一列)private LayoutParams rowPara; //行布局参数private List<PhotoInfo> imagesList; //照片的Url列表private OnItemClickListener mOnItemClickListener; //图片点击监听public MultiImageView(Context context){super(context);}public MultiImageView(Context context, AttributeSet attrs){super(context, attrs);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){if (MAX_WIDTH == 0){int width = MeasureSpec.getSize(widthMeasureSpec);if (width > 0){MAX_WIDTH = width;if (imagesList != null && imagesList.size() > 0){setList(imagesList);}}}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}/*** 设置图片URL*/public void setList(List<PhotoInfo> lists) throws IllegalArgumentException{if (lists == null){throw new IllegalArgumentException("imageList is null...");}imagesList = lists;if (MAX_WIDTH > 0){pxMoreWandH = (MAX_WIDTH - pxImagePadding * 2) / 3; //解决右侧图片和内容对不齐问题pxOneMaxWandH = MAX_WIDTH * 2 / 3;initImageLayoutParams();}initView();}/*** 根据imageView的数量初始化不同的View布局*/private void initView(){this.setOrientation(VERTICAL);this.removeAllViews();if (MAX_WIDTH == 0){//为了触发onMeasure()来测量MultiImageView的最大宽度,MultiImageView的宽设置为match_parentaddView(new View(getContext()));return;}if (imagesList == null || imagesList.size() == 0){return;}if (imagesList.size() == 1) //单图{addView(createImageView(0, false));} else //多图{int allCount = imagesList.size();if (allCount == 4) //四张图每行显示两张{MAX_PER_ROW_COUNT = 2;} else //其他每行显示三张{MAX_PER_ROW_COUNT = 3;}int rowCount = allCount / MAX_PER_ROW_COUNT+ (allCount % MAX_PER_ROW_COUNT > 0 ? 1 : 0); //行数for (int rowCursor = 0; rowCursor < rowCount; rowCursor++){LinearLayout rowLayout = new LinearLayout(getContext());rowLayout.setOrientation(LinearLayout.HORIZONTAL);rowLayout.setLayoutParams(rowPara);if (rowCursor != 0){rowLayout.setPadding(0, pxImagePadding, 0, 0);}int columnCount = allCount % MAX_PER_ROW_COUNT == 0 ? MAX_PER_ROW_COUNT: allCount % MAX_PER_ROW_COUNT; //最后一行的列数if (rowCursor != rowCount - 1) //非最后一行的列数{columnCount = MAX_PER_ROW_COUNT;}addView(rowLayout);int rowOffset = rowCursor * MAX_PER_ROW_COUNT; //行偏移for (int columnCursor = 0; columnCursor < columnCount; columnCursor++){int position = columnCursor + rowOffset; //图片位置rowLayout.addView(createImageView(position, true));}}}}/*** 创建ImageView*/private ImageView createImageView(int position, final boolean isMultiImage){PhotoInfo photoInfo = imagesList.get(position);ImageView imageView = new ImageView(getContext());if (isMultiImage) //多图{imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);imageView.setLayoutParams(position % MAX_PER_ROW_COUNT == 0 ? moreParaColumnFirst : morePara);//第一列图片不设置MarginLeft} else //单张图{imageView.setAdjustViewBounds(true);imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);//imageView.setMaxHeight(pxOneMaxWandH);int expectW = photoInfo.w;int expectH = photoInfo.h;if (expectW == 0 || expectH == 0){imageView.setLayoutParams(onePicPara);} else{int actualW = 0;int actualH = 0;float scale = ((float) expectH) / ((float) expectW);if (expectW > pxOneMaxWandH){actualW = pxOneMaxWandH;actualH = (int) (actualW * scale);} else if (expectW < pxMoreWandH){actualW = pxMoreWandH;actualH = (int) (actualW * scale);} else{actualW = expectW;actualH = expectH;}imageView.setLayoutParams(new LayoutParams(actualW, actualH));}}imageView.setId(photoInfo.url.hashCode());imageView.setOnClickListener(new ImageOnClickListener(position));imageView.setBackgroundColor(getResources().getColor(R.color.im_font_color_text_hint));Glide.with(getContext()).load(photoInfo.url).asBitmap().dontAnimate().placeholder(R.mipmap.ic_img_default).error(R.mipmap.ic_img_default).diskCacheStrategy(DiskCacheStrategy.RESULT).into(imageView);return imageView;}/*** 初始化布局参数*/private void initImageLayoutParams(){int wrap = LayoutParams.WRAP_CONTENT;int match = LayoutParams.MATCH_PARENT;onePicPara = new LayoutParams(wrap, wrap);moreParaColumnFirst = new LayoutParams(pxMoreWandH, pxMoreWandH);morePara = new LayoutParams(pxMoreWandH, pxMoreWandH);morePara.setMargins(pxImagePadding, 0, 0, 0);rowPara = new LayoutParams(match, wrap);}private class ImageOnClickListener implements OnClickListener{private int position;public ImageOnClickListener(int position){this.position = position;}@Overridepublic void onClick(View view){if (mOnItemClickListener != null){mOnItemClickListener.onItemClick(view, position);}}}public interface OnItemClickListener{void onItemClick(View view, int position);}//获取列数public int getColumn(){return MAX_PER_ROW_COUNT;}//获取图片间的间距public int getPxImagePadding(){return pxImagePadding;}public void setOnItemClickListener(OnItemClickListener onItemClickListener){mOnItemClickListener = onItemClickListener;}
}

3.先从入口setList()方法看起

    /*** 设置图片URL*/public void setList(List<PhotoInfo> lists) throws IllegalArgumentException{if (lists == null){throw new IllegalArgumentException("imageList is null...");}imagesList = lists;if (MAX_WIDTH > 0){pxMoreWandH = (MAX_WIDTH - pxImagePadding * 2) / 3; //解决右侧图片和内容对不齐问题pxOneMaxWandH = MAX_WIDTH * 2 / 3;initImageLayoutParams();}initView();}

4.设置图片URL的同时定义了一张图或多张图的图片宽高,并初始化了相关布局参数

    /*** 初始化布局参数*/private void initImageLayoutParams(){int wrap = LayoutParams.WRAP_CONTENT;int match = LayoutParams.MATCH_PARENT;onePicPara = new LayoutParams(wrap, wrap);moreParaColumnFirst = new LayoutParams(pxMoreWandH, pxMoreWandH);morePara = new LayoutParams(pxMoreWandH, pxMoreWandH);morePara.setMargins(pxImagePadding, 0, 0, 0);rowPara = new LayoutParams(match, wrap);}

5.这里的moreParaColumnFirst与morePara唯一差别在于,是否设置了marginLeft。因此,当包含多图时,第一列图片使用没有设置marginLeft的moreParaColumnFirst布局参数,非第一列图片使用设置marginLeft的morePara布局参数。

接着初始化相关控件

    /*** 根据imageView的数量初始化不同的View布局*/private void initView(){this.setOrientation(VERTICAL);this.removeAllViews();if (MAX_WIDTH == 0){//为了触发onMeasure()来测量MultiImageView的最大宽度,MultiImageView的宽设置为match_parentaddView(new View(getContext()));return;}if (imagesList == null || imagesList.size() == 0){return;}if (imagesList.size() == 1) //单图{addView(createImageView(0, false));} else //多图{int allCount = imagesList.size();if (allCount == 4) //四张图每行显示两张{MAX_PER_ROW_COUNT = 2;} else //其他每行显示三张{MAX_PER_ROW_COUNT = 3;}int rowCount = allCount / MAX_PER_ROW_COUNT+ (allCount % MAX_PER_ROW_COUNT > 0 ? 1 : 0); //行数for (int rowCursor = 0; rowCursor < rowCount; rowCursor++){LinearLayout rowLayout = new LinearLayout(getContext());rowLayout.setOrientation(LinearLayout.HORIZONTAL);rowLayout.setLayoutParams(rowPara);if (rowCursor != 0){rowLayout.setPadding(0, pxImagePadding, 0, 0);}int columnCount = allCount % MAX_PER_ROW_COUNT == 0 ? MAX_PER_ROW_COUNT: allCount % MAX_PER_ROW_COUNT; //最后一行的列数if (rowCursor != rowCount - 1) //非最后一行的列数{columnCount = MAX_PER_ROW_COUNT;}addView(rowLayout);int rowOffset = rowCursor * MAX_PER_ROW_COUNT; //行偏移for (int columnCursor = 0; columnCursor < columnCount; columnCursor++){int position = columnCursor + rowOffset; //图片位置rowLayout.addView(createImageView(position, true));}}}}

6.

这里分成单图和多图两种情况进行初始化。当只有一张图时,直接将图片放入创建好的单图ImageView中。当有多张图片时,需要根据图片的个数进行判断需要展示的行数和列数,以及列之间、行之间的偏移量等等,然后再一一add到创建的ImageView中,代码中已给出相关注释。

最后是创建ImageView

    /*** 创建ImageView*/private ImageView createImageView(int position, final boolean isMultiImage){PhotoInfo photoInfo = imagesList.get(position);ImageView imageView = new ImageView(getContext());if (isMultiImage) //多图{imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);imageView.setLayoutParams(position % MAX_PER_ROW_COUNT == 0 ? moreParaColumnFirst : morePara);//第一列图片不设置MarginLeft} else //单张图{imageView.setAdjustViewBounds(true);imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);//imageView.setMaxHeight(pxOneMaxWandH);int expectW = photoInfo.w;int expectH = photoInfo.h;if (expectW == 0 || expectH == 0){imageView.setLayoutParams(onePicPara);} else{int actualW = 0;int actualH = 0;float scale = ((float) expectH) / ((float) expectW);if (expectW > pxOneMaxWandH){actualW = pxOneMaxWandH;actualH = (int) (actualW * scale);} else if (expectW < pxMoreWandH){actualW = pxMoreWandH;actualH = (int) (actualW * scale);} else{actualW = expectW;actualH = expectH;}imageView.setLayoutParams(new LayoutParams(actualW, actualH));}}imageView.setId(photoInfo.url.hashCode());imageView.setOnClickListener(new ImageOnClickListener(position));imageView.setBackgroundColor(getResources().getColor(R.color.im_font_color_text_hint));Glide.with(getContext()).load(photoInfo.url).asBitmap().dontAnimate().placeholder(R.mipmap.ic_img_default).error(R.mipmap.ic_img_default).diskCacheStrategy(DiskCacheStrategy.RESULT).into(imageView);return imageView;}

7.

这里依然分成单图和多图两种情况进行创建。当是多图的时候,只需判断当前的图片是否位于第一列,从而进行设置相应的LayoutParam;而当单图时,如果该图片没有设置width和height,即宽高都为0,则ImageView使用我们最初定义的onePicPara布局参数,如果该图片设置了width和height,则需要与pxOneMaxWandH进行比较,具体是以宽定高,还是什么其他规则,则需要进行相关逻辑处理。

最后通过RecyclerView的adapter去使用该控件:

        @Overridepublic void convert(BaseViewHolder holder, NewsContent newsContent){MultiImageView multiImagView = holder.getView(R.id.multiImagView);List<PhotoInfo> images = newsContent.getImageurls();if (images.size() > 0){multiImagView.setList(imageInfo);}}

最后附上源码:源码在此等候多时       源码

Android 宫格图控件MultiImageView(RecyclerView适配器中展示不定数量的图片)相关推荐

  1. android自定义波浪图,Android自定义控件--波浪图控件

    今天给大家分享一个android的波浪图控件制作.具体效果如下图所示: 上次有个app使用了这个控件,感觉特别酷炫.今天讲解一下这个控件的思路分析与代码编写. 思路分析: 1.绘制波浪图 2.移动波浪 ...

  2. Android宫格动态列数,设置recyclerView的GridLayoutManager宫格间距

    遇到的问题 最近项目中用到了宫格列表布局,一直是用recyclerview作为列表开发控件,自然而然会用到其中的一个属性gridLayoutManager来做宫格布局列表.设计图是两行,并且是左右中都 ...

  3. android 分组宫格菜单,android 九宫格(16宫格)控件

    public class NineRectView extends ViewGroup { private Context ctx; private int wSize,hSize,row,colum ...

  4. Android自定义控件之轮播图控件

    背景 最近要做一个轮播图的效果,网上看了几篇文章,基本上都能找到实现,效果还挺不错,但是在写的时候感觉每次都要单独去重新在Activity里写一堆代码.于是自己封装了一下.这里只是做了下封装成一个控件 ...

  5. android listview 滑动条显示_第七十六回:Android中UI控件之RecyclerView基础

    各位看官们,大家好,上一回中咱们说的是Android中UI控件之ListView优化的例子,这一回咱们说的例子是UI控件之RecyclerView.闲话休提,言归正转.让我们一起Talk Androi ...

  6. Android手工打造脑图控件

    背景 所有的开发背景都是项目需要.先上屌炸天的设计图. 效果 导出效果不清晰,尽量看吧. 功能 脑图展示 样式订制(文字颜色.图标.样式.边框..) 折叠方式支持两种:a.同侧折叠不影响其他.b.同侧 ...

  7. Android常用酷炫控件(开源项目)github地址汇总

    转载一个很牛逼的控件收集帖... 第一部分 个性化控件(View) 主要介绍那些不错个性化的 View,包括 ListView.ActionBar.Menu.ViewPager.Gallery.Gri ...

  8. Android表格拖拽排序,Android 拖拽排序控件 DragGridView

    Android 拖拽排序控件 DragGridView Android 开发中,我们经常会遇到条目拖拽排序的需求,特别是在新闻类应用中就更普遍了.其实,我们在网上可以搜到许多关于拖拽排序的自定义控件, ...

  9. 安卓java代码写控件_安卓自定义流程进度图控件实例代码

    先上效果图: 如图,可实现设置:总流程数.已完进度程数.已完成颜色,各个标题 使用方法 1.导入compile 'com.github.pavlospt:circleview:1.3'依赖包(因为用到 ...

最新文章

  1. Beta 冲刺 (1/7)
  2. 几种特征选择方法的比较,孰好孰坏?
  3. 十七、深入Python异常处理
  4. 使用Eclipse构建Maven项目 (step-by-step)
  5. 小端字节序和大端字节序
  6. html特殊字符p如何屏蔽 asp,利用asp去除html标记的四种方法
  7. 运筹学笔记 整数规划
  8. 前端轻量级的toolTip插件-Tippy.js
  9. 国际物流、快递、空运、海运、FBA头程、专线分别都有什么不同
  10. 编程的思想是什么,如何建立编程思想,如何训练和提高编程思想?
  11. 微信表情的字符编号完整版【图文并茂哦 】
  12. 新冠肺炎的诊断与临床症状
  13. 操作系统内核框架图整理
  14. mysql 存储数据时,报错Cause: java.sql.SQLException: Incorrect string value: '\xF4\x80\xB3\x81\xE8\xAE...'
  15. 思岚科技机器人自主定位导航系统
  16. TVB的武侠怎么就拍得这么好看呢?
  17. 从应用到平台 – 云服务架构的演进过程
  18. 艾默生ITA2的UPS和依米康精密空调集中监控方案
  19. HTML5第一人称3D艺术画廊js特效
  20. 基于RS485的西门子PLC进行程序远程上下载与调试?

热门文章

  1. linux awk 多个分隔,awk支持多个记录分隔符的写法
  2. 【C语言】【unix c】文件锁的使用
  3. FreeSurfer 安装(Ubuntu20.04)
  4. Python计算21点扑克牌概率,判断是否要牌
  5. 好消息来了,欢迎来撩我!
  6. C++ 打怪游戏 原创 小镇5.0--怪物狂欢季(先导)
  7. 如何平均得到圆内点的随机分布
  8. Kesci的十套数据分析练习笔记
  9. NoSQL数据库———基础内容知多少
  10. 图片和ppm文件互转