Android:NineGridLayout — 仿微信朋友圈和QQ空间的九宫格图片展示自定义控件 – HMYANG314的专栏 – 博客频道 – CSDN.NET

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个方法,如下:

  1. public abstract class NineGridLayout extends ViewGroup {
  2.     //******************************其他代码省略**************************
  3.         /**
  4.          * 显示一张图片
  5.          * @param imageView
  6.          * @param url
  7.          * @param parentWidth 父控件宽度
  8.          * @return true 代表按照九宫格默认大小显示,false 代表按照自定义宽高显示
  9.          */
  10.         protected abstract boolean displayOneImage(RatioImageView imageView, String url, int parentWidth);
  11.         protected abstract void displayImage(RatioImageView imageView, String url);
  12.         /**
  13.          * 点击图片时执行
  14.          */
  15.         protected abstract void onClickImage(int position, String url, List<String> urlList);
  16.     }

2、我这里用NineGridTestLayout继承NineGridLayout实现,displayOneImage()与displayImage()中的参数都是显示图片需要的,我这里用的是ImageLoader显示图片,当然你也可以用其他的。

  1. public class NineGridTestLayout extends NineGridLayout {
  2.      protected static final int MAX_W_H_RATIO = 3;
  3.      public NineGridTestLayout(Context context) {
  4.          super(context);
  5.      }
  6.      public NineGridTestLayout(Context context, AttributeSet attrs) {
  7.          super(context, attrs);
  8.      }
  9.      @Override
  10.      protected boolean displayOneImage(final RatioImageView imageView, String url, final int parentWidth) {
  11.         //这里是只显示一张图片的情况,显示图片的宽高可以根据实际图片大小自由定制,parentWidth 为该layout的宽度
  12.          ImageLoader.getInstance().displayImage(imageView, url, ImageLoaderUtil.getPhotoImageOption(), new ImageLoadingListener() {
  13.              @Override
  14.              public void onLoadingStarted(String imageUri, View view) {
  15.              }
  16.              @Override
  17.              public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
  18.              }
  19.              @Override
  20.              public void onLoadingComplete(String imageUri, View view, Bitmap bitmap) {
  21.                  int w = bitmap.getWidth();
  22.                  int h = bitmap.getHeight();
  23.                  int newW;
  24.                  int newH;
  25.                  if (h > w * MAX_W_H_RATIO) {//h:w = 5:3
  26.                      newW = parentWidth / 2;
  27.                      newH = newW * 5 / 3;
  28.                  } else if (h < w) {//h:w = 2:3
  29.                      newW = parentWidth * 2 / 3;
  30.                      newH = newW * 2 / 3;
  31.                  } else {//newH:h = newW :w
  32.                      newW = parentWidth / 2;
  33.                      newH = h * newW / w;
  34.                  }
  35.                  setOneImageLayoutParams(imageView, newW, newH);
  36.              }
  37.              @Override
  38.              public void onLoadingCancelled(String imageUri, View view) {
  39.              }
  40.          });
  41.          return false;// true 代表按照九宫格默认大小显示(此时不要调用setOneImageLayoutParams);false 代表按照自定义宽高显示。
  42.      }
  43.      @Override
  44.      protected void displayImage(RatioImageView imageView, String url) {
  45.          ImageLoaderUtil.getImageLoader(mContext).displayImage(url, imageView, ImageLoaderUtil.getPhotoImageOption());
  46.      }
  47.      @Override
  48.      protected void onClickImage(int i, String url, List<String> urlList) {
  49.          Toast.makeText(mContext, “点击了图片” + url, Toast.LENGTH_SHORT).show();
  50.      }
  51. }

3、在xml中实现

  1. <com.hmy.ninegridlayout.view.NineGridTestLayout xmlns:app=“http://schemas.Android.com/apk/res-auto”
  2.     Android:id=“@+id/layout_nine_grid”
  3.     android:layout_width=“match_parent”
  4.     android:layout_height=“wrap_content”
  5.     android:layout_marginTop=“8dp”
  6.     app:sapcing=“4dp” />

app:sapcing是设置九宫格中图片之间的间隔。

4、使用:

  1. public List<String> urlList = new ArrayList<>();//图片url
  2. NineGridTestLayout layout = (NineGridTestLayout) view.findViewById(R.id.layout_nine_grid);
  3. layout.setIsShowAll(false); //当传入的图片数超过9张时,是否全部显示
  4. layout.setSpacing(5); //动态设置图片之间的间隔
  5. layout.setUrlList(urlList); //最后再设置图片url

 

三、核心类

NineGridLayout.java

  1. /**
  2.  * 描述:
  3.  * 作者:HMY
  4.  * 时间:2016/5/10
  5.  */
  6. public abstract class NineGridLayout extends ViewGroup {
  7.     private static final float DEFUALT_SPACING = 3f;
  8.     private static final int MAX_COUNT = 9;
  9.     protected Context mContext;
  10.     private float mSpacing = DEFUALT_SPACING;
  11.     private int mColumns;
  12.     private int mRows;
  13.     private int mTotalWidth;
  14.     private int mSingleWidth;
  15.     private boolean mIsShowAll = false;
  16.     private boolean mIsFirst = true;
  17.     private List<String> mUrlList = new ArrayList<>();
  18.     public NineGridLayout(Context context) {
  19.         super(context);
  20.         init(context);
  21.     }
  22.     public NineGridLayout(Context context, AttributeSet attrs) {
  23.         super(context, attrs);
  24.         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NineGridLayout);
  25.         mSpacing = typedArray.getDimension(R.styleable.NineGridLayout_sapcing, DEFUALT_SPACING);
  26.         typedArray.recycle();
  27.         init(context);
  28.     }
  29.     private void init(Context context) {
  30.         mContext = context;
  31.         if (getListSize(mUrlList) == 0) {
  32.             setVisibility(GONE);
  33.         }
  34.     }
  35.     @Override
  36.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  37.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  38.     }
  39.     @Override
  40.     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  41.         mTotalWidth = right – left;
  42.         mSingleWidth = (int) ((mTotalWidth – mSpacing * (3 – 1)) / 3);
  43.         if (mIsFirst) {
  44.             notifyDataSetChanged();
  45.             mIsFirst = false;
  46.         }
  47.     }
  48.     /**
  49.      * 设置间隔
  50.      *
  51.      * @param spacing
  52.      */
  53.     public void setSpacing(float spacing) {
  54.         mSpacing = spacing;
  55.     }
  56.     /**
  57.      * 设置是否显示所有图片(超过最大数时)
  58.      *
  59.      * @param isShowAll
  60.      */
  61.     public void setIsShowAll(boolean isShowAll) {
  62.         mIsShowAll = isShowAll;
  63.     }
  64.     public void setUrlList(List<String> urlList) {
  65.         if (getListSize(urlList) == 0) {
  66.             setVisibility(GONE);
  67.             return;
  68.         }
  69.         setVisibility(VISIBLE);
  70.         mUrlList.clear();
  71.         mUrlList.addAll(urlList);
  72.         if (!mIsFirst) {
  73.             notifyDataSetChanged();
  74.         }
  75.     }
  76.     public void notifyDataSetChanged() {
  77.         removeAllViews();
  78.         int size = getListSize(mUrlList);
  79.         if (size > 0) {
  80.             setVisibility(VISIBLE);
  81.         } else {
  82.             setVisibility(GONE);
  83.         }
  84.         if (size == 1) {
  85.             String url = mUrlList.get(0);
  86.             RatioImageView imageView = createImageView(0, url);
  87.             //避免在ListView中一张图未加载成功时,布局高度受其他item影响
  88.             LayoutParams params = getLayoutParams();
  89.             params.height = mSingleWidth;
  90.             setLayoutParams(params);
  91.             imageView.layout(00, mSingleWidth, mSingleWidth);
  92.             boolean isShowDefualt = displayOneImage(imageView, url, mTotalWidth);
  93.             if (isShowDefualt) {
  94.                 layoutImageView(imageView, 0, url, false);
  95.             } else {
  96.                 addView(imageView);
  97.             }
  98.             return;
  99.         }
  100.         generateChildrenLayout(size);
  101.         layoutParams();
  102.         for (int i = 0; i < size; i++) {
  103.             String url = mUrlList.get(i);
  104.             RatioImageView imageView;
  105.             if (!mIsShowAll) {
  106.                 if (i < MAX_COUNT – 1) {
  107.                     imageView = createImageView(i, url);
  108.                     layoutImageView(imageView, i, url, false);
  109.                 } else { //第9张时
  110.                     if (size <= MAX_COUNT) {//刚好第9张
  111.                         imageView = createImageView(i, url);
  112.                         layoutImageView(imageView, i, url, false);
  113.                     } else {//超过9张
  114.                         imageView = createImageView(i, url);
  115.                         layoutImageView(imageView, i, url, true);
  116.                         break;
  117.                     }
  118.                 }
  119.             } else {
  120.                 imageView = createImageView(i, url);
  121.                 layoutImageView(imageView, i, url, false);
  122.             }
  123.         }
  124.     }
  125.     private void layoutParams() {
  126.         int singleHeight = mSingleWidth;
  127.         //根据子view数量确定高度
  128.         LayoutParams params = getLayoutParams();
  129.         params.height = (int) (singleHeight * mRows + mSpacing * (mRows – 1));
  130.         setLayoutParams(params);
  131.     }
  132.     private RatioImageView createImageView(final int i, final String url) {
  133.         RatioImageView imageView = new RatioImageView(mContext);
  134.         imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
  135.         imageView.setOnClickListener(new OnClickListener() {
  136.             @Override
  137.             public void onClick(View v) {
  138.                 onClickImage(i, url, mUrlList);
  139.             }
  140.         });
  141.         return imageView;
  142.     }
  143.     /**
  144.      * @param imageView
  145.      * @param url
  146.      * @param showNumFlag 是否在最大值的图片上显示还有未显示的图片张数
  147.      */
  148.     private void layoutImageView(RatioImageView imageView, int i, String url, boolean showNumFlag) {
  149.         final int singleWidth = (int) ((mTotalWidth – mSpacing * (3 – 1)) / 3);
  150.         int singleHeight = singleWidth;
  151.         int[] position = findPosition(i);
  152.         int left = (int) ((singleWidth + mSpacing) * position[1]);
  153.         int top = (int) ((singleHeight + mSpacing) * position[0]);
  154.         int right = left + singleWidth;
  155.         int bottom = top + singleHeight;
  156.         imageView.layout(left, top, right, bottom);
  157.         addView(imageView);
  158.         if (showNumFlag) {//添加超过最大显示数量的文本
  159.             int overCount = getListSize(mUrlList) – MAX_COUNT;
  160.             if (overCount > 0) {
  161.                 float textSize = 30;
  162.                 final TextView textView = new TextView(mContext);
  163.                 textView.setText(“+” + String.valueOf(overCount));
  164.                 textView.setTextColor(Color.WHITE);
  165.                 textView.setPadding(0, singleHeight / 2 – getFontHeight(textSize), 00);
  166.                 textView.setTextSize(textSize);
  167.                 textView.setGravity(Gravity.CENTER);
  168.                 textView.setBackgroundColor(Color.BLACK);
  169.                 textView.getBackground().setAlpha(120);
  170.                 textView.layout(left, top, right, bottom);
  171.                 addView(textView);
  172.             }
  173.         }
  174.         displayImage(imageView, url);
  175.     }
  176.     private int[] findPosition(int childNum) {
  177.         int[] position = new int[2];
  178.         for (int i = 0; i < mRows; i++) {
  179.             for (int j = 0; j < mColumns; j++) {
  180.                 if ((i * mColumns + j) == childNum) {
  181.                     position[0] = i;//行
  182.                     position[1] = j;//列
  183.                     break;
  184.                 }
  185.             }
  186.         }
  187.         return position;
  188.     }
  189.     /**
  190.      * 根据图片个数确定行列数量
  191.      *
  192.      * @param length
  193.      */
  194.     private void generateChildrenLayout(int length) {
  195.         if (length <= 3) {
  196.             mRows = 1;
  197.             mColumns = length;
  198.         } else if (length <= 6) {
  199.             mRows = 2;
  200.             mColumns = 3;
  201.             if (length == 4) {
  202.                 mColumns = 2;
  203.             }
  204.         } else {
  205.             mColumns = 3;
  206.             if (mIsShowAll) {
  207.                 mRows = length / 3;
  208.                 int b = length % 3;
  209.                 if (b > 0) {
  210.                     mRows++;
  211.                 }
  212.             } else {
  213.                 mRows = 3;
  214.             }
  215.         }
  216.     }
  217.     protected void setOneImageLayoutParams(RatioImageView imageView, int width, int height) {
  218.         imageView.setLayoutParams(new LayoutParams(width, height));
  219.         imageView.layout(00, width, height);
  220.         LayoutParams params = getLayoutParams();
  221. //        params.width = width;
  222.         params.height = height;
  223.         setLayoutParams(params);
  224.     }
  225.     private int getListSize(List<String> list) {
  226.         if (list == null || list.size() == 0) {
  227.             return 0;
  228.         }
  229.         return list.size();
  230.     }
  231.     private int getFontHeight(float fontSize) {
  232.         Paint paint = new Paint();
  233.         paint.setTextSize(fontSize);
  234.         Paint.FontMetrics fm = paint.getFontMetrics();
  235.         return (int) Math.ceil(fm.descent – fm.ascent);
  236.     }
  237.     /**
  238.      * @param imageView
  239.      * @param url
  240.      * @param parentWidth 父控件宽度
  241.      * @return true 代表按照九宫格默认大小显示,false 代表按照自定义宽高显示
  242.      */
  243.     protected abstract boolean displayOneImage(RatioImageView imageView, String url, int parentWidth);
  244.     protected abstract void displayImage(RatioImageView imageView, String url);
  245.     protected abstract void onClickImage(int position, String url, List<String> urlList);
  246. }

 

RatioImageView.Java

该类有两个功能:

1、是用于ImageView被按下时有变暗效果

2、ImageView的宽高根据设置的比例动态适配高度,如在xml中设置 app:ratio=”2″ ,ImageView的高度根据其宽度改变,但始终是宽的2倍,该功能在该项目中没有使用。

  1. /**
  2.  * 根据宽高比例自动计算高度ImageView
  3.  * Created by HMY on 2016/4/21.
  4.  */
  5. public class RatioImageView extends ImageView {
  6.     /**
  7.      * 宽高比例
  8.      */
  9.     private float mRatio = 0f;
  10.     public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
  11.         super(context, attrs, defStyleAttr);
  12.     }
  13.     public RatioImageView(Context context, AttributeSet attrs) {
  14.         super(context, attrs);
  15.         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatioImageView);
  16.         mRatio = typedArray.getFloat(R.styleable.RatioImageView_ratio, 0f);
  17.         typedArray.recycle();
  18.     }
  19.     public RatioImageView(Context context) {
  20.         super(context);
  21.     }
  22.     /**
  23.      * 设置ImageView的宽高比
  24.      *
  25.      * @param ratio
  26.      */
  27.     public void setRatio(float ratio) {
  28.         mRatio = ratio;
  29.     }
  30.     @Override
  31.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  32.         int width = MeasureSpec.getSize(widthMeasureSpec);
  33.         if (mRatio != 0) {
  34.             float height = width / mRatio;
  35.             heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.EXACTLY);
  36.         }
  37.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  38.     }
  39.     @Override
  40.     public boolean onTouchEvent(MotionEvent event) {
  41.         switch (event.getAction()) {
  42.             case MotionEvent.ACTION_DOWN:
  43.                 Drawable drawable = getDrawable();
  44.                 if (drawable != null) {
  45.                     drawable.mutate().setColorFilter(Color.GRAY,
  46.                             PorterDuff.Mode.MULTIPLY);
  47.                 }
  48.                 break;
  49.             case MotionEvent.ACTION_MOVE:
  50.                 break;
  51.             case MotionEvent.ACTION_CANCEL:
  52.             case MotionEvent.ACTION_UP:
  53.                 Drawable drawableUp = getDrawable();
  54.                 if (drawableUp != null) {
  55.                     drawableUp.mutate().clearColorFilter();
  56.                 }
  57.                 break;
  58.         }
  59.         return super.onTouchEvent(event);
  60.     }
  61. }

代码可在我的GitHub上下载,地址:https://github.com/HMY314/NineGridLayout

 

效果图

    

分享到: 更多 (0)