问题来源:我们知道,说说的图片显示方式,是会跟图片的数量来决定布局方式,我们没办法写死一个界面,我们必须要根据图片的多少来决定布局方式,所以我们可以继承 viewgroup, 来根据传入的图片多少 来进行动态的布局
需求:
1.最大为9张图片,当为1-3, 5-9张图片时,以 3列的表格布局
2.当为4张图片时,以 2x2 的表格布局
实现思路: 首先,viewgroup 中有 一个 addView() 方法,向布局中添加view,我们可以通过给 ImageView.layout(left, top, right, bottom) 来决定 imageview 在父元素的布局
一.图片排列算法:
1.实现 一个 int[] findPosition(int childNum) childNUm从0开始 根据传来的第childNUm 张图片,返回一个 矩阵的坐标值
当 childNum == i*column + j 时 (i,j) 就是我们要找的下标
for (int i = 0; i < mRows; i++) {
for (int j = 0; j < mColumns; j++) {
if ((i * mColumns + j) == childNum) {
position[0] = i;//行
position[1] = j;//列
break;
}
}
}
2.rows,columns 需要根据 图片数量来决定, 当为4的时候, columns=2 ,其他情况columns=3, 代码中通过方法 generateRowAndColumn实现
二.代码总体思路:
在 onLayout(boolean changed, int left, int top, int right, int bottom) 方法中 计算单个图片的宽度为 总宽度减去2个间隔宽度 /3
OnLayout 方法可能会调用很多次,所以加一个 flag 只当第一次调用时,才请求布局。
主要的布局逻辑代码 在私有的 refresh 方法中, 开头第一行要清除所有view, 当 传过来的urls 为null或size为0,将此viewgroup 隐藏,不然 显示,根据图片多少生成行数跟列数, 通过行数列数,用 代码中的 layoutParam()方法去生成viewgroup 的长度和宽度, 最后吧一个个 imagevie 添加进去就ok
三.完整代码
为抽象类
需要用户自定义的接口
displayImage // 显示图片
onClickImage // 单击图片的监听
public abstract class NineGridLayout extends ViewGroup {
private static final float DEFUALT_SPACING = 15f;// 图片之间的间隔
private static final int MAX_COUNT = 9;// 最大图片大小
protected Context mContext;
private float mSpacing = DEFUALT_SPACING;
private int mColumns;
private int mRows;
private int mTotalWidth;
private int mSingleWidth;
private boolean mIsShowAll = false;
private boolean mIsFirst = true;
private List<String> mUrlList = new ArrayList<>();
public NineGridLayout(Context context) {
super(context);
init(context);
}
public NineGridLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NineGridLayout);
mSpacing = typedArray.getDimension(R.styleable.NineGridLayout_sapcing, DEFUALT_SPACING);
typedArray.recycle();
init(context);
}
private void init(Context context) {
mContext = context;
if (getListSize(mUrlList) == 0) {
setVisibility(GONE);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// float width=MeasureSpec.getSize(widthMeasureSpec);
// widthMeasureSpec=MeasureSpec.makeMeasureSpec((int)width-10,MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mTotalWidth = right - left;
mSingleWidth = (int) ((mTotalWidth - mSpacing * 2) / 3);
Log.e("aaa","width:"+mSingleWidth);
if (mIsFirst) {
notifyDataSetChanged();
mIsFirst = false;
} else {
Log.e("this", "Nine 不是第一次调用");
}
}
/**
* 设置间隔
*
* @param spacing
*/
public void setSpacing(float spacing) {
mSpacing = spacing;
}
/**
*
* @param urlList
*/
public void setUrlList(List<String> urlList) {
if (getListSize(urlList) == 0) {
setVisibility(GONE);
return;
}
setVisibility(VISIBLE);
mUrlList.clear();
mUrlList.addAll(urlList);
if (!mIsFirst) {
notifyDataSetChanged();
}
}
/**
* 更新界面
*/
public void notifyDataSetChanged() {
post(new TimerTask() {
@Override
public void run() {
refresh();
}
});
}
private void refresh() {
removeAllViews();
int size = getListSize(mUrlList);
if (size > 0) {
setVisibility(VISIBLE);
} else {
setVisibility(GONE);
}
generateRowAndColumn(size);
layoutParams();
if (size == 1) {
String url = mUrlList.get(0);
ImageView imageView = createImageView(0, url);
layoutImageView(imageView, 0, url);
return;
}
for (int i = 0; i < size; i++) {
String url = mUrlList.get(i);
ImageView imageView;
imageView = createImageView(i, url);
layoutImageView(imageView, i, url);
}
}
/**
* 根据行数列数生成viewgroup大小
*/
private void layoutParams() {
int singleHeight = mSingleWidth;
// 根据子view数量确定高度
LayoutParams params = getLayoutParams();
params.height = (int) (singleHeight * mRows + mSpacing * (mRows - 1));
setLayoutParams(params);
}
/**
* 创建 imagview 并设置监听
* @param i
* @param url
* @return
*/
private ImageView createImageView(final int i, final String url) {
ImageView imageView = new ImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER);
imageView.setBackgroundColor(Color.GRAY);
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onClickImage(i, url, mUrlList);
}
});
return imageView;
}
/* 布局子view的位置
* @param imageView
* @param url
*/
private void layoutImageView(ImageView imageView, int i, String url) {
final int singleWidth = (int) ((mTotalWidth - mSpacing * (3 - 1)) / 3);
int singleHeight = singleWidth;
int[] position = findPosition(i); //postion[0]代表行 1代表列
int left = (int) ((singleWidth + mSpacing) * position[1]);
int top = (int) ((singleHeight + mSpacing) * position[0]);
int right = left + singleWidth;
int bottom = top + singleHeight;
imageView.layout(left, top, right, bottom);
addView(imageView);
displayImage(imageView, url);
}
/**
* 返回 传来的照片下标的坐标值
* @param childNum
* @return
*/
private int[] findPosition(int childNum) {
int[] position = new int[2];
for (int i = 0; i < mRows; i++) {
for (int j = 0; j < mColumns; j++) {
if ((i * mColumns + j) == childNum) {
position[0] = i;//行
position[1] = j;//列
break;
}
}
}
return position;
}
/**
* 根据图片个数确定行列数量
* @param length
*/
private void generateRowAndColumn(int length) {
if (length == 4) {
mColumns = 2;
} else {
mColumns =3;
}
if (length <= 3) {
mRows = 1;
} else if (length <= 6) {
mRows = 2;
} else {
mRows = 3;
}
}
/**
*
* @param list
* @return
*/
private int getListSize(List<String> list) {
if (list == null || list.size() == 0) {
return 0;
}
return list.size();
}
protected abstract void displayImage(ImageView imageView, String url);
protected abstract void onClickImage(int position, String url, List<String> urlList);
}