Android NineGridLayout — 仿微信朋友圈和QQ空间的九宫格图片展示自定义控件
NineGridLayout
一个仿微信朋友圈和QQ空间的九宫格图片展示自定义控件。
GitHub:https://github.com/HMY314/NineGridLayout

一、介绍
1、当只有1张图时,可以自己定制图片宽高,也可以使用默认九宫格的宽高;
2、当只有4张图时,以2*2的方式显示;
3、除以上两种情况下,都是按照3列方式显示,但这时有一些细节:
a、如果只有9张图,当然是以3*3的方式显示;
b、如果超过9张图,可以设置是否全部显示。
如果设置不完全显示,则按照3*3的方式显示,但是在第9张图上会有一个带“+”号的数字,
代表还有几张没有显示,这里是模仿了QQ空间图片超出9张的显示方式;
如果设置全部显示,理所当然的将所有图片都显示出来。
4、图片被按下时,会有一个变暗的效果,这也是模仿微信朋友圈的效果。
二、使用方法
1、核心类是NineGridLayout,继承自ViewGroup的抽象类,所以我们实际项目使用需要继承它,并要实现3个方法,如下:
public abstract class NineGridLayout extends ViewGroup {
//******************************其他代码省略**************************
/**
* 显示一张图片
* @param imageView
* @param url
* @param parentWidth 父控件宽度
* @return true 代表按照九宫格默认大小显示,false 代表按照自定义宽高显示
*/
protected abstract boolean displayOneImage(RatioImageView imageView, String url, int parentWidth);
protected abstract void displayImage(RatioImageView imageView, String url);
/**
* 点击图片时执行
*/
protected abstract void onClickImage(int position, String url, List<String> urlList);
}
2、我这里用NineGridTestLayout继承NineGridLayout实现,displayOneImage()与displayImage()中的参数都是显示图片需要的,我这里用的是ImageLoader显示图片,当然你也可以用其他的。
public class NineGridTestLayout extends NineGridLayout {
protected static final int MAX_W_H_RATIO = ;
public NineGridTestLayout(Context context) {
super(context);
}
public NineGridTestLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected boolean displayOneImage(final RatioImageView imageView, String url, final int parentWidth) {
//这里是只显示一张图片的情况,显示图片的宽高可以根据实际图片大小自由定制,parentWidth 为该layout的宽度
ImageLoader.getInstance().displayImage(imageView, url, ImageLoaderUtil.getPhotoImageOption(), new ImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap bitmap) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
int newW;
int newH;
if (h > w * MAX_W_H_RATIO) {//h:w = 5:3
newW = parentWidth / ;
newH = newW * / ;
} else if (h < w) {//h:w = 2:3
newW = parentWidth * / ;
newH = newW * / ;
} else {//newH:h = newW :w
newW = parentWidth / ;
newH = h * newW / w;
}
setOneImageLayoutParams(imageView, newW, newH);
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
}
});
return false;// true 代表按照九宫格默认大小显示(此时不要调用setOneImageLayoutParams);false 代表按照自定义宽高显示。
}
@Override
protected void displayImage(RatioImageView imageView, String url) {
ImageLoaderUtil.getImageLoader(mContext).displayImage(url, imageView, ImageLoaderUtil.getPhotoImageOption());
}
@Override
protected void onClickImage(int i, String url, List<String> urlList) {
Toast.makeText(mContext, "点击了图片" + url, Toast.LENGTH_SHORT).show();
}
}
3、在xml中实现
<com.hmy.ninegridlayout.view.NineGridTestLayout xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layout_nine_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:sapcing="4dp" />
app:sapcing是设置九宫格中图片之间的间隔。
4、使用:
public List<String> urlList = new ArrayList<>();//图片url
NineGridTestLayout layout = (NineGridTestLayout) view.findViewById(R.id.layout_nine_grid);
layout.setIsShowAll(false); //当传入的图片数超过9张时,是否全部显示
layout.setSpacing(); //动态设置图片之间的间隔
layout.setUrlList(urlList); //最后再设置图片url
三、核心类
NineGridLayout.java
/**
* 描述:
* 作者:HMY
* 时间:2016/5/10
*/
public abstract class NineGridLayout extends ViewGroup { private static final float DEFUALT_SPACING = 3f;
private static final int MAX_COUNT = ; 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) == ) {
setVisibility(GONE);
}
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
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 * ( - )) / );
if (mIsFirst) {
notifyDataSetChanged();
mIsFirst = false;
}
} /**
* 设置间隔
*
* @param spacing
*/
public void setSpacing(float spacing) {
mSpacing = spacing;
} /**
* 设置是否显示所有图片(超过最大数时)
*
* @param isShowAll
*/
public void setIsShowAll(boolean isShowAll) {
mIsShowAll = isShowAll;
} public void setUrlList(List<String> urlList) {
if (getListSize(urlList) == ) {
setVisibility(GONE);
return;
}
setVisibility(VISIBLE); mUrlList.clear();
mUrlList.addAll(urlList); if (!mIsFirst) {
notifyDataSetChanged();
}
} public void notifyDataSetChanged() {
removeAllViews();
int size = getListSize(mUrlList);
if (size > ) {
setVisibility(VISIBLE);
} else {
setVisibility(GONE);
} if (size == ) {
String url = mUrlList.get();
RatioImageView imageView = createImageView(, url); //避免在ListView中一张图未加载成功时,布局高度受其他item影响
LayoutParams params = getLayoutParams();
params.height = mSingleWidth;
setLayoutParams(params);
imageView.layout(, , mSingleWidth, mSingleWidth); boolean isShowDefualt = displayOneImage(imageView, url, mTotalWidth);
if (isShowDefualt) {
layoutImageView(imageView, , url, false);
} else {
addView(imageView);
}
return;
} generateChildrenLayout(size);
layoutParams(); for (int i = ; i < size; i++) {
String url = mUrlList.get(i);
RatioImageView imageView;
if (!mIsShowAll) {
if (i < MAX_COUNT - ) {
imageView = createImageView(i, url);
layoutImageView(imageView, i, url, false);
} else { //第9张时
if (size <= MAX_COUNT) {//刚好第9张
imageView = createImageView(i, url);
layoutImageView(imageView, i, url, false);
} else {//超过9张
imageView = createImageView(i, url);
layoutImageView(imageView, i, url, true);
break;
}
}
} else {
imageView = createImageView(i, url);
layoutImageView(imageView, i, url, false);
}
}
} private void layoutParams() {
int singleHeight = mSingleWidth; //根据子view数量确定高度
LayoutParams params = getLayoutParams();
params.height = (int) (singleHeight * mRows + mSpacing * (mRows - ));
setLayoutParams(params);
} private RatioImageView createImageView(final int i, final String url) {
RatioImageView imageView = new RatioImageView(mContext);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onClickImage(i, url, mUrlList);
}
});
return imageView;
} /**
* @param imageView
* @param url
* @param showNumFlag 是否在最大值的图片上显示还有未显示的图片张数
*/
private void layoutImageView(RatioImageView imageView, int i, String url, boolean showNumFlag) {
final int singleWidth = (int) ((mTotalWidth - mSpacing * ( - )) / );
int singleHeight = singleWidth; int[] position = findPosition(i);
int left = (int) ((singleWidth + mSpacing) * position[]);
int top = (int) ((singleHeight + mSpacing) * position[]);
int right = left + singleWidth;
int bottom = top + singleHeight; imageView.layout(left, top, right, bottom); addView(imageView);
if (showNumFlag) {//添加超过最大显示数量的文本
int overCount = getListSize(mUrlList) - MAX_COUNT;
if (overCount > ) {
float textSize = ;
final TextView textView = new TextView(mContext);
textView.setText("+" + String.valueOf(overCount));
textView.setTextColor(Color.WHITE);
textView.setPadding(, singleHeight / - getFontHeight(textSize), , );
textView.setTextSize(textSize);
textView.setGravity(Gravity.CENTER);
textView.setBackgroundColor(Color.BLACK);
textView.getBackground().setAlpha(); textView.layout(left, top, right, bottom);
addView(textView);
}
}
displayImage(imageView, url);
} private int[] findPosition(int childNum) {
int[] position = new int[];
for (int i = ; i < mRows; i++) {
for (int j = ; j < mColumns; j++) {
if ((i * mColumns + j) == childNum) {
position[] = i;//行
position[] = j;//列
break;
}
}
}
return position;
} /**
* 根据图片个数确定行列数量
*
* @param length
*/
private void generateChildrenLayout(int length) {
if (length <= ) {
mRows = ;
mColumns = length;
} else if (length <= ) {
mRows = ;
mColumns = ;
if (length == ) {
mColumns = ;
}
} else {
mColumns = ;
if (mIsShowAll) {
mRows = length / ;
int b = length % ;
if (b > ) {
mRows++;
}
} else {
mRows = ;
}
} } protected void setOneImageLayoutParams(RatioImageView imageView, int width, int height) {
imageView.setLayoutParams(new LayoutParams(width, height));
imageView.layout(, , width, height); LayoutParams params = getLayoutParams();
// params.width = width;
params.height = height;
setLayoutParams(params);
} private int getListSize(List<String> list) {
if (list == null || list.size() == ) {
return ;
}
return list.size();
} private int getFontHeight(float fontSize) {
Paint paint = new Paint();
paint.setTextSize(fontSize);
Paint.FontMetrics fm = paint.getFontMetrics();
return (int) Math.ceil(fm.descent - fm.ascent);
} /**
* @param imageView
* @param url
* @param parentWidth 父控件宽度
* @return true 代表按照九宫格默认大小显示,false 代表按照自定义宽高显示
*/
protected abstract boolean displayOneImage(RatioImageView imageView, String url, int parentWidth); protected abstract void displayImage(RatioImageView imageView, String url); protected abstract void onClickImage(int position, String url, List<String> urlList);
}
RatioImageView.Java
该类有两个功能:
1、是用于ImageView被按下时有变暗效果
2、ImageView的宽高根据设置的比例动态适配高度,如在xml中设置 app:ratio="2" ,ImageView的高度根据其宽度改变,但始终是宽的2倍,该功能在该项目中没有使用。
/**
* 根据宽高比例自动计算高度ImageView
* Created by HMY on 2016/4/21.
*/
public class RatioImageView extends ImageView { /**
* 宽高比例
*/
private float mRatio = 0f; public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} public RatioImageView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatioImageView); mRatio = typedArray.getFloat(R.styleable.RatioImageView_ratio, 0f);
typedArray.recycle();
} public RatioImageView(Context context) {
super(context);
} /**
* 设置ImageView的宽高比
*
* @param ratio
*/
public void setRatio(float ratio) {
mRatio = ratio;
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
if (mRatio != ) {
float height = width / mRatio;
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} @Override
public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Drawable drawable = getDrawable();
if (drawable != null) {
drawable.mutate().setColorFilter(Color.GRAY,
PorterDuff.Mode.MULTIPLY);
}
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
Drawable drawableUp = getDrawable();
if (drawableUp != null) {
drawableUp.mutate().clearColorFilter();
}
break;
} return super.onTouchEvent(event);
} }
代码可在我的GitHub上下载,地址:https://github.com/HMY314/NineGridLayout
效果图

Android NineGridLayout — 仿微信朋友圈和QQ空间的九宫格图片展示自定义控件的更多相关文章
- Android 仿微信朋友圈发动态功能(相册图片多选)
代码分享 代码名称: 仿微信朋友圈发动态功能(相册图片多选) 代码描述: 仿微信朋友圈发动态功能(相册图片多选) 代码托管地址: http://www.apkbus.com/android-15276 ...
- Android 高仿微信朋友圈动态, 支持双击手势放大并滑动查看图片。
转载请注明出处:http://blog.csdn.net/sk719887916/article/details/40348873 作者skay: 最近参与了开发一款旅行APP,其中包含实时聊天和动态 ...
- Android 仿微信朋友圈添加图片
github地址(欢迎下载Demo) https://github.com/zhouxu88/WXCircleAddPic 老习惯,先上图,着急用的朋友,直接带走Demo,先拿来用吧,毕竟老板催的紧, ...
- Android仿微信朋友圈,全文收起功能,附源码
在众多的社交类软件中,朋友圈是必不可少的,可以与好友.同学等分享自己的日常和有意思的事情,在开发社交类App时,朋友圈发表的内容你不可能让他全部显示,全部显示的话用户体验度会非常不好,这时就要用到全文 ...
- Android 仿微信朋友圈发表图片拖拽和删除功能
朋友圈实现原理 我们使用 Android Device Monitor 来分析朋友圈发布图片的界面实现原理.如果需要分析其他应用的界面实现也是采用这种方法哦. 打开 Android Device Mo ...
- React Native(九)——实现仿微信朋友圈发表动态功能
好像很久都没写博客了,罪过罪过~ 许是因为刚接触App有点生疏不得窍门吧,这个月还没有更新过文章.小个把月下来,还是觉得应该边学边总结,这样才能像大神靠近(最近刚接触同业的大牛级人物,还是从中学到了很 ...
- Android 仿微信朋友圈拍小视频上传到服务器
这个接上一个写的实现拍小视频和传到服务器的 界面是这个样子滴. 我也知不知道怎么给图片搞小一点o(╯□╰)o 布局文件是这样的[认真脸] <?xml version="1.0&quo ...
- Android 仿微信朋友圈查看
项目要做一个类似于这样的功能,就做了. 项目下载地址:http://download.csdn.net/detail/u014608640/9917626 一,看下效果: 二.activity类 pu ...
- ReactNative仿微信朋友圈App
摘要: 欢迎各位同学加入: React-Native群:397885169 大前端群:544587175 大神超多,热情无私帮助解决各种问题. 一.前沿||潜心修心,学无止尽.生活如此,coding亦 ...
随机推荐
- ArcGIS api for javascript——图形-选择一个范围内的点
描述 本例展示了如何使用图形显示查询结果,如何使用draw toolbar在地图上选择图形和如何修改图形符号为“高亮”. 在地图上画一个矩形区域,加亮矩形范围内的城市.这个应用统计高亮的城市个数并列出 ...
- 找出一堆数中最小的前K个数
描写叙述: 给定一个整数数组.让你从该数组中找出最小的K个数 思路: 最简洁粗暴的方法就是将该数组进行排序,然后取最前面的K个数就可以. 可是,本题要求的仅仅是求出最小的k个数就可以,用排序能够但显然 ...
- 免费WiFi初体验——个小白的WiFi旅程
说来羞愧,真正接触到WiFi还是在毕业后,此前自己封闭在一个人的世界,再加上外在学校的包围,我还成了个"山里"的孩子. 去年毕业了,也算是个90后,可自觉得心态过于成熟.了解外界太 ...
- Android自定义组件系列【14】——Android5.0按钮波纹效果实现
今天任老师发表了一篇关于Android5.0中按钮按下的波纹效果实现<Android L中水波纹点击效果的实现>,出于好奇我下载了源代码看了一下效果,正好手边有一个Nexus手机,我结合实 ...
- [工具] UltraEdit使用技巧汇总
ltraEdit是一套功能强大的文本编辑器,可以编辑文本.十六进制.ASCII码,可以取代记事本,内建英文单字检查.C++及VB指令突显,可同时编辑多个文件,而且即使开启很大的文件速度也不会慢.说到编 ...
- CodeChef November Challenge 2013 部分题解
http://www.codechef.com/NOV13 还在比...我先放一部分题解吧... Uncle Johny 排序一遍 struct node{ int val; int pos; }a[ ...
- JavaScript笔记(4)
接上一篇笔记 -----> 打印: 打印: 打印: 一.break 和 continue 的区别 1.break 1.break语句可用于跳出循 ...
- 【Docker入门】
目录 Linux容器 Docker的优势 Docker三大概念 安装使用Docker 补充知识 [Docker入门] 发布文章 "qq_41964425" @ *** 所谓Dock ...
- java.sql.SQLException:错误 The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone.
错误 方法 添加后面的内容即可
- R与并行计算
本文在Creative Commons许可证下发布 什么是并行计算? 并行计算,准确地说应该包括高性能计算机和并行软件两个方面.不过,近年来随着个人PC机,廉价机群,以及各种加速卡(NVIDIA GP ...