NineGridLayout一个仿微信朋友圈和QQ空间的九宫格图片展示自定义控件。一、介绍 1、当只有1张图时,可以自己定制图片宽高,也可以使用默认九宫格的宽高; 2、当只有4张图时,以2*2的方式显示; 3、除以上两种情况下,都是按照3列方式显示,但这时有一些细节: a、如果只有9张图,当然是以3*3的方式
来源: Android:NineGridLayout — 仿微信朋友圈和QQ空间的九宫格图片展示自定义控件 – HMYANG314的专栏 – 博客频道 – CSDN.NET
转载请注明出处:http://blog.csdn.net/hmyang314/article/details/51415396
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 = 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) {
- //这里是只显示一张图片的情况,显示图片的宽高可以根据实际图片大小自由定制,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 / 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;// 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中实现
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
三、核心类
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 = 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) {
- 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() {
- 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, 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 != 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);
- }
- }
代码可在我的GitHub上下载,地址:https://github.com/HMY314/NineGridLayout
效果图