Android自定义ViewGroup实现9宫格布局

实现思想即自定义一个继承ViewGroup的view

需要实现三个类

1.NineGridView ,即自定义的ViewGroup

2.NineGridViewAdapter,数据适配器类

3.ImageInfo,图片信息的实体类

相应类的实现代码:

NineGridView

package com.gif.gifchannel.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.support.annotation.NonNull;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.gif.gifchannel.R;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Answer on 2017/7/14.
 */

public class NineGridView extends ViewGroup {

   // public static final int MODE_FILL = 0;          //填充模式,类似于微信
   // public static final int MODE_GRID = 1;          //网格模式,类似于QQ,4张图会 2X2布局

    private static ImageLoader mImageLoader;        //全局的图片加载器(必须设置,否则不显示图片)
    private clickL click;
    private int singleImageSize = 250;              // 单张图片时的最大大小,单位dp
    private float singleImageRatio = 1.0f;          // 单张图片的宽高比(宽/高)
    private int maxImageSize = 9;                   // 最大显示的图片数
    private int gridSpacing = 4;                    // 宫格间距,单位dp
   // private int mode = MODE_FILL;                   // 默认使用fill模式

    private int columnCount;    // 列数
    private int rowCount;       // 行数
    private int gridWidth;      // 宫格宽度
    private int gridHeight;     // 宫格高度

    private List<ImageView> imageViews;
    private List<ImageInfo> mImageInfo;
    private NineGridViewAdapter mAdapter;

    public NineGridView(Context context) {
        this(context, null);
    }

    public NineGridView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public NineGridView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        gridSpacing = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, gridSpacing, dm);

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NineGridView);
        gridSpacing = (int) a.getDimension(R.styleable.NineGridView_ngv_gridSpacing, gridSpacing);
        a.recycle();

        imageViews = new ArrayList<>();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //控件总宽度
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = 0;
        //为所有图片分配的总宽度,包括图片间距
        int totalWidth = width - getPaddingLeft() - getPaddingRight();
        if (mImageInfo != null && mImageInfo.size() > 0) {
            //只有1张图时 图片宽度即是总宽度  高度等于宽度
            if (mImageInfo.size() == 1) {
                gridWidth = totalWidth;
                gridHeight = gridWidth;
                Log.d("yzpzz","wid  "+gridWidth+ "height   "+gridHeight);
            } else {
                //按照有几列 来计算每张图片的宽度和高度
                //gridWidth 、gridHeight表示每张图片占据的宽、高
                gridWidth = gridHeight = (totalWidth - gridSpacing * (columnCount - 1)) / columnCount;
               }
            //此处width height就是我们即将为控件设置的总宽度 width可在layout中给定 height需要根据图片数量计算
            width = gridWidth * columnCount + gridSpacing * (columnCount - 1) + getPaddingLeft() + getPaddingRight();
            height = gridHeight * rowCount + gridSpacing * (rowCount - 1) + getPaddingTop() + getPaddingBottom();
            Log.d("yzp","wid  "+width+ "height   "+height);
        }
        setMeasuredDimension(width,height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mImageInfo == null) return;
        int childrenCount = mImageInfo.size();
        //根据图片数量 创建相应个数的imageview  并根据设定的imageview的宽高以及间距 ,将imageview放置在正确的位置
        for (int i = 0; i < childrenCount; i++) {
            //得到父控件中相应的imageview
            ImageView childrenView = (ImageView) getChildAt(i);
            int rowNum = i / columnCount;
            int columnNum = i % columnCount;
            int left = (gridWidth + gridSpacing) * columnNum + getPaddingLeft();
            int top = (gridHeight + gridSpacing) * rowNum + getPaddingTop();
            int right = left + gridWidth;
            int bottom = top + gridHeight;
            Log.d("yyy","left  "+left+"right "+right+ "top "+top+"bottom  "+bottom);
            childrenView.layout(left, top, right, bottom);
            //imageloader是接口,调用接口中的onDisplayImage方法绘制imageview 。在使用的地方调用setImageLoader方法
            //重写接口中的方法,可以使用不同的图片框架显示图片 如Glide Piccasso等
            if (mImageLoader != null) {
                mImageLoader.onDisplayImage(getContext(), childrenView, mImageInfo.get(i).imageUrl);
            }
        }
    }

    /** 设置适配器 */
    //等到adapter 得到需要显示的图片信息
    public void setAdapter(@NonNull NineGridViewAdapter adapter) {
        mAdapter = adapter;
        List<ImageInfo> imageInfo = adapter.getImageInfo();

        if (imageInfo == null || imageInfo.isEmpty()) {
            setVisibility(GONE);
            return;
        } else {
            setVisibility(VISIBLE);
        }

        int imageCount = imageInfo.size();
        if (maxImageSize > 0 && imageCount > maxImageSize) {
            imageInfo = imageInfo.subList(0, maxImageSize);
            imageCount = imageInfo.size();   //再次获取图片数量
        }

        //默认是3列显示,行数根据图片的数量决定
        rowCount = imageCount / 3 + (imageCount % 3 == 0 ? 0 : 1);
        columnCount = 3;
        //grid模式下,显示4张图片使用2X2模式
        if (imageCount == 4) {
                rowCount = 2;
                columnCount = 2;
        }
        //grid模式下,显示2张图片使用 2X1 模式

        if (imageCount == 2) {
            rowCount = 1;
            columnCount = 2;
        }
        if(imageCount ==1){
            rowCount =1;
            columnCount =1;
        }

        //保证View的复用,避免重复创建
        //mImageInfo=null 表示第一次创建
        if (mImageInfo == null) {
            for (int i = 0; i < imageCount; i++) {
                ImageView iv = getImageView(i);
                if (iv == null) return;
                //添加相应个imageview到父控件 viewgroup中
                addView(iv, generateDefaultLayoutParams());
            }
        } else {
            //根据上次已有的imageview数量以及这次需要创建的imageview数量 移除或添加相应个数个imageview
            int oldViewCount = mImageInfo.size();
            int newViewCount = imageCount;
            if (oldViewCount > newViewCount) {
                removeViews(newViewCount, oldViewCount - newViewCount);
            } else if (oldViewCount < newViewCount) {
                for (int i = oldViewCount; i < newViewCount; i++) {
                    ImageView iv = getImageView(i);
                    if (iv == null) return;
                    addView(iv, generateDefaultLayoutParams());
                }
            }
        }
        //修改最后一个条目,决定是否显示更多
        /*if (adapter.getImageInfo().size() > maxImageSize) {
            View child = getChildAt(maxImageSize - 1);
            if (child instanceof NineGridViewWrapper) {
                NineGridViewWrapper imageView = (NineGridViewWrapper) child;
                imageView.setMoreNum(adapter.getImageInfo().size() - maxImageSize);
            }
        }*/
        //将传过来的imageInfo信息赋给mImageInfo 用于判断下次是否需要全部创建
        mImageInfo = imageInfo;
        requestLayout();
    }

    /** 获得 ImageView 保证了 ImageView 的重用 */
    private ImageView getImageView(final int position) {
        ImageView imageView;
        if (position < imageViews.size()) {
            imageView = imageViews.get(position);
        } else {
            imageView = mAdapter.generateImageView(getContext());
            imageView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                  //  mAdapter.onImageItemClick(getContext(), NineGridView.this, position, mAdapter.getImageInfo());
                    click.imageClick();
                }
            });
            imageViews.add(imageView);
        }
        return imageView;
    }

    /** 设置宫格间距 */
    public void setGridSpacing(int spacing) {
        gridSpacing = spacing;
    }

    /** 设置只有一张图片时的宽 */
    public void setSingleImageSize(int maxImageSize) {
        singleImageSize = maxImageSize;
    }

    /** 设置只有一张图片时的宽高比 */
    public void setSingleImageRatio(float ratio) {
        singleImageRatio = ratio;
    }

    /** 设置最大图片数 */
    public void setMaxSize(int maxSize) {
        maxImageSize = maxSize;
    }

    public int getMaxSize() {
        return maxImageSize;
    }

    public static void setImageLoader(ImageLoader imageLoader) {
        mImageLoader = imageLoader;
    }

    public static ImageLoader getImageLoader() {
        return mImageLoader;
    }

    public interface ImageLoader {
        /**
         * 需要子类实现该方法,以确定如何加载和显示图片
         *
         * @param context   上下文
         * @param imageView 需要展示图片的ImageView
         * @param url       图片地址
         */
        void onDisplayImage(Context context, ImageView imageView, String url);

      /*  *//**
         * @param url 图片的地址
         * @return 当前框架的本地缓存图片
         *//*
        Bitmap getCacheImage(String url);*/
    }
    //设置图片的点击事件
    public  void setClick(clickL click){
        this.click=click;
    }
    public  interface  clickL{
        void imageClick();
    }
}

NineGridViewAdapter

package com.gif.gifchannel.widget;

import android.content.Context;
import android.widget.ImageView;

import com.gif.gifchannel.R;

import java.io.Serializable;
import java.util.List;

/**
 * Created by Answer on 2017/7/14.
 * 适配器类,用于添加数据以及生成imageview
 */

public  class NineGridViewAdapter implements Serializable {

    protected Context context;
    private List<ImageInfo> imageInfo;

    public NineGridViewAdapter(Context context, List<ImageInfo> imageInfo) {
        this.context = context;
        this.imageInfo = imageInfo;
    }

    /**
     * 如果要实现图片点击的逻辑,重写此方法即可
     *
     * @param context      上下文
     * @param nineGridView 九宫格控件
     * @param index        当前点击图片的的索引
     * @param imageInfo    图片地址的数据集合
     */
    protected void onImageItemClick(Context context, NineGridView nineGridView, int index, List<ImageInfo> imageInfo) {
    }

    /**
     * 生成ImageView容器的方式,
     * 如果需要自定义图片展示效果,重写此方法即可
     *
     * @param context 上下文
     * @return 生成的 ImageView
     */
    protected ImageView generateImageView(Context context) {
        ImageView imageView = new ImageView(context);
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        //imageView.setImageResource(R.drawable.ic);
        return imageView;
    }

    public List<ImageInfo> getImageInfo() {
        return imageInfo;
    }

    public void setImageInfoList(List<ImageInfo> imageInfo) {
        this.imageInfo = imageInfo;
    }
}


ImageInfo

package com.gif.gifchannel.widget;

import java.io.Serializable;

/**
 * Created by Answer on 2017/7/14.
 * 图片信息的实体类,三个参数分别为图片的URL地址 图片的高度以及宽度
 */

public class ImageInfo implements Serializable {
    public String imageUrl;
    public int imageViewHeight;
    public int imageViewWidth;


    public String getImageUrl() {
        return imageUrl;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    public int getImageViewHeight() {
        return imageViewHeight;
    }

    public void setImageViewHeight(int imageViewHeight) {
        this.imageViewHeight = imageViewHeight;
    }

    public int getImageViewWidth() {
        return imageViewWidth;
    }

    public void setImageViewWidth(int imageViewWidth) {
        this.imageViewWidth = imageViewWidth;
    }

    @Override
    public String toString() {
        return "ImageInfo{" +
                "imageUrl='" + imageUrl + '\'' +
                ", imageViewHeight=" + imageViewHeight +
                ", imageViewWidth=" + imageViewWidth +
                '}';
    }
}
自定义属性,在attrs.xml中

<declare-styleable name="NineGridView">
        <attr name="ngv_gridSpacing" format="dimension"/>
</declare-styleable>



一般的应用场景是在列表中展示多条9宫格样式的图片,如微信朋友圈

因此需要在LIstView或RecyclerView的adapter对应的Item布局中引入NIneGridView, 在adapter中添加数据以及设置9宫格数据的适配器

使用方法:

1.布局中引入NineGridView,设置控件width,height,paddingLeft,paddingRight等信息

         <com.gif.gifchannel.widget.NineGridView
            android:id="@+id/ugc_item_ninegrid"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:layout_below="@+id/ugc_item_content"
            android:layout_marginTop="12dp"
            android:paddingLeft="8dp"
            android:paddingRight="8dp"
            app:ngv_gridSpacing="4dp"
            />


2.在ListView或RecyclerView的adapter中调用setImageLoader方法,确定如何加载和显示图片(就是用哪种图片框架)

            NineGridView.setImageLoader(new NineGridView.ImageLoader() {
                @Override
                public void onDisplayImage(Context context, ImageView imageView, String url) {
                    Glide.with(context).load(url).priority(Priority.NORMAL).placeholder(R.mipmap.placehold).centerCrop().into(
                            imageView);
                }
            });

3.添加图片数据信息,创建NineGridViewAdapter,将包含图片信息的数据集合list添加进去,最后调用NineGridView的setAdapter方法,

设置数据适配器。

                    List<ImageInfo> imageInfoList = new ArrayList<>();
                    for (int i = 0; i < ugcItem.getImages().size(); i++) {
                    String url = ugcItem.getImages().get(i).getUrl();
                    ImageInfo info = new ImageInfo();
                    info.setImageUrl(url);
                    imageInfoList.add(info);
                    }

                    NineGridViewAdapter adapter = new NineGridViewAdapter(mContext, imageInfoList);
                    holder.ugc_item_ninegrid.setAdapter(adapter);






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值