• Android NineGridLayout — 仿微信朋友圈和QQ空间的九宫格图片展示自定义控件


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

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

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

    public class NineGridTestLayout extends NineGridLayout {
    
         protected static final int MAX_W_H_RATIO = 3;
    
         public NineGridTestLayout(Context context) {
             super(context);
         }
    
         public NineGridTestLayout(Context context, AttributeSet attrs) {
             super(context, attrs);
         }
    
         @Override
         protected boolean displayOneImage(final RatioImageView imageView, String url, final int parentWidth) {
    
            //这里是只显示一张图片的情况,显示图片的宽高可以根据实际图片大小自由定制,parentWidth 为该layout的宽度
             ImageLoader.getInstance().displayImage(imageView, url, ImageLoaderUtil.getPhotoImageOption(), new ImageLoadingListener() {
                 @Override
                 public void onLoadingStarted(String imageUri, View view) {
    
                 }
    
                 @Override
                 public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
    
                 }
    
                 @Override
                 public void onLoadingComplete(String imageUri, View view, Bitmap bitmap) {
                     int w = bitmap.getWidth();
                     int h = bitmap.getHeight();
    
                     int newW;
                     int newH;
                     if (h > w * MAX_W_H_RATIO) {//h:w = 5:3
                         newW = parentWidth / 2;
                         newH = newW * 5 / 3;
                     } else if (h < w) {//h:w = 2:3
                         newW = parentWidth * 2 / 3;
                         newH = newW * 2 / 3;
                     } else {//newH:h = newW :w
                         newW = parentWidth / 2;
                         newH = h * newW / w;
                     }
                     setOneImageLayoutParams(imageView, newW, newH);
                 }
    
                 @Override
                 public void onLoadingCancelled(String imageUri, View view) {
    
                 }
             });
             return false;// true 代表按照九宫格默认大小显示(此时不要调用setOneImageLayoutParams);false 代表按照自定义宽高显示。
         }
    
         @Override
         protected void displayImage(RatioImageView imageView, String url) {
             ImageLoaderUtil.getImageLoader(mContext).displayImage(url, imageView, ImageLoaderUtil.getPhotoImageOption());
         }
    
         @Override
         protected void onClickImage(int i, String url, List<String> urlList) {
             Toast.makeText(mContext, "点击了图片" + url, Toast.LENGTH_SHORT).show();
         }
    }

    3、在xml中实现

    <com.hmy.ninegridlayout.view.NineGridTestLayout xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/layout_nine_grid"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        app:sapcing="4dp" />

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

    4、使用:

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

    三、核心类

    NineGridLayout.java

    /**
     * 描述:
     * 作者:HMY
     * 时间:2016/5/10
     */
    public abstract class NineGridLayout extends ViewGroup {
    
        private static final float DEFUALT_SPACING = 3f;
        private static final int MAX_COUNT = 9;
    
        protected Context mContext;
        private float mSpacing = DEFUALT_SPACING;
        private int mColumns;
        private int mRows;
        private int mTotalWidth;
        private int mSingleWidth;
    
        private boolean mIsShowAll = false;
        private boolean mIsFirst = true;
        private List<String> mUrlList = new ArrayList<>();
    
        public NineGridLayout(Context context) {
            super(context);
            init(context);
        }
    
        public NineGridLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NineGridLayout);
    
            mSpacing = typedArray.getDimension(R.styleable.NineGridLayout_sapcing, DEFUALT_SPACING);
            typedArray.recycle();
            init(context);
        }
    
        private void init(Context context) {
            mContext = context;
            if (getListSize(mUrlList) == 0) {
                setVisibility(GONE);
            }
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            mTotalWidth = right - left;
            mSingleWidth = (int) ((mTotalWidth - mSpacing * (3 - 1)) / 3);
            if (mIsFirst) {
                notifyDataSetChanged();
                mIsFirst = false;
            }
        }
    
        /**
         * 设置间隔
         *
         * @param spacing
         */
        public void setSpacing(float spacing) {
            mSpacing = spacing;
        }
    
        /**
         * 设置是否显示所有图片(超过最大数时)
         *
         * @param isShowAll
         */
        public void setIsShowAll(boolean isShowAll) {
            mIsShowAll = isShowAll;
        }
    
        public void setUrlList(List<String> urlList) {
            if (getListSize(urlList) == 0) {
                setVisibility(GONE);
                return;
            }
            setVisibility(VISIBLE);
    
            mUrlList.clear();
            mUrlList.addAll(urlList);
    
            if (!mIsFirst) {
                notifyDataSetChanged();
            }
        }
    
        public void notifyDataSetChanged() {
            removeAllViews();
            int size = getListSize(mUrlList);
            if (size > 0) {
                setVisibility(VISIBLE);
            } else {
                setVisibility(GONE);
            }
    
            if (size == 1) {
                String url = mUrlList.get(0);
                RatioImageView imageView = createImageView(0, url);
    
                //避免在ListView中一张图未加载成功时,布局高度受其他item影响
                LayoutParams params = getLayoutParams();
                params.height = mSingleWidth;
                setLayoutParams(params);
                imageView.layout(0, 0, mSingleWidth, mSingleWidth);
    
                boolean isShowDefualt = displayOneImage(imageView, url, mTotalWidth);
                if (isShowDefualt) {
                    layoutImageView(imageView, 0, url, false);
                } else {
                    addView(imageView);
                }
                return;
            }
    
            generateChildrenLayout(size);
            layoutParams();
    
            for (int i = 0; i < size; i++) {
                String url = mUrlList.get(i);
                RatioImageView imageView;
                if (!mIsShowAll) {
                    if (i < MAX_COUNT - 1) {
                        imageView = createImageView(i, url);
                        layoutImageView(imageView, i, url, false);
                    } else { //第9张时
                        if (size <= MAX_COUNT) {//刚好第9张
                            imageView = createImageView(i, url);
                            layoutImageView(imageView, i, url, false);
                        } else {//超过9张
                            imageView = createImageView(i, url);
                            layoutImageView(imageView, i, url, true);
                            break;
                        }
                    }
                } else {
                    imageView = createImageView(i, url);
                    layoutImageView(imageView, i, url, false);
                }
            }
        }
    
        private void layoutParams() {
            int singleHeight = mSingleWidth;
    
            //根据子view数量确定高度
            LayoutParams params = getLayoutParams();
            params.height = (int) (singleHeight * mRows + mSpacing * (mRows - 1));
            setLayoutParams(params);
        }
    
        private RatioImageView createImageView(final int i, final String url) {
            RatioImageView imageView = new RatioImageView(mContext);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    onClickImage(i, url, mUrlList);
                }
            });
            return imageView;
        }
    
        /**
         * @param imageView
         * @param url
         * @param showNumFlag 是否在最大值的图片上显示还有未显示的图片张数
         */
        private void layoutImageView(RatioImageView imageView, int i, String url, boolean showNumFlag) {
            final int singleWidth = (int) ((mTotalWidth - mSpacing * (3 - 1)) / 3);
            int singleHeight = singleWidth;
    
            int[] position = findPosition(i);
            int left = (int) ((singleWidth + mSpacing) * position[1]);
            int top = (int) ((singleHeight + mSpacing) * position[0]);
            int right = left + singleWidth;
            int bottom = top + singleHeight;
    
            imageView.layout(left, top, right, bottom);
    
            addView(imageView);
            if (showNumFlag) {//添加超过最大显示数量的文本
                int overCount = getListSize(mUrlList) - MAX_COUNT;
                if (overCount > 0) {
                    float textSize = 30;
                    final TextView textView = new TextView(mContext);
                    textView.setText("+" + String.valueOf(overCount));
                    textView.setTextColor(Color.WHITE);
                    textView.setPadding(0, singleHeight / 2 - getFontHeight(textSize), 0, 0);
                    textView.setTextSize(textSize);
                    textView.setGravity(Gravity.CENTER);
                    textView.setBackgroundColor(Color.BLACK);
                    textView.getBackground().setAlpha(120);
    
                    textView.layout(left, top, right, bottom);
                    addView(textView);
                }
            }
            displayImage(imageView, url);
        }
    
        private int[] findPosition(int childNum) {
            int[] position = new int[2];
            for (int i = 0; i < mRows; i++) {
                for (int j = 0; j < mColumns; j++) {
                    if ((i * mColumns + j) == childNum) {
                        position[0] = i;//
                        position[1] = j;//
                        break;
                    }
                }
            }
            return position;
        }
    
        /**
         * 根据图片个数确定行列数量
         *
         * @param length
         */
        private void generateChildrenLayout(int length) {
            if (length <= 3) {
                mRows = 1;
                mColumns = length;
            } else if (length <= 6) {
                mRows = 2;
                mColumns = 3;
                if (length == 4) {
                    mColumns = 2;
                }
            } else {
                mColumns = 3;
                if (mIsShowAll) {
                    mRows = length / 3;
                    int b = length % 3;
                    if (b > 0) {
                        mRows++;
                    }
                } else {
                    mRows = 3;
                }
            }
    
        }
    
        protected void setOneImageLayoutParams(RatioImageView imageView, int width, int height) {
            imageView.setLayoutParams(new LayoutParams(width, height));
            imageView.layout(0, 0, width, height);
    
            LayoutParams params = getLayoutParams();
    //        params.width = width;
            params.height = height;
            setLayoutParams(params);
        }
    
        private int getListSize(List<String> list) {
            if (list == null || list.size() == 0) {
                return 0;
            }
            return list.size();
        }
    
        private int getFontHeight(float fontSize) {
            Paint paint = new Paint();
            paint.setTextSize(fontSize);
            Paint.FontMetrics fm = paint.getFontMetrics();
            return (int) Math.ceil(fm.descent - fm.ascent);
        }
    
        /**
         * @param imageView
         * @param url
         * @param parentWidth 父控件宽度
         * @return true 代表按照九宫格默认大小显示,false 代表按照自定义宽高显示
         */
        protected abstract boolean displayOneImage(RatioImageView imageView, String url, int parentWidth);
    
        protected abstract void displayImage(RatioImageView imageView, String url);
    
        protected abstract void onClickImage(int position, String url, List<String> urlList);
    }

    RatioImageView.Java

    该类有两个功能:

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

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

    /**
     * 根据宽高比例自动计算高度ImageView
     * Created by HMY on 2016/4/21.
     */
    public class RatioImageView extends ImageView {
    
        /**
         * 宽高比例
         */
        private float mRatio = 0f;
    
        public RatioImageView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        public RatioImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatioImageView);
    
            mRatio = typedArray.getFloat(R.styleable.RatioImageView_ratio, 0f);
            typedArray.recycle();
        }
    
        public RatioImageView(Context context) {
            super(context);
        }
    
        /**
         * 设置ImageView的宽高比
         *
         * @param ratio
         */
        public void setRatio(float ratio) {
            mRatio = ratio;
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int width = MeasureSpec.getSize(widthMeasureSpec);
            if (mRatio != 0) {
                float height = width / mRatio;
                heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.EXACTLY);
            }
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    Drawable drawable = getDrawable();
                    if (drawable != null) {
                        drawable.mutate().setColorFilter(Color.GRAY,
                                PorterDuff.Mode.MULTIPLY);
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    Drawable drawableUp = getDrawable();
                    if (drawableUp != null) {
                        drawableUp.mutate().clearColorFilter();
                    }
                    break;
            }
    
            return super.onTouchEvent(event);
        }
    
    }

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

    效果图

        

  • 相关阅读:
    base64编码
    ios开发之指纹识别
    date
    php的学习
    mac下安装mysql遇到的无法连接的问题
    关于git上传文件过大报错的问题 remote: warning: Large files detected.
    安卓开发中Theme.AppCompat.Light的解决方法
    ubuntu操作系统中卸载mysql的安装与卸载
    重新格式化删除U盘隐藏分区与如何在LMT下安装非Ghost win7
    网易有道笔试2015-05-12
  • 原文地址:https://www.cnblogs.com/zhujiabin/p/7184001.html
Copyright © 2020-2023  润新知