android多图显示,多出图片以文字提示

可以控制最多显示多少个,多余最大数量则在最后一行以文字提示,通过控制maxSize大小来控制最多显示图片数量。效果如下图所示

  • app:maxSize="2"


    image.png
  • app:maxSize="5"


    image.png
  • app:maxSize="9"


    image.png

只有一行显示图片,多出来的图在最后一张图上显示图片总数量。

1 需要用到的知识

  • MeasureSpec.getSize(widthMeasureSpec)
    指的是总宽度,包含padding的值
  • setMeasuredDimension(width, height)
    设置测量的尺寸。重写onMeasure后需要调用该方法,否则自定义的控件不会显示。
  • generateDefaultLayoutParams()
    使用默认的布局参数,宽高都是wrap_content
  • textView.bringToFront()
    将视图图层放在最上层显示
  • textView.setBackgroundColor(0x80000000)
    设置背景半透明
  • FontMetrics
    FontMetrics为字体度量,指对于指定字号的某种字体,在度量方面的各种属性,其描述参数包括:
  • baseline:字符基线
  • ascent:字符最高点到baseline的推荐距离
  • top:字符最高点到baseline的最大距离
  • descent:字符最低点到baseline的推荐距离
  • bottom:字符最低点到baseline的最大距离
  • leading:行间距,即前一行的descent与下一行的ascent之间的距离

参考图片


image.png
  • getTextBounds
    获取文本内容矩形

2 问题

  1. 文本没有居中,使用textView.setGravity(Gravity.CENTER)只能水平居中显示。
    解决方法:
                    //设置文字位置
                    // 1.用FontMetrics对象计算高度
                    Paint.FontMetricsInt fontMetricsInt = textView.getPaint().getFontMetricsInt();
                    int textHeight = fontMetricsInt.bottom - fontMetricsInt.top;//文本高度
                    int paddingTop = (mImageSize - textHeight) / 2;
                    //方法一:设置居中,setGravity(Gravity.CENTER)只显示为水平居中,所以需要设置padding
                    textView.setPadding(0, paddingTop, 0, paddingTop);
                    textView.setGravity(Gravity.CENTER);

                   //方法二:设置居中
//                    Rect bounds = new Rect();
//                    textView.getPaint().getTextBounds(text, 0, text.length(), bounds);
//                    int textWidth = bounds.right - bounds.left;
//                    int paddingLeft = (mImageSize - textWidth) / 2;
//                    textView.setPadding(paddingLeft, paddingTop, 0, 0);

3 完全代码

ThreeImageView.java

package com.yds.jianshulib.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.yds.jianshulib.R;

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

/**
 * Created by yds
 * on 2020/3/11.
 */
public class ThreeImageView<T> extends ViewGroup {
    private Context mContext;
    private int mGap;//图片间距
    private int mSingleImgSize;//单张图片尺寸
    private int mMaxSize = 3;//最大图片数
    private List<ImageView> mImageViewList = new ArrayList<>();
    private List<T> mImgDataList;
    private int mImageSize;//图片大小
    private int mRowCount;//行数,用于后续扩展
    private int mColumnCount;//列数
    private TextView textView;

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

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

    public ThreeImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        this.mContext = context;
        //自定义xml属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ThreeImageView);
        this.mGap = (int) typedArray.getDimension(R.styleable.ThreeImageView_imgGap, 0);
        this.mSingleImgSize = typedArray.getDimensionPixelSize(R.styleable.ThreeImageView_singleImgSize, -1);
        this.mMaxSize = typedArray.getInteger(R.styleable.ThreeImageView_maxSize, 3);
        typedArray.recycle();
    }

    private void layoutChildrenView() {
        if (mImgDataList == null) return;
        int showChildrenCount = getNeedShowCount(mImgDataList.size());
        layoutMaxCountChildrenView(showChildrenCount);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        layoutChildrenView();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取总宽度
        int width = MeasureSpec.getSize(widthMeasureSpec);
        //获取总高度
        int height = MeasureSpec.getSize(heightMeasureSpec);
        //控件内容的总宽度
        int totalWidth = width - getPaddingLeft() - getPaddingRight();
        if (mImgDataList != null && mImgDataList.size() > 0) {
            //如果图片只有一个且单个图片尺寸不为-1
            if (mImgDataList.size() == 1 && mSingleImgSize != -1) {
                //图片尺寸取总宽度与单个图片宽度中的较小值
                mImageSize = mSingleImgSize > totalWidth ? totalWidth : mSingleImgSize;
            } else {
                //如果图片不止有一个,则图片尺寸为(总宽度-图片间总间距)/图片数量
                mImageSize = (totalWidth - mGap * (mColumnCount - 1)) / mColumnCount;
            }
            //(图片尺寸*行数)+总间距+上内边距+下内边距   设置行数是为后续扩展
            height = mImageSize * mRowCount + mGap * (mRowCount - 1) + getPaddingTop() + getPaddingBottom();
        }
        //设置测量的尺寸
        setMeasuredDimension(width, height);
    }


    private void layoutMaxCountChildrenView(int childrenCount) {
        int left = 0, top = 0, right=0, bottom=0;
        for (int i = 0; i < childrenCount; i++) {
            ImageView childrenView = (ImageView) getChildAt(i);
            childrenView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            left = getPaddingLeft()+i%3*mImageSize+i%3*mGap;
            top = getPaddingTop()+i/3*mImageSize+i/3*mGap;
            right = left+mImageSize;
            bottom = top + mImageSize;
            childrenView.layout(left,top,right,bottom);
            Glide.with(mContext).load(mImgDataList.get(i)).into(childrenView);
        }
        showImageAndText(left,top,right,bottom);
    }

    private void showImageAndText(int left,int top,int right,int bottom){

        if (mImgDataList.size() > mMaxSize) {
            if (textView != null) {
                textView.bringToFront();
                //设置字体大小
                String text = "共" + mImgDataList.size() + "张图";
                int textSize = px2sp(mContext, mImageSize / 6);
                textView.setTextSize(textSize);
                textView.setText(text);

                //设置文本颜色及背景半透明
                textView.setTextColor(Color.WHITE);
                textView.setBackgroundColor(0x80000000);

                //设置文字位置
                // 1.用FontMetrics对象计算高度
                Paint.FontMetricsInt fontMetricsInt = textView.getPaint().getFontMetricsInt();
                int textHeight = fontMetricsInt.bottom - fontMetricsInt.top;//文本高度
                int paddingTop = (mImageSize - textHeight) / 2;
                //方法一:设置居中,setGravity(Gravity.CENTER)只显示为水平居中,所以需要设置padding
                textView.setPadding(0, paddingTop, 0, paddingTop);
                textView.setGravity(Gravity.CENTER);

                //方法二:设置居中
//                    Rect bounds = new Rect();
//                    textView.getPaint().getTextBounds(text, 0, text.length(), bounds);
//                    int textWidth = bounds.right - bounds.left;
//                    int paddingLeft = (mImageSize - textWidth) / 2;
//                    textView.setPadding(paddingLeft, paddingTop, 0, 0);

                textView.layout(left, top, right, bottom);
                //这里设置只会显示水平居中,所以需要上面的padding

            }
        }
    }

    /**
     * 获取需要显示的数量
     *
     * @param size
     * @return
     */
    private int getNeedShowCount(int size) {
        //如果size大于最大显示数量,则用最大显示数量
        if (mMaxSize > 0 && size > mMaxSize) {
            return mMaxSize;
        } else {
            //如果size小于最大显示数量,则用size显示数量
            return size;
        }
    }

    /**
     * 设置图片数据
     *
     * @param list
     */
    public void setImagesData(List<T> list) {
        mImgDataList = list;
        removeAllViews();
        if (list == null || list.size() == 0) {
            //如果图片列表为空,则隐藏控件
            this.setVisibility(GONE);
        } else {
            //如果图片列表不为空,则显示
            this.setVisibility(VISIBLE);
        }
        //获取需要显示的数量
        int showCount = getNeedShowCount(list.size());
        //计算参数,根据需要显示的数量计算行数和列数
        int[] params = calculateParam(showCount);
        mRowCount = params[0];//行数
        mColumnCount = params[1];//列数
        for (int i = 0; i < showCount; i++) {
            ImageView iv = getImageView(i);
            if (iv == null) {
                return;
            }
            addView(iv, generateDefaultLayoutParams());
            if (i == mMaxSize - 1 && list.size() > mMaxSize) {
                textView = new TextView(mContext);
                addView(textView, generateDefaultLayoutParams());
            }
        }
        requestLayout();
    }

    protected int[] calculateParam(int imageSize) {
        int[] params = new int[2];
        params[0] = imageSize / 3 + (imageSize % 3 == 0 ? 0 : 1);
        params[1] = 3;
        return params;
    }

    private ImageView getImageView(final int position) {
        if (position < mImageViewList.size()) {
            return mImageViewList.get(position);
        } else {
            ImageView imageView = new ImageView(mContext);
            mImageViewList.add(imageView);
            return imageView;
        }
    }

    public void setGap(int gap) {
        this.mGap = gap;
    }

    public int getGap() {
        return mGap;
    }

    /**
     * 设置只有一张图片时的尺寸大小
     *
     * @param singleImgSize 单张图片的尺寸大小
     */
    public void setSingleImgSize(int singleImgSize) {
        mSingleImgSize = singleImgSize;
    }

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

    private int px2sp(Context context, float pxValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (pxValue / fontScale + 0.5f);
    }
}

res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ThreeImageView">
        <attr name="singleImgSize" format="dimension"/>
        <attr name="imgGap" format="dimension"/>
        <attr name="maxSize" format="integer"/>
    </declare-styleable>
</resources>

使用

    <com.yds.jianshulib.widget.ThreeImageView
        android:id="@+id/image_three"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:imgGap="10dp"
        app:maxSize="3"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        app:singleImgSize="50dp"
        android:paddingBottom="10dp"
        />
List<Integer> mList = new ArrayList<>();
mList.add(R.drawable.test01);
mList.add(R.drawable.test02);
mList.add(R.drawable.test03);
mList.add(R.drawable.test04);

mImageView = findViewById(R.id.image_three);
mImageView.setImagesData(mList);

源码地址:https://github.com/Yedongsheng/Jianshu
源碼地址:https://github.com/ydslib/MultiImageView

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容