Android 仿朋友圈之九宫格多图显示
Posted 冰糖葫芦三剑客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 仿朋友圈之九宫格多图显示相关的知识,希望对你有一定的参考价值。
一个仿微信朋友圈和QQ空间的九宫格图片展示自定义控件效果:
一.介绍:
1、当只有1张图时,可以自己定制图片宽高,也可以使用默认九宫格的宽高;
2、当只有4张图时,以2*2的方式显示;
3、除以上两种情况下,都是按照3列方式显示,但这时有一些细节:
a、如果只有9张图,当然是以3*3的方式显示;
b、如果超过9张图,可以设置是否全部显示。
如果设置不完全显示,则按照3*3的方式显示,但是在第9张图上会有一个带“+”号的数字,
代表还有几张没有显示,这里是模仿了QQ空间图片超出9张的显示方式;
如果设置全部显示,理所当然的将所有图片都显示出来。
4、图片被按下时,会有一个变暗的效果,这也是模仿微信朋友圈的效果。
二.使用步骤:
1、核心类是NineGridLayout,继承自ViewGroup的抽象类,所以我们实际项目使用需要继承它,并要实现3个方法,如下:
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.donghe.wxfriend.R;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;
public abstract class NineGridLayout extends ViewGroup
private static final float DEFUALT_SPACING = 3f;
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 ArrayList<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)
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 * (3 - 1)) / 3);
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) == 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);
if (size == 1)
String url = mUrlList.get(0);
RatioImageView imageView = createImageView(0, url);
//避免在ListView中一张图未加载成功时,布局高度受其他item影响
LayoutParams params = getLayoutParams();
params.height = mSingleWidth;
setLayoutParams(params);
imageView.layout(0, 0, mSingleWidth, mSingleWidth);
boolean isShowDefualt = displayOneImage(imageView, url, mTotalWidth);
if (isShowDefualt)
layoutImageView(imageView, 0, url, false);
else
addView(imageView);
return;
generateChildrenLayout(size);
layoutParams();
for (int i = 0; i < size; i++)
String url = mUrlList.get(i);
RatioImageView imageView;
if (!mIsShowAll)
if (i < MAX_COUNT - 1)
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 - 1));
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 * (3 - 1)) / 3);
int singleHeight = singleWidth;
int[] position = findPosition(i);
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);
if (showNumFlag) //添加超过最大显示数量的文本
int overCount = getListSize(mUrlList) - MAX_COUNT;
if (overCount > 0)
float textSize = 30;
final TextView textView = new TextView(mContext);
textView.setText("+" + String.valueOf(overCount));
textView.setTextColor(Color.WHITE);
textView.setPadding(0, singleHeight / 2 - getFontHeight(textSize), 0, 0);
textView.setTextSize(textSize);
textView.setGravity(Gravity.CENTER);
textView.setBackgroundColor(Color.BLACK);
textView.getBackground().setAlpha(120);
textView.layout(left, top, right, bottom);
addView(textView);
displayImage(imageView, url);
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 generateChildrenLayout(int length)
if (length <= 3)
mRows = 1;
mColumns = length;
else if (length <= 6)
mRows = 2;
mColumns = 3;
if (length == 4)
mColumns = 2;
else
mColumns = 3;
if (mIsShowAll)
mRows = length / 3;
int b = length % 3;
if (b > 0)
mRows++;
else
mRows = 3;
protected void setOneImageLayoutParams(RatioImageView imageView, int width, int height)
imageView.setLayoutParams(new LayoutParams(width, height));
imageView.layout(0, 0, width, height);
LayoutParams params = getLayoutParams();
// params.width = width;
params.height = height;
setLayoutParams(params);
private int getListSize(List<String> list)
if (list == null || list.size() == 0)
return 0;
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, ArrayList<String> urlList);
2、我这里用NineGridTestLayout继承NineGridLayout实现,displayOneImage()与displayImage()中的参数都是显示图片需要的,我这里用的是ImageLoader显示图片,当然你也可以用其他的
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Toast;
import com.example.donghe.wxfriend.activity.GalleryActivity;
import com.example.donghe.wxfriend.activity.MyGalleryActivity;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import java.util.ArrayList;
import java.util.List;
public class NineGridTestLayout extends NineGridLayout
protected static final int MAX_W_H_RATIO = 3;
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)
ImageLoaderUtil.displayImage(mContext, 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 / 2;
newH = newW * 5 / 3;
else if (h < w) //h:w = 2:3
newW = parentWidth * 2 / 3;
newH = newW * 2 / 3;
else //newH:h = newW :w
newW = parentWidth / 2;
newH = h * newW / w;
setOneImageLayoutParams(imageView, newW, newH);
@Override
public void onLoadingCancelled(String imageUri, View view)
);
return 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, ArrayList<String> urlList)
Toast.makeText(mContext, "点击了图片" + url, Toast.LENGTH_SHORT).show();
Intent intent = new Intent(mContext, MyGalleryActivity.class);
Bundle bundle = new Bundle();
bundle.putString("999",url);
bundle.putStringArrayList("list",urlList);
bundle.putInt("po",i);
intent.putExtras(bundle);
mContext.startActivity(intent);
3.布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_head"
android:layout_width="50dp"
android:layout_height="50dp" />
<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="4dp"
android:text="asas" />
</LinearLayout>
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp" />
<!--<com.example.donghe.wxfriend.MultiImageView-->
<!--android:id="@+id/gridview"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_marginTop="4dp" />-->
<com.lvfq.code.dynamic.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" />
</LinearLayout>
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(5); //动态设置图片之间的间隔
layout.setUrlList(urlList); //最后再设置图片url
5.RatioImageView :
该类有两个功能:a.是用于ImageView被按下时有变暗效果
b.ImageView的宽高根据设置的比例动态适配高度,如在xml中设置 app:ratio="2" ,ImageView的高度根据其宽度改变,但始终是宽的2倍,该功能在该项目中没有使用
/**
* 根据宽高比例自动计算高度ImageView
*/
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 != 0)
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);
6.初始化 ImageLoader
import android.app.Application;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
public class AppApplication extends Application
@Override
public void onCreate()
super.onCreate();
initImageLoader();
private void initImageLoader()
ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
ImageLoader.getInstance().init(configuration);
7.values中的atts文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RatioImageView">
<attr name="ratio" format="float"/>
</declare-styleable>
<declare-styleable name="NineGridLayout">
<attr name="sapcing" format="dimension"/>
</declare-styleable>
</resources>
8.ImageLoaderUtil
import android.content.Context;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.example.donghe.wxfriend.R;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
public class ImageLoaderUtil
public static ImageLoader getImageLoader(Context context)
return ImageLoader.getInstance();
public static DisplayImageOptions getPhotoImageOption()
Integer extra = 1;
DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true).cacheOnDisk(true)
.showImageForEmptyUri(R.drawable.banner_default).showImageOnFail(R.drawable.banner_default)
.showImageOnLoading(R.drawable.banner_default)
.extraForDownloader(extra)
.bitmapConfig(Bitmap.Config.RGB_565).build();
return options;
public static void displayImage(Context context, ImageView imageView, String url, DisplayImageOptions options)
getImageLoader(context).displayImage(url, imageView, options);
public static void displayImage(Context context, ImageView imageView, String url, DisplayImageOptions options, ImageLoadingListener listener)
getImageLoader(context).displayImage(url, imageView, options, listener);
最后在lib中添加上ImageLoader的jar即可
相关九宫格源码:(类似微博微信的图片九宫格)
https://github.com/HMY314/NineGridLayout
https://github.com/panyiho/NineGridView
https://github.com/GitLqr/LQRNineGridImageView
https://github.com/EdwardSituwende/NinePatchAvatar
https://github.com/yangchaojiang/ZoomPreviewPicture
https://github.com/mzule/NineGridLayout
https://github.com/Vanish136/NineGridView
快速开发库--微信、支付宝支付(含签名,下单)、自定义View(验证码/密码,九宫格图片上传等)、图片预览、RxJava、EventBus、base层封装等
https://github.com/sdwfqin/AndroidQuick
以上是关于Android 仿朋友圈之九宫格多图显示的主要内容,如果未能解决你的问题,请参考以下文章
Android控件GridView之仿支付宝钱包首页带有分割线的GridView九宫格的完美实现
Android控件GridView之仿支付宝钱包首页带有分割线的GridView九宫格的完美实现