注:本文是根据https://github.com/jeasonlzy/NineGridView进行扩展;
类似QQ空间,微信朋友圈,微博主页等,展示图片的九宫格控件;
原作者只有仿朋友圈样式和QQ空间样式,新增平铺样式;
源码在CcMall项目中:https://github.com/CuiChenbo/CcMall/tree/master/ninegridview
什么是平铺样式、效果图如下:
一张图、2或4张图 和多张图;
一张图:两张图:
四张图: 多张图:
效果就是一张图、2或4张图时也铺满屏幕;于是就对该开源框架进行了一下扩展,下面上代码;
一、新增平铺模式;
设置平铺模式的最大高度(也就是一张图时的高度);
public static final int MODE_TILE = 2; //平铺网格模式
private int tileMaxHeight = 200; // 平铺模式最大高度
<declare-styleable name="NineGridView">
<attr name="ngv_singleImageSize" format="dimension"/>
<attr name="ngv_singleImageRatio" format="float"/>
<attr name="ngv_gridSpacing" format="dimension"/>
<attr name="ngv_tileMaxHeight" format="dimension"/>
<attr name="ngv_maxSize" format="integer"/>
<attr name="ngv_mode" format="enum">
<enum name="fill" value="0"/>
<enum name="grid" value="1"/>
<enum name="tile" value="2"/>
</attr>
</declare-styleable>
二、设置平铺模式的控件大小 ;
2或4张时图片的高度是单张图片的0.7倍 , 多张图时高度时单张图片的0.5倍 , 当然这个是为了满足我这个项目的需求,这里也可以自定义(固定高度或宽高相等);
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ***
if (mImageInfo != null && mImageInfo.size() > 0) {
if (mode == MODE_TILE) { //平铺模式
if (mImageInfo.size() == 1) {
gridWidth = totalWidth;
gridHeight = tileMaxHeight;
} else if (mImageInfo.size() == 2 || mImageInfo.size() == 4) {
gridWidth = (totalWidth - gridSpacing) / 2;
gridHeight = (int) (tileMaxHeight * 0.7);
} else {
gridWidth = (totalWidth - gridSpacing * 2) / 3;
gridHeight = (int) (tileMaxHeight * 0.5);
}
} else {
// ***
}
width = gridWidth * columnCount + gridSpacing * (columnCount - 1) + getPaddingLeft() + getPaddingRight();
height = gridHeight * rowCount + gridSpacing * (rowCount - 1) + getPaddingTop() + getPaddingBottom();
}
setMeasuredDimension(width, height);
}
三、2或4张图的排列方式;
2张图时为1行 、 4张图时2为2行;
public void setAdapter(@NonNull NineGridViewAdapter adapter) {
// ***
//grid模式下,显示4张使用2X2模式
if (mode == MODE_GRID) {
if (imageCount == 4) {
rowCount = 2;
columnCount = 2;
}
} else if (mode == MODE_TILE) { //平铺模式
if (imageCount == 2) {
rowCount = 1;
columnCount = 2;
} else if (imageCount == 4) {
rowCount = 2;
columnCount = 2;
}
}
// ***
}
当然我这边也做了其它的小修改,比如图片可以设置资源文件之类的,这些的修改都不影响原框架的使用;
此源码已上传:https://github.com/CuiChenbo/CcMall/tree/master/ninegridview
下面是全部代码:
public class NineGridView extends ViewGroup {
public static final int MODE_FILL = 0; //填充模式,类似于微信
public static final int MODE_GRID = 1; //网格模式,类似于QQ,4张图会 2X2布局
public static final int MODE_TILE = 2; //平铺网格模式
private static ImageLoader mImageLoader; //全局的图片加载器(必须设置,否者不显示图片)
private int tileMaxHeight = 200; // 平铺模式最大高度
private int singleImageSize = 250; // 单张图片时的最大大小,单位dp
private float singleImageRatio = 1.0f; // 单张图片的宽高比(宽/高)
private int maxImageSize = 9; // 最大显示的图片数
private int gridSpacing = 3; // 宫格间距,单位dp
private int mode = MODE_FILL; // 默认使用fill模式
private int columnCount; // 列数
private int rowCount; // 行数
private int gridWidth; // 宫格宽度
private int gridHeight; // 宫格高度
private List<ImageView> imageViews;
private List<ImageInfo> mImageInfo;
private NineGridViewAdapter mAdapter;
public NineGridView(Context context) {
this(context, null);
}
public NineGridView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NineGridView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
DisplayMetrics dm = context.getResources().getDisplayMetrics();
gridSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gridSpacing, dm);
singleImageSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, singleImageSize, dm);
tileMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tileMaxHeight, dm);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NineGridView);
gridSpacing = (int) a.getDimension(R.styleable.NineGridView_ngv_gridSpacing, gridSpacing);
singleImageSize = a.getDimensionPixelSize(R.styleable.NineGridView_ngv_singleImageSize, singleImageSize);
tileMaxHeight = a.getDimensionPixelSize(R.styleable.NineGridView_ngv_tileMaxHeight, tileMaxHeight);
singleImageRatio = a.getFloat(R.styleable.NineGridView_ngv_singleImageRatio, singleImageRatio);
maxImageSize = a.getInt(R.styleable.NineGridView_ngv_maxSize, maxImageSize);
mode = a.getInt(R.styleable.NineGridView_ngv_mode, mode);
a.recycle();
imageViews = new ArrayList<>();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
int totalWidth = width - getPaddingLeft() - getPaddingRight();
if (mImageInfo != null && mImageInfo.size() > 0) {
if (mode == MODE_TILE) { //平铺模式
if (mImageInfo.size() == 1) {
gridWidth = totalWidth;
gridHeight = tileMaxHeight;
} else if (mImageInfo.size() == 2 || mImageInfo.size() == 4) {
gridWidth = (totalWidth - gridSpacing) / 2;
gridHeight = (int) (tileMaxHeight * 0.7); //自定义高度(单张图片的0.7倍)
} else {
gridWidth = (totalWidth - gridSpacing * 2) / 3;
gridHeight = (int) (tileMaxHeight * 0.5); //自定义高度
// gridHeight = gridWidth ; //宽高相等
}
} else {
if (mImageInfo.size() == 1) {
gridWidth = singleImageSize > totalWidth ? totalWidth : singleImageSize;
gridHeight = (int) (gridWidth / singleImageRatio);
//矫正图片显示区域大小,不允许超过最大显示范围
if (gridHeight > singleImageSize) {
float ratio = singleImageSize * 1.0f / gridHeight;
gridWidth = (int) (gridWidth * ratio);
gridHeight = singleImageSize;
}
} else {
// gridWidth = gridHeight = (totalWidth - gridSpacing * (columnCount - 1)) / columnCount;
//这里无论是几张图片,宽高都按总宽度的 1/3
gridWidth = gridHeight = (totalWidth - gridSpacing * 2) / 3;
}
}
width = gridWidth * columnCount + gridSpacing * (columnCount - 1) + getPaddingLeft() + getPaddingRight();
height = gridHeight * rowCount + gridSpacing * (rowCount - 1) + getPaddingTop() + getPaddingBottom();
}
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (mImageInfo == null) return;
int childrenCount = mImageInfo.size();
for (int i = 0; i < childrenCount; i++) {
ImageView childrenView = (ImageView) getChildAt(i);
int rowNum = i / columnCount;
int columnNum = i % columnCount;
int left = (gridWidth + gridSpacing) * columnNum + getPaddingLeft();
int top = (gridHeight + gridSpacing) * rowNum + getPaddingTop();
int right = left + gridWidth;
int bottom = top + gridHeight;
childrenView.layout(left, top, right, bottom);
if (mImageLoader != null) {
if (mImageInfo.get(i).thumbnailUrl != null) {
mImageLoader.onDisplayImage(getContext(), childrenView, mImageInfo.get(i).thumbnailUrl);
} else {
mImageLoader.onDisplayImage(getContext(), childrenView, mImageInfo.get(i).srcUrl);
}
}
}
}
/**
* 设置适配器
*/
public void setAdapter(@NonNull NineGridViewAdapter adapter) {
mAdapter = adapter;
List<ImageInfo> imageInfo = adapter.getImageInfo();
if (imageInfo == null || imageInfo.isEmpty()) {
setVisibility(GONE);
return;
} else {
setVisibility(VISIBLE);
}
int imageCount = imageInfo.size();
if (maxImageSize > 0 && imageCount > maxImageSize) {
imageInfo = imageInfo.subList(0, maxImageSize);
imageCount = imageInfo.size(); //再次获取图片数量
}
//默认是3列显示,行数根据图片的数量决定
rowCount = imageCount / 3 + (imageCount % 3 == 0 ? 0 : 1);
columnCount = 3;
//grid模式下,显示4张使用2X2模式
if (mode == MODE_GRID) {
if (imageCount == 4) {
rowCount = 2;
columnCount = 2;
}
} else if (mode == MODE_TILE) { //平铺模式
if (imageCount == 2) {
rowCount = 1;
columnCount = 2;
} else if (imageCount == 4) {
rowCount = 2;
columnCount = 2;
}
}
//保证View的复用,避免重复创建
if (mImageInfo == null) {
for (int i = 0; i < imageCount; i++) {
ImageView iv = getImageView(i);
if (iv == null) return;
addView(iv, generateDefaultLayoutParams());
}
} else {
int oldViewCount = mImageInfo.size();
int newViewCount = imageCount;
if (oldViewCount > newViewCount) {
removeViews(newViewCount, oldViewCount - newViewCount);
} else if (oldViewCount < newViewCount) {
for (int i = oldViewCount; i < newViewCount; i++) {
ImageView iv = getImageView(i);
if (iv == null) return;
addView(iv, generateDefaultLayoutParams());
}
}
}
//修改最后一个条目,决定是否显示更多
if (adapter.getImageInfo().size() > maxImageSize) {
View child = getChildAt(maxImageSize - 1);
if (child instanceof NineGridViewWrapper) {
NineGridViewWrapper imageView = (NineGridViewWrapper) child;
imageView.setMoreNum(adapter.getImageInfo().size() - maxImageSize);
}
}
mImageInfo = imageInfo;
requestLayout();
}
/**
* 获得 ImageView 保证了 ImageView 的重用
*/
private ImageView getImageView(final int position) {
ImageView imageView;
if (position < imageViews.size()) {
imageView = imageViews.get(position);
} else {
imageView = mAdapter.generateImageView(getContext());
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mAdapter.onImageItemClick(getContext(), NineGridView.this, position, mAdapter.getImageInfo());
}
});
imageViews.add(imageView);
}
return imageView;
}
/**
* 设置宫格间距
*/
public void setGridSpacing(int spacing) {
gridSpacing = spacing;
}
/**
* 设置只有一张图片时的宽
*/
public void setSingleImageSize(int maxImageSize) {
singleImageSize = maxImageSize;
}
/**
* 设置只有一张图片时的宽高比
*/
public void setSingleImageRatio(float ratio) {
singleImageRatio = ratio;
}
/**
* 设置最大图片数
*/
public void setMaxSize(int maxSize) {
maxImageSize = maxSize;
}
public int getMaxSize() {
return maxImageSize;
}
public static void setImageLoader(ImageLoader imageLoader) {
mImageLoader = imageLoader;
}
public static ImageLoader getImageLoader() {
return mImageLoader;
}
public interface ImageLoader {
/**
* 需要子类实现该方法,以确定如何加载和显示图片
*
* @param context 上下文
* @param imageView 需要展示图片的ImageView
* @param url 图片地址
*/
void onDisplayImage(Context context, ImageView imageView, String url);
void onDisplayImage(Context context, ImageView imageView, Integer src);
/**
* @param url 图片的地址
* @return 当前框架的本地缓存图片
*/
Bitmap getCacheImage(String url);
}
}
再次感谢 https://github.com/jeasonlzy/NineGridView 的开源,同时该作者还有一个优秀的网络框架https://github.com/jeasonlzy/okhttp-OkGo ;