一百行代码实现微信朋友圈九宫格图片显示

前言
    很多时候我们都在刷微博或者微信朋友圈的时候都会看到很多图片,而这些图片的显示跟我们平时很多控件的显示方式都不一样,而且,当我们仔细去观察后就会发现,他加载的图片都是根据图片数量动态加载的,根据不同的图片数量来用不同的布局显示

当图片是4张的时候,就会形成一个2x2的正方形,除了一张的情况,另外的都是按照九宫格的方式显示和排列图片的。那么这种布局是怎么实现的呢,一开始,好多人都可能认为用原生的GridView就能搞掂,但是,却有几种特殊的情况是GridView解决不了的,例如4张图片的情况,或者1张,其实也可以根据图片的数量然后用几个不同布局的GridView来实现,不过那样的话就复杂得多了。而且处理起来很麻烦,其实,大部分的实现都是通过自定义ViewGroup来实现的,通过代码编写来设定childrenView的layout来实现这种布局,而NineGridView控件就是这么一个东西,代码其实很简单,100行就够了。
代码编写                         
     先自定义一个View集成ViewGroup,编辑器会提示你实现OnLayout方法,实现之,这里我们动态的添加的话其实不用到OnLayout方法,自定义一个layoutChildrenView()用来为子view设定位置就行了,该方法的实现如下:
        这代码里面在调用子view的layout方法的同时设定了本身ViewGroup的高度大小,因为NineGridView的高度是要根据子View的高度来确定的.


  1.     private void layoutChildrenView(){
  2.         int childrenCount = listData.size();

  3.         int singleWidth = (totalWidth - gap * (3 - 1)) / 3;
  4.         int singleHeight = singleWidth;

  5.         //根据子view数量确定高度
  6.         ViewGroup.LayoutParams params = getLayoutParams();
  7.         params.height = singleHeight * rows + gap * (rows - 1);
  8.         setLayoutParams(params);

  9.         for (int i = 0; i < childrenCount; i++) {
  10.             CustomImageView childrenView = (CustomImageView) getChildAt(i);
  11.             childrenView.setImageUrl(((Image) listData.get(i)).getUrl());
  12.             int[] position = findPosition(i);
  13.             int left = (singleWidth + gap) * position[1];
  14.             int top = (singleHeight + gap) * position[0];
  15.             int right = left + singleWidth;
  16.             int bottom = top + singleHeight;

  17.             childrenView.layout(left, top, right, bottom);
  18.         }

  19.     }
复制代码

   添加一个设置图片资源的接口,一般情况下我们都是用在listview来显示数据,而数据都是封装好的,这里提供一个Image封装类,接口和封装类代码如下:  
  1.     public void setImagesData(List<Image> lists) {
  2.         if (lists == null || lists.isEmpty()) {
  3.             return;
  4.         }
  5.         //初始化布局
  6.         generateChildrenLayout(lists.size());
  7.         //这里做一个重用view的处理
  8.         if (listData == null) {
  9.             int i = 0;
  10.             while (i < lists.size()) {
  11.                 CustomImageView iv = generateImageView();
  12.                 addView(iv,generateDefaultLayoutParams());
  13.                 i++;
  14.             }
  15.         } else {
  16.             int oldViewCount = listData.size();
  17.             int newViewCount = lists.size();
  18.             if (oldViewCount > newViewCount) {
  19.                 removeViews(newViewCount - 1, oldViewCount - newViewCount);
  20.             } else if (oldViewCount < newViewCount) {
  21.                 for (int i = 0; i < newViewCount - oldViewCount; i++) {
  22.                     CustomImageView iv = generateImageView();
  23.                     addView(iv,generateDefaultLayoutParams());
  24.                 }
  25.             }
  26.         }
  27.         listData = lists;
  28.         layoutChildrenView();
  29.     }
复制代码

Image封装类:

  1. public class Image {
  2.     private String url;
  3.     private int width;
  4.     private int height;

  5.     public Image(String url, int width, int height) {
  6.         this.url = url;
  7.         this.width = width;
  8.         this.height = height;
  9.         L.i(toString());
  10.     }

  11.     public String getUrl() {
  12.         return url;
  13.     }

  14.     public void setUrl(String url) {
  15.         this.url = url;
  16.     }

  17.     public int getWidth() {
  18.         return width;
  19.     }

  20.     public void setWidth(int width) {
  21.         this.width = width;
  22.     }

  23.     public int getHeight() {
  24.         return height;
  25.     }

  26.     public void setHeight(int height) {
  27.         this.height = height;
  28.     }

  29.     @Override
  30.     public String toString() {

  31.         return "image---->>url="+url+"width="+width+"height"+height;
  32.     }
  33. }
复制代码




在添加数据的时候,我们要根据图片的个数来确定具体的布局情况,这个函数就是generateChildrenLayout(),实现如下:
  1.     /**
  2.      * 根据图片个数确定行列数量
  3.      * 对应关系如下
  4.      * num        row        column
  5.      * 1           1        1
  6.      * 2           1        2
  7.      * 3           1        3
  8.      * 4           2        2
  9.      * 5           2        3
  10.      * 6           2        3
  11.      * 7           3        3
  12.      * 8           3        3
  13.      * 9           3        3
  14.      *
  15.      * @param length
  16.      */
  17.     private void generateChildrenLayout(int length) {
  18.         if (length <= 3) {
  19.             rows = 1;
  20.             columns = length;
  21.         } else if (length <= 6) {
  22.             rows = 2;
  23.             columns = 3;
  24.             if (length == 4) {
  25.                 columns = 2;
  26.             }
  27.         } else {
  28.             rows = 3;
  29.             columns = 3;
  30.         }
  31.     }
复制代码
这些,就是NineGridLayout的核心代码了,是不是很简单,整个类的源码如下:
  1. package com.weixinninegridlayout;

  2. import android.content.Context;
  3. import android.graphics.Color;
  4. import android.graphics.drawable.ColorDrawable;
  5. import android.util.AttributeSet;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. import android.widget.ImageView;

  9. import java.util.List;


  10. /**
  11. * Created by Pan_ on 2015/2/2.
  12. */
  13. public class NineGridlayout extends ViewGroup {

  14.     /**
  15.      * 图片之间的间隔
  16.      */
  17.     private int gap = 5;
  18.     private int columns;//
  19.     private int rows;//
  20.     private List listData;
  21.     private int totalWidth;

  22.     public NineGridlayout(Context context) {
  23.         super(context);
  24.     }

  25.     public NineGridlayout(Context context, AttributeSet attrs) {
  26.         super(context, attrs);
  27.         ScreenTools screenTools=ScreenTools.instance(getContext());
  28.         totalWidth=screenTools.getScreenWidth()-screenTools.dip2px(80);
  29.     }

  30.     @Override
  31.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  32.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  33.     }

  34.     @Override
  35.     protected void onLayout(boolean changed, int l, int t, int r, int b) {

  36.     }
  37.     private void layoutChildrenView(){
  38.         int childrenCount = listData.size();

  39.         int singleWidth = (totalWidth - gap * (3 - 1)) / 3;
  40.         int singleHeight = singleWidth;

  41.         //根据子view数量确定高度
  42.         ViewGroup.LayoutParams params = getLayoutParams();
  43.         params.height = singleHeight * rows + gap * (rows - 1);
  44.         setLayoutParams(params);

  45.         for (int i = 0; i < childrenCount; i++) {
  46.             CustomImageView childrenView = (CustomImageView) getChildAt(i);
  47.             childrenView.setImageUrl(((Image) listData.get(i)).getUrl());
  48.             int[] position = findPosition(i);
  49.             int left = (singleWidth + gap) * position[1];
  50.             int top = (singleHeight + gap) * position[0];
  51.             int right = left + singleWidth;
  52.             int bottom = top + singleHeight;

  53.             childrenView.layout(left, top, right, bottom);
  54.         }

  55.     }


  56.     private int[] findPosition(int childNum) {
  57.         int[] position = new int[2];
  58.         for (int i = 0; i < rows; i++) {
  59.             for (int j = 0; j < columns; j++) {
  60.                 if ((i * columns + j) == childNum) {
  61.                     position[0] = i;//行
  62.                     position[1] = j;//列
  63.                     break;
  64.                 }
  65.             }
  66.         }
  67.         return position;
  68.     }

  69.     public int getGap() {
  70.         return gap;
  71.     }

  72.     public void setGap(int gap) {
  73.         this.gap = gap;
  74.     }


  75.     public void setImagesData(List<Image> lists) {
  76.         if (lists == null || lists.isEmpty()) {
  77.             return;
  78.         }
  79.         //初始化布局
  80.         generateChildrenLayout(lists.size());
  81.         //这里做一个重用view的处理
  82.         if (listData == null) {
  83.             int i = 0;
  84.             while (i < lists.size()) {
  85.                 CustomImageView iv = generateImageView();
  86.                 addView(iv,generateDefaultLayoutParams());
  87.                 i++;
  88.             }
  89.         } else {
  90.             int oldViewCount = listData.size();
  91.             int newViewCount = lists.size();
  92.             if (oldViewCount > newViewCount) {
  93.                 removeViews(newViewCount - 1, oldViewCount - newViewCount);
  94.             } else if (oldViewCount < newViewCount) {
  95.                 for (int i = 0; i < newViewCount - oldViewCount; i++) {
  96.                     CustomImageView iv = generateImageView();
  97.                     addView(iv,generateDefaultLayoutParams());
  98.                 }
  99.             }
  100.         }
  101.         listData = lists;
  102.         layoutChildrenView();
  103.     }


  104.     /**
  105.      * 根据图片个数确定行列数量
  106.      * 对应关系如下
  107.      * num        row        column
  108.      * 1           1        1
  109.      * 2           1        2
  110.      * 3           1        3
  111.      * 4           2        2
  112.      * 5           2        3
  113.      * 6           2        3
  114.      * 7           3        3
  115.      * 8           3        3
  116.      * 9           3        3
  117.      *
  118.      * @param length
  119.      */
  120.     private void generateChildrenLayout(int length) {
  121.         if (length <= 3) {
  122.             rows = 1;
  123.             columns = length;
  124.         } else if (length <= 6) {
  125.             rows = 2;
  126.             columns = 3;
  127.             if (length == 4) {
  128.                 columns = 2;
  129.             }
  130.         } else {
  131.             rows = 3;
  132.             columns = 3;
  133.         }
  134.     }

  135.     private CustomImageView generateImageView() {
  136.         CustomImageView iv = new CustomImageView(getContext());
  137.         iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
  138.         iv.setOnClickListener(new OnClickListener() {
  139.             @Override
  140.             public void onClick(View v) {

  141.             }
  142.         });
  143.         iv.setBackgroundColor(Color.parseColor("#f5f5f5"));
  144.         return iv;
  145.     }


  146. }
复制代码
    因为微信那些图片在点击的时候是有一个灰色的蒙版的,实现起来其实很简单,我们这里在自定义一个imageview,叫做CustomImageView,复写onTouchEvent方法,在onKeyDown的时候添加一个colorfilter,然后再onKeyUp的时候clear掉,这样就实现了点击有灰色蒙版的效果,同时为了方便项目加载图片的解耦,我加载图片用了picasso这个开源库,这个开源库的地址为点击打开链接 ,具体的代码如下:
  1. package com.weixinninegridlayout;

  2. import android.content.Context;
  3. import android.graphics.Color;
  4. import android.graphics.PorterDuff;
  5. import android.graphics.drawable.ColorDrawable;
  6. import android.graphics.drawable.Drawable;
  7. import android.text.TextUtils;
  8. import android.util.AttributeSet;
  9. import android.view.MotionEvent;
  10. import android.widget.ImageView;

  11. import com.squareup.picasso.Picasso;


  12. /**
  13. * Created by Pan_ on 2015/2/2.
  14. */
  15. public class CustomImageView extends ImageView {
  16.     private String url;
  17.     private boolean isAttachedToWindow;

  18.     public CustomImageView(Context context, AttributeSet attrs) {
  19.         super(context, attrs);
  20.     }

  21.     public CustomImageView(Context context) {
  22.         super(context);
  23.     }


  24.     @Override
  25.     public boolean onTouchEvent(MotionEvent event) {

  26.         switch (event.getAction()) {
  27.             case MotionEvent.ACTION_DOWN:
  28.                 Drawable drawable=getDrawable();
  29.                 if(drawable!=null) {
  30.                     drawable.mutate().setColorFilter(Color.GRAY,
  31.                             PorterDuff.Mode.MULTIPLY);
  32.                 }
  33.                 break;
  34.             case MotionEvent.ACTION_MOVE:
  35.                 break;
  36.             case MotionEvent.ACTION_CANCEL:
  37.             case MotionEvent.ACTION_UP:
  38.                 Drawable drawableUp=getDrawable();
  39.                 if(drawableUp!=null) {
  40.                     drawableUp.mutate().clearColorFilter();
  41.                 }
  42.                 break;
  43.         }

  44.         return super.onTouchEvent(event);
  45.     }

  46.     @Override
  47.     public void onAttachedToWindow() {
  48.         isAttachedToWindow = true;
  49.         setImageUrl(url);
  50.         super.onAttachedToWindow();
  51.     }

  52.     @Override
  53.     public void onDetachedFromWindow() {
  54.         Picasso.with(getContext()).cancelRequest(this);
  55.         isAttachedToWindow = false;
  56.         setImageBitmap(null);
  57.         super.onDetachedFromWindow();
  58.     }


  59.     public void setImageUrl(String url) {
  60.         if (!TextUtils.isEmpty(url)) {
  61.             this.url = url;
  62.             if (isAttachedToWindow) {
  63.                 Picasso.with(getContext()).load(url).placeholder(new ColorDrawable(Color.parseColor("#f5f5f5"))).into(this);
  64.             }
  65.         }
  66.     }
  67. }

源码要的留言

一百行代码实现微信朋友圈九宫格图片显示

上一篇:微信小程序 View:flex 布局


下一篇:[1]微信小程序技能点-自定义组件-slot与组件内数据