Android 仿朋友圈之九宫格多图显示

Posted 冰糖葫芦三剑客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 仿朋友圈之九宫格多图显示相关的知识,希望对你有一定的参考价值。

一个仿微信朋友圈和QQ空间的九宫格图片展示自定义控件效果:

 

一.介绍:

    1、当只有1张图时,可以自己定制图片宽高,也可以使用默认九宫格的宽高;
    2、当只有4张图时,以2*2的方式显示;
    3、除以上两种情况下,都是按照3列方式显示,但这时有一些细节:
        a、如果只有9张图,当然是以3*3的方式显示;
        b、如果超过9张图,可以设置是否全部显示。
            如果设置不完全显示,则按照3*3的方式显示,但是在第9张图上会有一个带“+”号的数字,
            代表还有几张没有显示,这里是模仿了QQ空间图片超出9张的显示方式;
            如果设置全部显示,理所当然的将所有图片都显示出来。
    4、图片被按下时,会有一个变暗的效果,这也是模仿微信朋友圈的效果。

二.使用步骤:

1、核心类是NineGridLayout,继承自ViewGroup的抽象类,所以我们实际项目使用需要继承它,并要实现3个方法,如下:

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

import com.example.donghe.wxfriend.R;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;


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 ArrayList<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() 
        post(new TimerTask() 
            @Override
            public void run() 
                refresh();
            
        );
    

    private void refresh() 
        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, ArrayList<String> urlList);

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

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;
import android.widget.Toast;

import com.example.donghe.wxfriend.activity.GalleryActivity;
import com.example.donghe.wxfriend.activity.MyGalleryActivity;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;

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


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) 

        ImageLoaderUtil.displayImage(mContext, 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;
    

    @Override
    protected void displayImage(RatioImageView imageView, String url) 
        ImageLoaderUtil.getImageLoader(mContext).displayImage(url, imageView, ImageLoaderUtil.getPhotoImageOption());
    

    @Override
    protected void onClickImage(int i, String url, ArrayList<String> urlList) 
        Toast.makeText(mContext, "点击了图片" + url, Toast.LENGTH_SHORT).show();
        Intent intent = new Intent(mContext, MyGalleryActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("999",url);
        bundle.putStringArrayList("list",urlList);
        bundle.putInt("po",i);
        intent.putExtras(bundle);
                    mContext.startActivity(intent);
    

3.布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"

    android:padding="8dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:id="@+id/iv_head"
            android:layout_width="50dp"
            android:layout_height="50dp" />

        <TextView
            android:id="@+id/tv_username"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="4dp"
            android:text="asas" />

    </LinearLayout>

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp" />

    <!--<com.example.donghe.wxfriend.MultiImageView-->
        <!--android:id="@+id/gridview"-->

        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="wrap_content"-->
        <!--android:layout_marginTop="4dp" />-->
    <com.lvfq.code.dynamic.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" />


</LinearLayout>
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

5.RatioImageView :

该类有两个功能:a.是用于ImageView被按下时有变暗效果

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

/**
 * 根据宽高比例自动计算高度ImageView
 */
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);
    
 

6.初始化 ImageLoader

import android.app.Application;

import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;

public class AppApplication extends Application 

    @Override
    public void onCreate() 
        super.onCreate();
        initImageLoader();
    

    private void initImageLoader() 
        ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
        ImageLoader.getInstance().init(configuration);

    

7.values中的atts文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="RatioImageView">
        <attr name="ratio" format="float"/>
    </declare-styleable>

    <declare-styleable name="NineGridLayout">
        <attr name="sapcing" format="dimension"/>
    </declare-styleable>
</resources>

8.ImageLoaderUtil

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

import com.example.donghe.wxfriend.R;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;

public class ImageLoaderUtil 

    public static ImageLoader getImageLoader(Context context) 
        return ImageLoader.getInstance();
    

    public static DisplayImageOptions getPhotoImageOption() 
        Integer extra = 1;
        DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true).cacheOnDisk(true)
                .showImageForEmptyUri(R.drawable.banner_default).showImageOnFail(R.drawable.banner_default)
                .showImageOnLoading(R.drawable.banner_default)
                .extraForDownloader(extra)
                .bitmapConfig(Bitmap.Config.RGB_565).build();
        return options;
    

    public static void displayImage(Context context, ImageView imageView, String url, DisplayImageOptions options) 
        getImageLoader(context).displayImage(url, imageView, options);
    

    public static void displayImage(Context context, ImageView imageView, String url, DisplayImageOptions options, ImageLoadingListener listener) 
        getImageLoader(context).displayImage(url, imageView, options, listener);
    

最后在lib中添加上ImageLoader的jar即可

参考

相关九宫格源码:(类似微博微信的图片九宫格)

https://github.com/HMY314/NineGridLayout

https://github.com/panyiho/NineGridView

https://github.com/GitLqr/LQRNineGridImageView

https://github.com/EdwardSituwende/NinePatchAvatar

https://github.com/yangchaojiang/ZoomPreviewPicture

https://github.com/mzule/NineGridLayout

https://github.com/Vanish136/NineGridView

快速开发库--微信、支付宝支付(含签名,下单)、自定义View(验证码/密码,九宫格图片上传等)、图片预览、RxJava、EventBus、base层封装等

https://github.com/sdwfqin/AndroidQuick

以上是关于Android 仿朋友圈之九宫格多图显示的主要内容,如果未能解决你的问题,请参考以下文章

Android控件GridView之仿支付宝钱包首页带有分割线的GridView九宫格的完美实现

Android控件GridView之仿支付宝钱包首页带有分割线的GridView九宫格的完美实现

android自定义View——仿九宫格解锁

给我两分钟的时间:微博风格九宫格:UICollectionView实现

Android进阶之自定义View实战九宫格手势解锁实现

Android仿天猫横向滑动指示器