实现思想即自定义一个继承ViewGroup的view
需要实现三个类
1.NineGridView ,即自定义的ViewGroup
2.NineGridViewAdapter,数据适配器类
3.ImageInfo,图片信息的实体类
相应类的实现代码:
NineGridView
package com.gif.gifchannel.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.gif.gifchannel.R;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Answer on 2017/7/14.
*/
public class NineGridView extends ViewGroup {
// public static final int MODE_FILL = 0; //填充模式,类似于微信
// public static final int MODE_GRID = 1; //网格模式,类似于QQ,4张图会 2X2布局
private static ImageLoader mImageLoader; //全局的图片加载器(必须设置,否则不显示图片)
private clickL click;
private int singleImageSize = 250; // 单张图片时的最大大小,单位dp
private float singleImageRatio = 1.0f; // 单张图片的宽高比(宽/高)
private int maxImageSize = 9; // 最大显示的图片数
private int gridSpacing = 4; // 宫格间距,单位dp
// private int mode = MODE_FILL; // 默认使用fill模式
private int columnCount; // 列数
private int rowCount; // 行数
private int gridWidth; // 宫格宽度
private int gridHeight; // 宫格高度
private List imageViews;
private List 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);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NineGridView);
gridSpacing = (int) a.getDimension(R.styleable.NineGridView_ngv_gridSpacing, gridSpacing);
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) {
//只有1张图时 图片宽度即是总宽度 高度等于宽度
if (mImageInfo.size() == 1) {
gridWidth = totalWidth;
gridHeight = gridWidth;
Log.d("yzpzz","wid "+gridWidth+ "height "+gridHeight);
} else {
//按照有几列 来计算每张图片的宽度和高度
//gridWidth 、gridHeight表示每张图片占据的宽、高
gridWidth = gridHeight = (totalWidth - gridSpacing * (columnCount - 1)) / columnCount;
}
//此处width height就是我们即将为控件设置的总宽度 width可在layout中给定 height需要根据图片数量计算
width = gridWidth * columnCount + gridSpacing * (columnCount - 1) + getPaddingLeft() + getPaddingRight();
height = gridHeight * rowCount + gridSpacing * (rowCount - 1) + getPaddingTop() + getPaddingBottom();
Log.d("yzp","wid "+width+ "height "+height);
}
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();
//根据图片数量 创建相应个数的imageview 并根据设定的imageview的宽高以及间距 ,将imageview放置在正确的位置
for (int i = 0; i < childrenCount; i++) {
//得到父控件中相应的imageview
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;
Log.d("yyy","left "+left+"right "+right+ "top "+top+"bottom "+bottom);
childrenView.layout(left, top, right, bottom);
//imageloader是接口,调用接口中的onDisplayImage方法绘制imageview 。在使用的地方调用setImageLoader方法
//重写接口中的方法,可以使用不同的图片框架显示图片 如Glide Piccasso等
if (mImageLoader != null) {
mImageLoader.onDisplayImage(getContext(), childrenView, mImageInfo.get(i).imageUrl);
}
}
}
/** 设置适配器 */
//等到adapter 得到需要显示的图片信息
public void setAdapter(@NonNull NineGridViewAdapter adapter) {
mAdapter = adapter;
List 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 (imageCount == 4) {
rowCount = 2;
columnCount = 2;
}
//grid模式下,显示2张图片使用 2X1 模式
if (imageCount == 2) {
rowCount = 1;
columnCount = 2;
}
if(imageCount ==1){
rowCount =1;
columnCount =1;
}
//保证View的复用,避免重复创建
//mImageInfo=null 表示第一次创建
if (mImageInfo == null) {
for (int i = 0; i < imageCount; i++) {
ImageView iv = getImageView(i);
if (iv == null) return;
//添加相应个imageview到父控件 viewgroup中
addView(iv, generateDefaultLayoutParams());
}
} else {
//根据上次已有的imageview数量以及这次需要创建的imageview数量 移除或添加相应个数个imageview
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);
}
}*/
//将传过来的imageInfo信息赋给mImageInfo 用于判断下次是否需要全部创建
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());
click.imageClick();
}
});
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);
/* *//**
* @param url 图片的地址
* @return 当前框架的本地缓存图片
*//*
Bitmap getCacheImage(String url);*/
}
//设置图片的点击事件
public void setClick(clickL click){
this.click=click;
}
public interface clickL{
void imageClick();
}
}
NineGridViewAdapter
package com.gif.gifchannel.widget;
import android.content.Context;
import android.widget.ImageView;
import com.gif.gifchannel.R;
import java.io.Serializable;
import java.util.List;
/**
* Created by Answer on 2017/7/14.
* 适配器类,用于添加数据以及生成imageview
*/
public class NineGridViewAdapter implements Serializable {
protected Context context;
private List imageInfo;
public NineGridViewAdapter(Context context, List imageInfo) {
this.context = context;
this.imageInfo = imageInfo;
}
/**
* 如果要实现图片点击的逻辑,重写此方法即可
*
* @param context 上下文
* @param nineGridView 九宫格控件
* @param index 当前点击图片的的索引
* @param imageInfo 图片地址的数据集合
*/
protected void onImageItemClick(Context context, NineGridView nineGridView, int index, List imageInfo) {
}
/**
* 生成ImageView容器的方式,
* 如果需要自定义图片展示效果,重写此方法即可
*
* @param context 上下文
* @return 生成的 ImageView
*/
protected ImageView generateImageView(Context context) {
ImageView imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
//imageView.setImageResource(R.drawable.ic);
return imageView;
}
public List getImageInfo() {
return imageInfo;
}
public void setImageInfoList(List imageInfo) {
this.imageInfo = imageInfo;
}
}
ImageInfo
package com.gif.gifchannel.widget;
import java.io.Serializable;
/**
* Created by Answer on 2017/7/14.
* 图片信息的实体类,三个参数分别为图片的URL地址 图片的高度以及宽度
*/
public class ImageInfo implements Serializable {
public String imageUrl;
public int imageViewHeight;
public int imageViewWidth;
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public int getImageViewHeight() {
return imageViewHeight;
}
public void setImageViewHeight(int imageViewHeight) {
this.imageViewHeight = imageViewHeight;
}
public int getImageViewWidth() {
return imageViewWidth;
}
public void setImageViewWidth(int imageViewWidth) {
this.imageViewWidth = imageViewWidth;
}
@Override
public String toString() {
return "ImageInfo{" +
"imageUrl='" + imageUrl + '\'' +
", imageViewHeight=" + imageViewHeight +
", imageViewWidth=" + imageViewWidth +
'}';
}
}
自定义属性,在attrs.xml中
一般的应用场景是在列表中展示多条9宫格样式的图片,如微信朋友圈
因此需要在LIstView或RecyclerView的adapter对应的Item布局中引入NIneGridView, 在adapter中添加数据以及设置9宫格数据的适配器
使用方法:
1.布局中引入NineGridView,设置控件width,height,paddingLeft,paddingRight等信息
2.在ListView或RecyclerView的adapter中调用setImageLoader方法,确定如何加载和显示图片(就是用哪种图片框架)
NineGridView.setImageLoader(new NineGridView.ImageLoader() {
@Override
public void onDisplayImage(Context context, ImageView imageView, String url) {
Glide.with(context).load(url).priority(Priority.NORMAL).placeholder(R.mipmap.placehold).centerCrop().into(
imageView);
}
});
3.添加图片数据信息,创建NineGridViewAdapter,将包含图片信息的数据集合list添加进去,最后调用NineGridView的setAdapter方法,
设置数据适配器。
List imageInfoList = new ArrayList<>();
for (int i = 0; i < ugcItem.getImages().size(); i++) {
String url = ugcItem.getImages().get(i).getUrl();
ImageInfo info = new ImageInfo();
info.setImageUrl(url);
imageInfoList.add(info);
}
NineGridViewAdapter adapter = new NineGridViewAdapter(mContext, imageInfoList);
holder.ugc_item_ninegrid.setAdapter(adapter);