Android实现九宫格图片控件

没有效果图的示例简直就是扯淡

下篇文章实现安卓实现微信朋友圈查看图片功能,跟微信朋友圈效果一样,支持拖拽,支持动画,代码完全开发,完全自定义,可自行修改效果。快去看看吧

在这里插入图片描述

控件通过完全自定义的方式实现:具有以下功能:

  1. 完全动态适配手机屏幕宽度;
  2. 单张图片时可以自定义图片大小;
  3. 可自定义多张图片之间的间距;
  4. 可自定义最大限制长度;
  5. 多张图片时根据间距自动计算图片的大小;
  6. 可实现图片的点击功能;
  7. 可实现图片的长按功能;
  8. 大于9张图片时+n布局自动展示;
  9. +n布局可以在代码中自动修改样式;
  10. 支持Glide等多种图片加载框架。

直接看代码吧。

图片加载布局自定义类:
MyCustomImageLayout.class

import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;

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

/**
 * 根据ImageWatcher源码修改得来
 *
 * @author 小口口
 * 自定义显示图片(可显示一张、多张。如果大于9张最后一张会显示+n)
 */
public class MyCustomImageLayout extends FrameLayout implements View.OnClickListener, View.OnLongClickListener {

    //默认布局最多显示9个,超出显示+n
    //也可以通过函数setMaxCount()方法设置
    public int MAX_DISPLAY_COUNT = 9;

    //image布局
    private LayoutParams lpChildImage =
            new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

    //单张最大宽高
    private int mSingleMaxSize;

    //多张图片间距
    private int mSpace;

    //显示view的集合
    private final List<ImageView> iPictureList = new ArrayList<>();
    private final SparseArray<ImageView> mVisiblePictureList = new SparseArray<>();

    //如果超出9个,最后一个+n显示的布局
    private final TextView tOverflowCount;

    //图片点击事件回调
    private Callback mCallback;

    //图片长按点击事件回调
    private OnImageLongClickListener clickListener;

    //图片数据集合
    private List<String> mImageDataLists = new ArrayList<>();

    /**
     * 构造方法(用来处理view初始化)
     *
     * @param context
     * @param attrs
     */
    public MyCustomImageLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);

        //获取单张图片默认大小和间距
        DisplayMetrics mDisplayMetrics = context.getResources().getDisplayMetrics();
        //本来是要也可以通过配置xml文件实现
        //这里用的是直接设置的

        //设置单个图片大小为200dp*200dp
        //这是多个图片之间的间距为5dp
        mSingleMaxSize = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, mDisplayMetrics));
        mSpace = (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, mDisplayMetrics));


        //开始创建9个imageview
        for (int i = 0; i < MAX_DISPLAY_COUNT; i++) {
            ImageView squareImageView = new SquareImageView(context);
            squareImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            squareImageView.setVisibility(View.GONE);
            //new图片的时候添加点击事件和长按事件
            squareImageView.setOnClickListener(this);
            squareImageView.setOnLongClickListener(this);
            addView(squareImageView);
            iPictureList.add(squareImageView);
        }

        //如果图片个数大于9张,这个就是第九个图片上面的+n的view创建
        tOverflowCount = new TextView(context);
        tOverflowCount.setTextColor(0xFFFFFFFF);
        tOverflowCount.setTextSize(24);
        tOverflowCount.setGravity(Gravity.CENTER);
        tOverflowCount.setBackgroundColor(0x66000000);
        tOverflowCount.setVisibility(View.GONE);
        addView(tOverflowCount);
    }

    /**
     * 这个是单张图片的调用方法(废弃了)
     *
     * @param list
     */
    public void setUrlData(String list) {
        mImageDataLists.clear();
        mImageDataLists.add(list);
        mSingleMaxSize = (int) (getWidth() * 1f - mSpace);
        notifyDataChanged();
    }

    /**
     * 设置图片数据方法
     * 目前是用的list集合,如果是数组的话请自行转
     * @param list
     */
    public void setUrlListData(List<String> list) {
        mImageDataLists.clear();
        mImageDataLists = list;
        notifyDataChanged();
    }

    /**
     * 数据刷新
     */
    private void notifyDataChanged() {

        //图片的长度
        final int urlListSize = mImageDataLists.size();

        //如果图片不为空并且大于0,显示布局
        setVisibility(mImageDataLists.size() < 1 ? View.GONE : View.VISIBLE);
        if (mImageDataLists.size() < 1) {
            return;
        }

        //默认每行显示3个图片
        int column = 3;
        //判断每行需要显示的图片数量
        if (urlListSize == 1) {
            column = 1;
        } else if (urlListSize == 4) {
            column = 2;
        }

        //默认显示1行
        int row = 1;
        //判断需要显示的行数
        if (urlListSize > 6) {
            row = 3;
        } else if (urlListSize > 3) {
            row = 2;
        } else if (urlListSize > 0) {
            row = 1;
        }

        //确认每个图片显示的大小了
        int imageSize = 0;
        //如果是一个图片的话,就显示默认的单个图片大小
        if (urlListSize == 1) {
            imageSize = mSingleMaxSize;
        } else if (urlListSize == 2 || urlListSize == 4) {      //如果是两张或者四张的话,每行显示2个,显示2行
            imageSize = (int) ((getWidth() * 1f - mSpace) / 2);
        } else {
            imageSize = (int) ((getWidth() * 1f - mSpace) / 3); //否则显示3个
        }

        //将图片大小给到子布局(宽高设置为一样的)
        lpChildImage.width = imageSize;
        lpChildImage.height = imageSize;

        //如果图片数量大于MAX_DISPLAY_COUNT值,显示+n布局
        tOverflowCount.setVisibility(urlListSize > MAX_DISPLAY_COUNT ? View.VISIBLE : View.GONE);
        tOverflowCount.setText("+ " + (urlListSize - MAX_DISPLAY_COUNT));
        tOverflowCount.setLayoutParams(lpChildImage);

        //加载图片之前,清空已显示的图片集合
        mVisiblePictureList.clear();

        //遍历图片集合
        for (int i = 0; i < iPictureList.size(); i++) {
            //加载每一个图片
            final ImageView iPicture = iPictureList.get(i);
            if (i < urlListSize) {
                iPicture.setVisibility(View.VISIBLE);
                mVisiblePictureList.put(i, iPicture);
                iPicture.setLayoutParams(lpChildImage);
                iPicture.setBackgroundResource(R.mipmap.icon_myphoto_default);
                Glide.with(getContext()).load(mImageDataLists.get(i)).into(iPicture);
                iPicture.setTranslationX((i % column) * (imageSize + mSpace));
                iPicture.setTranslationY((i / column) * (imageSize + mSpace));
                //设置tag,在图片点击事件的时候可以当作图片的下标
                iPicture.setTag(i);
            } else {
                iPicture.setVisibility(View.GONE);
            }

            //第九个单独处理
            if (i == MAX_DISPLAY_COUNT - 1) {
                tOverflowCount.setTranslationX((i % column) * (imageSize + mSpace));
                tOverflowCount.setTranslationY((i / column) * (imageSize + mSpace));
            }
        }

        //根据自布局显示整体布局的高
        getLayoutParams().height = imageSize * row + mSpace * (row - 1);
    }



    /**
     * 设置回调接口
     *
     * @param callback
     */
    public void setOnImageClickListener(Callback callback) {
        mCallback = callback;
    }



    /**
     * 图片的点击事件
     *
     * @param v
     */
    @Override
    public void onClick(View v) {
        if (mCallback != null) {
            mCallback.onImageClick((ImageView) v, mVisiblePictureList, mImageDataLists);
        }
    }

    /**
     * 回调事件接口
     */
    public interface Callback {
        void onImageClick(ImageView i, SparseArray<ImageView> imageGroupList, List<String> urlList);
    }



    /**
     * 长按事件
     *
     * @param v
     * @return
     */
    @Override
    public boolean onLongClick(View v) {
        if (clickListener != null) {
            clickListener.onImageLongClick((ImageView) v, mVisiblePictureList, mImageDataLists);
        }
        return true;
    }

    /**
     * 回调长按事件接口
     */
    public interface OnImageLongClickListener {
        void onImageLongClick(ImageView i, SparseArray<ImageView> imageGroupList, List<String> urlList);
    }



    /**
     * 设置长按事件回调接口
     *
     * @param callback
     */
    public void setOnImageLongClickListener(OnImageLongClickListener callback) {
        clickListener = callback;
    }

    /**
     * 一旦计算出宽高,刷新数据
     *
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        notifyDataChanged();
    }

    /**
     * 设置图片的最大数量
     *
     * @param count
     */
    public void setMaxCount(int count) {
        this.MAX_DISPLAY_COUNT = count;
    }

}
import android.content.Context;
import android.util.AttributeSet;

/**
 * 正方形的ImageView
 * @author 小口口、
 */
public class SquareImageView extends androidx.appcompat.widget.AppCompatImageView {

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        setMeasuredDimension(widthSize, widthSize);
    }
}

就这样,代码完了。


Activity这样调用

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;

import com.google.android.material.snackbar.Snackbar;

import java.util.ArrayList;

/**
 * 主界面
 * @author 小口口、
 */
public class MainActivity extends AppCompatActivity {

    //获取布局
    MyCustomImageLayout mPhotoGridView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mPhotoGridView = findViewById(R.id.image_layout);
        //监听点击事件
        mPhotoGridView.setOnImageClickListener((i, imageGroupList, urlList) -> {
            //图片下标+1
            int index = (int)i.getTag() + 1;
            //单个图片的点击事件
            Snackbar.make(findViewById(R.id.cl), "你点击了第" + index + "个图片", 3000).show();
        });
        //监听长按事件
        mPhotoGridView.setOnImageLongClickListener((i, imageGroupList, urlList) -> {
            //图片下标+1
            int index = (int)i.getTag() + 1;
            //单个图片的长按事件
            Snackbar.make(findViewById(R.id.cl), "你长按了第" + index + "个图片", 3000).show();
        });


        //点击事件
        findViewById(R.id.txt_1).setOnClickListener(v -> setImage(1));
        findViewById(R.id.txt_2).setOnClickListener(v -> setImage(2));
        findViewById(R.id.txt_3).setOnClickListener(v -> setImage(3));
        findViewById(R.id.txt_4).setOnClickListener(v -> setImage(4));
        findViewById(R.id.txt_6).setOnClickListener(v -> setImage(6));
        findViewById(R.id.txt_9).setOnClickListener(v -> setImage(9));
        findViewById(R.id.txt_15).setOnClickListener(v -> setImage(15));
    }

    /**
     * 根据要求生成集合个数
     * @param number
     */
    private void setImage(int number){
        //动态相册集合
        ArrayList<String> imageList = new ArrayList<>();
        for (int i = 0; i < number; i++){
            imageList.add("https://img2.baidu.com/it/u=4067224682,690721702&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=450");
        }
        //设置数据
        mPhotoGridView.setUrlListData(imageList);
    }

}

activity的xml文件是这样的

<com.cc.imageiwatcherdemo.MyCustomImageLayout
        android:id="@+id/image_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        tools:ignore="MissingConstraints" />

另外还有个图片:icon_myphoto_default.png

在这里插入图片描述


呐,代码就这些啦,简单吧~~

附上demo源码。

源码:源码请点这里

如果下不了源码,请联系我。


phone:18588400509
email:mr.cai_cai@foxmail.com

如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。

											                               	---财财亲笔
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android实现九宫格布局可以使用GridView控件,步骤如下: 1. 在布局文件中添加GridView控件,并设置其列数和每个格子的宽高。 ```xml <GridView android:id="@+id/grid_view" android:numColumns="3" android:gravity="center" android:columnWidth="100dp" android:stretchMode="columnWidth" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. 创建一个Adapter,并重写getView方法,用于向GridView中填充数据。 ```java public class GridAdapter extends BaseAdapter { private Context mContext; private List<String> mData; public GridAdapter(Context context, List<String> data) { mContext = context; mData = data; } @Override public int getCount() { return mData.size(); } @Override public Object getItem(int position) { return mData.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = LayoutInflater.from(mContext).inflate(R.layout.grid_item, parent, false); holder = new ViewHolder(); holder.text = convertView.findViewById(R.id.text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.text.setText(mData.get(position)); return convertView; } static class ViewHolder { TextView text; } } ``` 3. 创建一个布局文件用于表示每个格子的样式。 ```xml <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text" android:layout_width="match_parent" android:layout_height="match_parent" android:textSize="20sp" android:gravity="center" android:textColor="@android:color/black" /> ``` 4. 在Activity中设置Adapter并为GridView设置点击事件。 ```java public class MainActivity extends AppCompatActivity { private GridView mGridView; private List<String> mData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); mGridView = findViewById(R.id.grid_view); GridAdapter adapter = new GridAdapter(this, mData); mGridView.setAdapter(adapter); mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MainActivity.this, mData.get(position), Toast.LENGTH_SHORT).show(); } }); } private void initData() { mData = new ArrayList<>(); for (int i = 1; i <= 9; i++) { mData.add("Item " + i); } } } ``` 以上就是在Android实现九宫格布局的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谁抢我的小口口

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值