Android 宫格图控件MultiImageView(RecyclerView适配器中展示不定数量的图片)

 Android 宫格图控件MultiImageView(RecyclerView适配器中展示不定数量的图片)

最近的github上的996ICU在IT界应该很多人听说了,不知道CSDN上什么是最受大家欢迎的,最重要的是大家开心就好,在北京 杭州 上海 广州等各地的同胞们每天记得给自己一个微笑......

实现效果如下图:

 

一.实现逻辑:

1.首先创建图片实体类

public class PhotoInfo
{

    public String url;
    public int w;
    public int h;

    public String getUrl()
    {
        return url;
    }

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

    public int getW()
    {
        return w;
    }

    public void setW(int w)
    {
        this.w = w;
    }

    public int getH()
    {
        return h;
    }

    public void setH(int h)
    {
        this.h = h;
    }

    @Override
    public String toString()
    {
        return "PhotoInfo{" +
                "url='" + url + '\'' +
                ", w=" + w +
                ", h=" + h +
                '}';
    }
}

2.这里的宽高用于处理单张图片时ImageView的宽高。

接着创建MultiImageView

package com.example.lttechdemo.view;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.example.lttechdemo.R;
import com.example.lttechdemo.bean.PhotoInfo;
import com.example.lttechdemo.util.BaseUtils;

import java.util.List;

/**
 * Created by litong on 2018/2/23.
 * 宫格图控件
 */

public class MultiImageView extends LinearLayout
{
    private int pxOneMaxWandH; //单张图最大允许宽高
    private int pxMoreWandH = 0; //多张图的宽高
    private int MAX_PER_ROW_COUNT = 3; //每行显示最大数
    private int MAX_WIDTH = 0; //控件最大宽度
    private int pxImagePadding = BaseUtils.getInstance().dip2px(3); //图片间的间距

    private LayoutParams onePicPara; //一张图的布局参数
    private LayoutParams morePara, moreParaColumnFirst; //多张图的布局参数(非第一列、第一列)
    private LayoutParams rowPara; //行布局参数

    private List<PhotoInfo> imagesList; //照片的Url列表
    private OnItemClickListener mOnItemClickListener; //图片点击监听

    public MultiImageView(Context context)
    {
        super(context);
    }

    public MultiImageView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        if (MAX_WIDTH == 0)
        {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            if (width > 0)
            {
                MAX_WIDTH = width;
                if (imagesList != null && imagesList.size() > 0)
                {
                    setList(imagesList);
                }
            }
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 设置图片URL
     */
    public void setList(List<PhotoInfo> lists) throws IllegalArgumentException
    {
        if (lists == null)
        {
            throw new IllegalArgumentException("imageList is null...");
        }
        imagesList = lists;

        if (MAX_WIDTH > 0)
        {
            pxMoreWandH = (MAX_WIDTH - pxImagePadding * 2) / 3; //解决右侧图片和内容对不齐问题
            pxOneMaxWandH = MAX_WIDTH * 2 / 3;
            initImageLayoutParams();
        }
        initView();
    }


    /**
     * 根据imageView的数量初始化不同的View布局
     */
    private void initView()
    {
        this.setOrientation(VERTICAL);
        this.removeAllViews();
        if (MAX_WIDTH == 0)
        {
            //为了触发onMeasure()来测量MultiImageView的最大宽度,MultiImageView的宽设置为match_parent
            addView(new View(getContext()));
            return;
        }

        if (imagesList == null || imagesList.size() == 0)
        {
            return;
        }

        if (imagesList.size() == 1) //单图
        {
            addView(createImageView(0, false));
        } else //多图
        {
            int allCount = imagesList.size();
            if (allCount == 4) //四张图每行显示两张
            {
                MAX_PER_ROW_COUNT = 2;
            } else //其他每行显示三张
            {
                MAX_PER_ROW_COUNT = 3;
            }
            int rowCount = allCount / MAX_PER_ROW_COUNT
                    + (allCount % MAX_PER_ROW_COUNT > 0 ? 1 : 0); //行数
            for (int rowCursor = 0; rowCursor < rowCount; rowCursor++)
            {
                LinearLayout rowLayout = new LinearLayout(getContext());
                rowLayout.setOrientation(LinearLayout.HORIZONTAL);

                rowLayout.setLayoutParams(rowPara);
                if (rowCursor != 0)
                {
                    rowLayout.setPadding(0, pxImagePadding, 0, 0);
                }

                int columnCount = allCount % MAX_PER_ROW_COUNT == 0 ? MAX_PER_ROW_COUNT
                        : allCount % MAX_PER_ROW_COUNT; //最后一行的列数
                if (rowCursor != rowCount - 1) //非最后一行的列数
                {
                    columnCount = MAX_PER_ROW_COUNT;
                }
                addView(rowLayout);

                int rowOffset = rowCursor * MAX_PER_ROW_COUNT; //行偏移
                for (int columnCursor = 0; columnCursor < columnCount; columnCursor++)
                {
                    int position = columnCursor + rowOffset; //图片位置
                    rowLayout.addView(createImageView(position, true));
                }
            }
        }
    }

    /**
     * 创建ImageView
     */
    private ImageView createImageView(int position, final boolean isMultiImage)
    {
        PhotoInfo photoInfo = imagesList.get(position);
        ImageView imageView = new ImageView(getContext());
        if (isMultiImage) //多图
        {
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setLayoutParams(position % MAX_PER_ROW_COUNT == 0 ? moreParaColumnFirst : morePara);//第一列图片不设置MarginLeft
        } else //单张图
        {
            imageView.setAdjustViewBounds(true);
            imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
            //imageView.setMaxHeight(pxOneMaxWandH);

            int expectW = photoInfo.w;
            int expectH = photoInfo.h;

            if (expectW == 0 || expectH == 0)
            {
                imageView.setLayoutParams(onePicPara);
            } else
            {
                int actualW = 0;
                int actualH = 0;
                float scale = ((float) expectH) / ((float) expectW);
                if (expectW > pxOneMaxWandH)
                {
                    actualW = pxOneMaxWandH;
                    actualH = (int) (actualW * scale);
                } else if (expectW < pxMoreWandH)
                {
                    actualW = pxMoreWandH;
                    actualH = (int) (actualW * scale);
                } else
                {
                    actualW = expectW;
                    actualH = expectH;
                }
                imageView.setLayoutParams(new LayoutParams(actualW, actualH));
            }
        }

        imageView.setId(photoInfo.url.hashCode());
        imageView.setOnClickListener(new ImageOnClickListener(position));
        imageView.setBackgroundColor(getResources().getColor(R.color.im_font_color_text_hint));
        Glide.with(getContext()).load(photoInfo.url)
                .asBitmap()
                .dontAnimate()
                .placeholder(R.mipmap.ic_img_default)
                .error(R.mipmap.ic_img_default)
                .diskCacheStrategy(DiskCacheStrategy.RESULT)
                .into(imageView);
        return imageView;
    }

    /**
     * 初始化布局参数
     */
    private void initImageLayoutParams()
    {
        int wrap = LayoutParams.WRAP_CONTENT;
        int match = LayoutParams.MATCH_PARENT;

        onePicPara = new LayoutParams(wrap, wrap);

        moreParaColumnFirst = new LayoutParams(pxMoreWandH, pxMoreWandH);
        morePara = new LayoutParams(pxMoreWandH, pxMoreWandH);
        morePara.setMargins(pxImagePadding, 0, 0, 0);

        rowPara = new LayoutParams(match, wrap);
    }

    private class ImageOnClickListener implements OnClickListener
    {

        private int position;

        public ImageOnClickListener(int position)
        {
            this.position = position;
        }

        @Override
        public void onClick(View view)
        {
            if (mOnItemClickListener != null)
            {
                mOnItemClickListener.onItemClick(view, position);
            }
        }
    }

    public interface OnItemClickListener
    {
        void onItemClick(View view, int position);
    }

    //获取列数
    public int getColumn()
    {
        return MAX_PER_ROW_COUNT;
    }

    //获取图片间的间距
    public int getPxImagePadding()
    {
        return pxImagePadding;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener)
    {
        mOnItemClickListener = onItemClickListener;
    }
}

3.先从入口setList()方法看起

    /**
     * 设置图片URL
     */
    public void setList(List<PhotoInfo> lists) throws IllegalArgumentException
    {
        if (lists == null)
        {
            throw new IllegalArgumentException("imageList is null...");
        }
        imagesList = lists;

        if (MAX_WIDTH > 0)
        {
            pxMoreWandH = (MAX_WIDTH - pxImagePadding * 2) / 3; //解决右侧图片和内容对不齐问题
            pxOneMaxWandH = MAX_WIDTH * 2 / 3;
            initImageLayoutParams();
        }
        initView();
    }

4.设置图片URL的同时定义了一张图或多张图的图片宽高,并初始化了相关布局参数

    /**
     * 初始化布局参数
     */
    private void initImageLayoutParams()
    {
        int wrap = LayoutParams.WRAP_CONTENT;
        int match = LayoutParams.MATCH_PARENT;

        onePicPara = new LayoutParams(wrap, wrap);

        moreParaColumnFirst = new LayoutParams(pxMoreWandH, pxMoreWandH);
        morePara = new LayoutParams(pxMoreWandH, pxMoreWandH);
        morePara.setMargins(pxImagePadding, 0, 0, 0);

        rowPara = new LayoutParams(match, wrap);
    }

5.这里的moreParaColumnFirst与morePara唯一差别在于,是否设置了marginLeft。因此,当包含多图时,第一列图片使用没有设置marginLeft的moreParaColumnFirst布局参数,非第一列图片使用设置marginLeft的morePara布局参数。

接着初始化相关控件
 

    /**
     * 根据imageView的数量初始化不同的View布局
     */
    private void initView()
    {
        this.setOrientation(VERTICAL);
        this.removeAllViews();
        if (MAX_WIDTH == 0)
        {
            //为了触发onMeasure()来测量MultiImageView的最大宽度,MultiImageView的宽设置为match_parent
            addView(new View(getContext()));
            return;
        }

        if (imagesList == null || imagesList.size() == 0)
        {
            return;
        }

        if (imagesList.size() == 1) //单图
        {
            addView(createImageView(0, false));
        } else //多图
        {
            int allCount = imagesList.size();
            if (allCount == 4) //四张图每行显示两张
            {
                MAX_PER_ROW_COUNT = 2;
            } else //其他每行显示三张
            {
                MAX_PER_ROW_COUNT = 3;
            }
            int rowCount = allCount / MAX_PER_ROW_COUNT
                    + (allCount % MAX_PER_ROW_COUNT > 0 ? 1 : 0); //行数
            for (int rowCursor = 0; rowCursor < rowCount; rowCursor++)
            {
                LinearLayout rowLayout = new LinearLayout(getContext());
                rowLayout.setOrientation(LinearLayout.HORIZONTAL);

                rowLayout.setLayoutParams(rowPara);
                if (rowCursor != 0)
                {
                    rowLayout.setPadding(0, pxImagePadding, 0, 0);
                }

                int columnCount = allCount % MAX_PER_ROW_COUNT == 0 ? MAX_PER_ROW_COUNT
                        : allCount % MAX_PER_ROW_COUNT; //最后一行的列数
                if (rowCursor != rowCount - 1) //非最后一行的列数
                {
                    columnCount = MAX_PER_ROW_COUNT;
                }
                addView(rowLayout);

                int rowOffset = rowCursor * MAX_PER_ROW_COUNT; //行偏移
                for (int columnCursor = 0; columnCursor < columnCount; columnCursor++)
                {
                    int position = columnCursor + rowOffset; //图片位置
                    rowLayout.addView(createImageView(position, true));
                }
            }
        }
    }

6.

这里分成单图和多图两种情况进行初始化。当只有一张图时,直接将图片放入创建好的单图ImageView中。当有多张图片时,需要根据图片的个数进行判断需要展示的行数和列数,以及列之间、行之间的偏移量等等,然后再一一add到创建的ImageView中,代码中已给出相关注释。

最后是创建ImageView

    /**
     * 创建ImageView
     */
    private ImageView createImageView(int position, final boolean isMultiImage)
    {
        PhotoInfo photoInfo = imagesList.get(position);
        ImageView imageView = new ImageView(getContext());
        if (isMultiImage) //多图
        {
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setLayoutParams(position % MAX_PER_ROW_COUNT == 0 ? moreParaColumnFirst : morePara);//第一列图片不设置MarginLeft
        } else //单张图
        {
            imageView.setAdjustViewBounds(true);
            imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
            //imageView.setMaxHeight(pxOneMaxWandH);

            int expectW = photoInfo.w;
            int expectH = photoInfo.h;

            if (expectW == 0 || expectH == 0)
            {
                imageView.setLayoutParams(onePicPara);
            } else
            {
                int actualW = 0;
                int actualH = 0;
                float scale = ((float) expectH) / ((float) expectW);
                if (expectW > pxOneMaxWandH)
                {
                    actualW = pxOneMaxWandH;
                    actualH = (int) (actualW * scale);
                } else if (expectW < pxMoreWandH)
                {
                    actualW = pxMoreWandH;
                    actualH = (int) (actualW * scale);
                } else
                {
                    actualW = expectW;
                    actualH = expectH;
                }
                imageView.setLayoutParams(new LayoutParams(actualW, actualH));
            }
        }

        imageView.setId(photoInfo.url.hashCode());
        imageView.setOnClickListener(new ImageOnClickListener(position));
        imageView.setBackgroundColor(getResources().getColor(R.color.im_font_color_text_hint));
        Glide.with(getContext()).load(photoInfo.url)
                .asBitmap()
                .dontAnimate()
                .placeholder(R.mipmap.ic_img_default)
                .error(R.mipmap.ic_img_default)
                .diskCacheStrategy(DiskCacheStrategy.RESULT)
                .into(imageView);
        return imageView;
    }

7.

这里依然分成单图和多图两种情况进行创建。当是多图的时候,只需判断当前的图片是否位于第一列,从而进行设置相应的LayoutParam;而当单图时,如果该图片没有设置width和height,即宽高都为0,则ImageView使用我们最初定义的onePicPara布局参数,如果该图片设置了width和height,则需要与pxOneMaxWandH进行比较,具体是以宽定高,还是什么其他规则,则需要进行相关逻辑处理。

最后通过RecyclerView的adapter去使用该控件:

        @Override
        public void convert(BaseViewHolder holder, NewsContent newsContent)
        {
            MultiImageView multiImagView = holder.getView(R.id.multiImagView);
            List<PhotoInfo> images = newsContent.getImageurls();
            if (images.size() > 0)
            {
                multiImagView.setList(imageInfo);
            }
        }

最后附上源码:源码在此等候多时       源码

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android宫格布局可以使用GridView来实现。GridView是一个可滚动的视,其的元素按照网格形式排列。GridView需要一个adapter来提供数据和视,可以使用ArrayAdapter或自定义的Adapter。 下面是一个简单的宫格布局实现: 1. 在XML布局文件添加GridView组件: ``` <GridView android:id="@+id/gridview" android:layout_width="match_parent" android:layout_height="match_parent" android:columnWidth="100dp" android:numColumns="auto_fit" android:verticalSpacing="10dp" android:horizontalSpacing="10dp" android:stretchMode="columnWidth" android:gravity="center"/> ``` 2. 在Activity获取GridView组件,并设置Adapter: ``` GridView gridview = (GridView) findViewById(R.id.gridview); gridview.setAdapter(new ImageAdapter(this)); ``` 3. 自定义Adapter实现getView方法: ``` public class ImageAdapter extends BaseAdapter { private Context mContext; public ImageAdapter(Context c) { mContext = c; } public int getCount() { return mThumbIds.length; } public Object getItem(int position) { return null; } public long getItemId(int position) { return 0; } // create a new ImageView for each item referenced by the Adapter public View getView(int position, View convertView, ViewGroup parent) { ImageView imageView; if (convertView == null) { // if it's not recycled, initialize some attributes imageView = new ImageView(mContext); imageView.setLayoutParams(new GridView.LayoutParams(100, 100)); imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); imageView.setPadding(8, 8, 8, 8); } else { imageView = (ImageView) convertView; } imageView.setImageResource(mThumbIds[position]); return imageView; } // references to our images private Integer[] mThumbIds = { R.drawable.image1, R.drawable.image2, R.drawable.image3, R.drawable.image4, R.drawable.image5, R.drawable.image6, R.drawable.image7, R.drawable.image8, R.drawable.image9, R.drawable.image10 }; } ``` 这里使用了ImageView作为GridView的子项,并设置了每个子项的大小、填充和缩放方式。在getView方法,将图片资源设置为ImageView像。 这样就可以实现一个简单的宫格布局。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值