Android模仿微信选择图片

前言

最近公司需要做一个类似微信那种选择头像和上传图片的功能,本想上github上找的,后来想了想,还是自己做一个,不仅方便以后用(毕竟自己写的修改起来也比较方便),还可以学到一些知识,废话少说,先看效果图:



(ps:大致的功能就这样,拍照功能就是拍了照片之后跳到剪切的页面,这里没有截出来,主要是为了压缩下gif图的大小)

现在来简单的介绍下有什么功能:

  • 可根据传入的值控制是选择头像还是上传图片,大于1就是选择图片,等于1救是选择头像
  • 可根据传入的值控制选择图片的数量,并且当选中的图片数量等于这个数量时,则其他没有被选中的图片变成不可选择
  • 可选择不同文件夹下的图片
  • 自定义ViewGroup显示选中的图片数,不同的数量显示的格式不一样(gif最后那一帧录制的不是很清楚),类似微信那种,1张,2张还是9张显示的格式不同
  • 拍照剪切头像

大概下就是这么些,现在我们来说说实现的思路,主要分为几步

  • 在子线程中读出sd卡下所有的文件夹下的图片,并且在RecyclerView中显示出来
  • RecyclerView采用多item布局方式,分开拍照和图片,主要是方便修改拍照的view,这里只是用图片显示
  • 适配每个图片等宽度和高度为屏幕宽度等三分之一
  • 底部采用PopupWindow显示出所有的图片所在的文件夹
  • 如果是多选图片,则为每个view添加checkbox的选中监听,否则就调用系统的剪切图片功能,剪切完成之后显示出来
  • 根据选中的图片数,展示不同的布局

好了,现在我们来看看代码


ImageSelectActivity

这个activity主要是用来显示从sd中读取出来的图片

package com.myimageselectcontainer;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.provider.MediaStore;
import android.support.annotation.BoolRes;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;

import com.myimageselectcontainer.adapter.ImageDirAdapter;
import com.myimageselectcontainer.adapter.MyAdapter;
import com.myimageselectcontainer.adapter.SpacesItemDecoration;
import com.myimageselectcontainer.bean.ImageBean;
import com.myimageselectcontainer.bean.ImageDirBean;
import com.myimageselectcontainer.click.OnChangeListener;
import com.myimageselectcontainer.click.OnImageDirItemListener;
import com.myimageselectcontainer.click.OnItemClickListener;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;


public class ImageSelectActivity extends AppCompatActivity implements OnItemClickListener,
        OnChangeListener, View.OnClickListener, PopupWindow.OnDismissListener, OnImageDirItemListener {
    private static final int PHOTO_REQUEST_CAMERA = 1;// 拍照
    private static final int PHOTO_REQUEST_CUT = 2;// 结果
    private ProgressDialog mProgressDialog;
    /**
     * 存储文件夹中的图片数量
     */
    private int mPicsSize;
    /**
     * 扫描拿到所有的图片文件夹
     */
    private List<ImageDirBean> imageDirBeans = new ArrayList<>();
    /**
     * 图片数量
     */
    private int totalCount = 0;
    /**
     * 临时的辅助类,用于防止同一个文件夹的多次扫描
     */
    private HashSet<String> mDirPaths = new HashSet<>();
    /**
     * 所有的图片
     */
    private List<ImageBean> mImages = new ArrayList<>();
    /**
     * 选中的图片集合
     */
    private ArrayList<ImageBean> mSelectImages = new ArrayList<>();
    /**
     * 最大的图片数
     */
    private int maxImageCount = 9;
    /**
     * 屏幕高度
     */
    private int mScreenHeight;
    /**
     * 用来存储选中的文件
     */
    private File mSelectFile;

    /**
     * 用于显示全部文件夹
     */
    private PopupWindow mPopupWindow;
    /**
     * 当PopupWindow显示或者消失时改变背景色
     */
    private WindowManager.LayoutParams lp;
    /**
     * 拿到传过来的值,测试选择图片
     */
    private int select;
    /**
     * 存储拍照和选中的图片
     */
    private File file;
    private MyThread mThread;

    private ImageDirBean imageDirBean;
    private ImageBean imageBean;
    private RecyclerView rcyImageSelect;
    private TextView tvImageCount;
    private TextView tvImageDir;
    private TextView tvConfirm;
    private LinearLayout linearLayout;

    private Handler mHandler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            mProgressDialog.dismiss();
            //绑定数据
            setData();
            if (mThread != null && !mThread.isInterrupted()) {
                mThread.isInterrupted();
            }
        }
    };

    private MyAdapter mAdapter;

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


        select = getIntent().getIntExtra("select", 0);
        maxImageCount = select;


        rcyImageSelect = (RecyclerView) findViewById(R.id.rcyView_imageSelect);
        tvImageCount = (TextView) findViewById(R.id.tv_imageCount);
        tvImageDir = (TextView) findViewById(R.id.tv_imageDir);
        tvConfirm = (TextView) findViewById(R.id.tv_confirm);
        linearLayout = (LinearLayout) findViewById(R.id.linearLayout);

        mPopupWindow = new PopupWindow(this);
        //获取屏幕高度,设给PopupWindow
        DisplayMetrics outMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
        mScreenHeight = outMetrics.heightPixels;
        lp = getWindow().getAttributes();

        rcyImageSelect.setLayoutManager(new GridLayoutManager(this, 3));
        tvConfirm.setText("确定" + mSelectImages.size() + "/" + maxImageCount);

        getImageList();
        setImageDirData();

        tvImageDir.setOnClickListener(this);
        tvConfirm.setOnClickListener(this);
        mPopupWindow.setOnDismissListener(this);

        file = new File(Environment.getExternalStorageDirectory(), "temp.jpg");

    }

    private void setData() {
        mAdapter = new MyAdapter(this, maxImageCount, mImages, this, this);
        rcyImageSelect.setAdapter(mAdapter);
        rcyImageSelect.addItemDecoration(new SpacesItemDecoration(2));
        tvImageCount.setText(totalCount + "张");
    }

    //图片文件数据
    private void setImageDirData() {
        if (!imageDirBeans.isEmpty()) {
            View contentView = LayoutInflater.from(this).inflate(R.layout.image_dir_list, null);
            RecyclerView rcyViewImageDir = (RecyclerView) contentView.findViewById(R.id.rcyView_imageDir);
            rcyViewImageDir.setLayoutManager(new LinearLayoutManager(this));
            rcyViewImageDir.setAdapter(new ImageDirAdapter(this, imageDirBeans, this));

            mPopupWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
            mPopupWindow.setHeight((int) (mScreenHeight * 0.7f));
            mPopupWindow.setContentView(contentView);

            mPopupWindow.setOutsideTouchable(true);
            mPopupWindow.setFocusable(true);
            mPopupWindow.showAsDropDown(linearLayout, 0, 0);
            // 设置背景颜色变暗
            lp.alpha = 0.5f;
            getWindow().setAttributes(lp);
        }
    }

    private void getImageList() {
        //判断是否有内存卡
        if (!Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            Toast.makeText(this, "暂无外部存储", Toast.LENGTH_SHORT).show();
        } else {
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setMessage("正在加载...");
            mProgressDialog.show();

            mThread = new MyThread();
            mThread.start();

            //在子线程中读取最多图片的集合
//            new Thread(new Runnable() {
//                @Override
//                public void run() {
//                    //第一张图片
//                    String firstImage = null;
//                    //获取内存卡路径
//                    Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//                    //通过内容解析器解析出png和jpeg格式的图片
//                    ContentResolver mContentResolver = ImageSelectActivity.this
//                            .getContentResolver();
//                    Cursor mCursor = mContentResolver.query(mImageUri, null,
//                            MediaStore.Images.Media.MIME_TYPE + "=? or "
//                                    + MediaStore.Images.Media.MIME_TYPE + "=?",
//                            new String[]{"image/png", "image/jpeg"},
//                            MediaStore.Images.Media.DATE_MODIFIED);
//                    //判断是否存在图片
//                    if (mCursor.getCount() > 0) {
//                        while (mCursor.moveToNext()) {
//                            // 获取图片的路径
//                            String path = mCursor.getString(mCursor
//                                    .getColumnIndex(MediaStore.Images.Media.DATA));
//                            // 拿到第一张图片的路径
//                            if (firstImage == null)
//                                firstImage = path;
//                            // 获取该图片的文件名
//                            File parentFile = new File(path).getParentFile();
//                            if (parentFile == null)
//                                continue;
//                            //获取到文件地址
//                            String dirPath = parentFile.getAbsolutePath();
//                            imageBean = new ImageBean();
//                            imageBean.setPath(path);
//                            mImages.add(imageBean);
//                            if (mDirPaths.contains(dirPath)) {
//                                continue;
//                            } else {
//                                mDirPaths.add(dirPath);
//                                imageDirBean = new ImageDirBean();
//                                imageDirBean.setDir(dirPath);
//                                imageDirBean.setImagePath(path);
//                            }
//                            int picSize = parentFile.list(new FilenameFilter() {
//                                @Override
//                                public boolean accept(File dir, String filename) {
//                                    if (filename.endsWith(".jpg")
//                                            || filename.endsWith(".png")
//                                            || filename.endsWith(".jpeg"))
//                                        return true;
//                                    return false;
//                                }
//                            }).length;
//                            totalCount += picSize;
//
//                            imageDirBean.setImageCount(picSize);
//                            imageDirBeans.add(imageDirBean);
//                            if (picSize > mPicsSize) {
//                                mPicsSize = picSize;
//                            }
//                        }
//                        mCursor.close();
//                        mDirPaths = null;
//                        // 通知Handler扫描图片完成
//                        mHandler.sendEmptyMessage(0x110);
//                    }
//                }
//            }).start();


        }
    }

    @Override
    public void onItemClickListener(View view, int position) {
        if (position != 0) {
            if (maxImageCount == 1) {
                clipPhoto(Uri.fromFile(new File(mImages.get(position).getPath())), PHOTO_REQUEST_CUT);//开始裁减图片
            } else {
                Toast.makeText(this, position + "", Toast.LENGTH_SHORT).show();
            }
        } else if (select == 1) {
            Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            // 下面这句指定调用相机拍照后的照片存储的路径
            cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
            startActivityForResult(cameraIntent, PHOTO_REQUEST_CAMERA);// CAMERA_OK是用作判断返回结果的标识
        }

    }

    @Override
    public void OnChangeListener(int position, boolean isChecked) {
        if (isChecked) {
            mImages.get(position).setSelect(true);
            if (!contains(mSelectImages, mImages.get(position))) {
                mSelectImages.add(mImages.get(position));
                if (mSelectImages.size() == maxImageCount) {
                    mAdapter.notifyData(mSelectImages);
                }
            }
        } else {
            mImages.get(position).setSelect(false);
            if (contains(mSelectImages, mImages.get(position))) {
                mSelectImages.remove(mImages.get(position));
                if (mSelectImages.size() == maxImageCount - 1) {
                    mAdapter.notifyData(mSelectImages);
                }
            }
        }
        tvConfirm.setText("确定" + mSelectImages.size() + "/" + maxImageCount);
    }

    private boolean contains(List<ImageBean> list, ImageBean imageBean) {
        for (ImageBean bean : list) {
            if (bean.getPath().equals(imageBean.getPath())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.tv_imageDir) {
            setImageDirData();
        } else if (v.getId() == R.id.tv_confirm) {
            if (mSelectImages.size() != 0) {
                Intent intent = new Intent();
                intent.putParcelableArrayListExtra("selectImages", mSelectImages);
                setResult(Activity.RESULT_OK, intent);
                finish();
            } else {
                Toast.makeText(this, "请选择至少一张图片", Toast.LENGTH_SHORT).show();
            }
        }
    }

    @Override
    public void onDismiss() {
        // 设置背景颜色变暗
        lp = getWindow().getAttributes();
        lp.alpha = 1.0f;
        getWindow().setAttributes(lp);
    }

    @Override
    public void onImageDirItemListener(View view, int position) {
        mImages.clear();
        mImages.add(null);
        if (mSelectImages.size() > 0) {
            mSelectImages.clear();
        }
        String dir = imageDirBeans.get(position).getDir();
        mSelectFile = new File(dir);
        List<String> imagePath = Arrays.asList(mSelectFile.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String filename) {
                if (filename.endsWith(".jpg") || filename.endsWith(".png")
                        || filename.endsWith(".jpeg"))
                    return true;
                return false;
            }
        }));
        for (int i = 0; i < imagePath.size(); i++) {
            imageBean = new ImageBean();
            imageBean.setPath(dir + "/" + imagePath.get(i));
            imageBean.setSelect(false);
            mImages.add(imageBean);
        }
        tvImageDir.setText(imageDirBeans.get(position).getImageName());
        tvImageCount.setText(imageDirBeans.get(position).getImageCount() + "张");
        mAdapter.notifyDataSetChanged();
        mPopupWindow.dismiss();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK)
            switch (requestCode) {
                case PHOTO_REQUEST_CAMERA:
                    clipPhoto(Uri.fromFile(file), PHOTO_REQUEST_CAMERA);//开始裁减图片
                    break;
                case PHOTO_REQUEST_CUT:
                    Bitmap bitmap = data.getParcelableExtra("data");
                    Intent intent = new Intent();
                    ByteArrayOutputStream bs = new ByteArrayOutputStream();
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, bs);
                    byte[] bitmapByte = bs.toByteArray();
                    intent.putExtra("bitmap", bitmapByte);
                    setResult(RESULT_OK, intent);
                    finish();
                    break;
            }
    }

    private void clipPhoto(Uri uri, int type) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setAction("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");// mUri是已经选择的图片Uri
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 1);// 裁剪框比例
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", 150);// 输出图片大小
        intent.putExtra("outputY", 150);
        intent.putExtra("return-data", true);

        if (type == PHOTO_REQUEST_CAMERA) {
            intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        }
        startActivityForResult(intent, PHOTO_REQUEST_CUT);
    }

    //    利用子线程来读取sd卡的图片
    class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            //第一张图片
            String firstImage = null;
            //获取内存卡路径
            Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            //通过内容解析器解析出png和jpeg格式的图片
            ContentResolver mContentResolver = ImageSelectActivity.this
                    .getContentResolver();
            Cursor mCursor = mContentResolver.query(mImageUri, null,
                    MediaStore.Images.Media.MIME_TYPE + "=? or "
                            + MediaStore.Images.Media.MIME_TYPE + "=?",
                    new String[]{"image/png", "image/jpeg"},
                    MediaStore.Images.Media.DATE_MODIFIED);
            //判断是否存在图片
            if (mCursor.getCount() > 0) {
                mImages.add(null);
                while (mCursor.moveToNext()) {
                    // 获取图片的路径
                    String path = mCursor.getString(mCursor
                            .getColumnIndex(MediaStore.Images.Media.DATA));
                    // 拿到第一张图片的路径
                    if (firstImage == null)
                        firstImage = path;
                    // 获取该图片的文件名
                    File parentFile = new File(path).getParentFile();
                    if (parentFile == null)
                        continue;
                    //获取到文件地址
                    String dirPath = parentFile.getAbsolutePath();
                    imageBean = new ImageBean();
                    imageBean.setPath(path);
                    mImages.add(imageBean);
                    if (mDirPaths.contains(dirPath)) {
                        continue;
                    } else {
                        mDirPaths.add(dirPath);
                        imageDirBean = new ImageDirBean();
                        imageDirBean.setDir(dirPath);
                        imageDirBean.setImagePath(path);
                    }
                    int picSize = parentFile.list(new FilenameFilter() {
                        @Override
                        public boolean accept(File dir, String filename) {
                            if (filename.endsWith(".jpg")
                                    || filename.endsWith(".png")
                                    || filename.endsWith(".jpeg"))
                                return true;
                            return false;
                        }
                    }).length;
                    totalCount += picSize;

                    imageDirBean.setImageCount(picSize);
                    imageDirBeans.add(imageDirBean);
                    if (picSize > mPicsSize) {
                        mPicsSize = picSize;
                    }
                }
                mCursor.close();
                mDirPaths = null;
            }
            // 通知Handler扫描图片完成
            mHandler.sendEmptyMessage(0x110);
        }
    }
}

我们在子线程中读取到sd卡中的文件夹已经文件夹下的图片,并且用两个bean来记录他们,当操作完成之后,通过handler来设置给RecyclerView,我们在来看看adapter


MyAdapter

package com.myimageselectcontainer.adapter;


import android.content.Context;
import android.os.Handler;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.myimageselectcontainer.click.OnChangeListener;
import com.myimageselectcontainer.click.OnItemClickListener;
import com.myimageselectcontainer.R;
import com.myimageselectcontainer.bean.ImageBean;

import java.util.List;

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    //相机布局
    private static final int CAMERA_TYPE = 0;
    //普通布局
    private static final int LAYOUT_TYPE = 1;


    private List<ImageBean> mList;
    private Context mContext;
    private float mScreenWidth;
    //可选择的图片数,动态控制选择图片
    private int mMaxImageCount;
    private OnItemClickListener mOnItemClickListener;
    private OnChangeListener mOnChangeListener;
    private List<ImageBean> mSelectImages;


    public MyAdapter(Context context, int maxImageCount, List<ImageBean> list, OnChangeListener onChangeListener,
                     OnItemClickListener onItemClickListener) {
        //获取屏幕宽度
        WindowManager wm = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics dm = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(dm);
        mScreenWidth = dm.widthPixels;


        this.mContext = context;
        this.mMaxImageCount = maxImageCount;
        this.mOnItemClickListener = onItemClickListener;
        this.mOnChangeListener = onChangeListener;
        this.mList = list;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return CAMERA_TYPE;
        }
        return LAYOUT_TYPE;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view;
        if (viewType == CAMERA_TYPE) {
            view = LayoutInflater.from(mContext).inflate(R.layout.camera_item, parent, false);
            return new CameraViewHolder(view, mOnItemClickListener);
        } else {
            view = LayoutInflater.from(mContext).inflate(R.layout.image_select_item, parent, false);
            return new MyViewHolder(view, mOnItemClickListener, mOnChangeListener);
        }
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, final int position) {
        if (viewHolder instanceof MyViewHolder) {
            MyViewHolder holder = (MyViewHolder) viewHolder;
            holder.cbSelect.setVisibility(View.VISIBLE);
            Glide.with(mContext).load(mList.get(position).getPath()).into(holder.imageView);
            if (mList.get(position).isSelect()) {
                holder.cbSelect.setChecked(true);
                holder.canSelect();
            } else {
                if (mSelectImages == null) {
                    holder.canSelect();
                } else if (mSelectImages.size() == mMaxImageCount) {
                    holder.cannotSelect();
                } else {
                    holder.canSelect();
                }
                holder.cbSelect.setChecked(false);
            }

            if (mMaxImageCount == 1) {
                holder.cbSelect.setVisibility(View.GONE);
            }
        }
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }

    //当选择的图片大于可选择图片数的时候,就不能继续选择
    public void notifyData(List<ImageBean> list) {
        mSelectImages = list;
        Handler handler = new Handler();
        handler.post(new Runnable() {
            @Override
            public void run() {
                notifyDataSetChanged();
            }
        });
    }

    class MyViewHolder extends RecyclerView.ViewHolder
            implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
        private ImageView imageView;
        private CheckBox cbSelect;
        private OnItemClickListener mOnItemClickListener;
        private OnChangeListener mOnChangeListener;

        public MyViewHolder(View itemView, OnItemClickListener onItemClickListener,
                            OnChangeListener onChangeListener) {
            super(itemView);
            this.mOnItemClickListener = onItemClickListener;
            this.mOnChangeListener = onChangeListener;
            imageView = (ImageView) itemView.findViewById(R.id.imageView);
            cbSelect = (CheckBox) itemView.findViewById(R.id.ch_select);
            //适配imageView,正方形,宽和高都是屏幕宽度的1/3
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) imageView.getLayoutParams();
            params.width = (int) mScreenWidth / 3 - params.rightMargin - params.leftMargin;
            params.height = (int) mScreenWidth / 3 - params.topMargin - params.bottomMargin;
            imageView.setLayoutParams(params);
            if (onItemClickListener != null) {
                itemView.setOnClickListener(this);
            }
            if (onChangeListener != null) {
                cbSelect.setOnCheckedChangeListener(this);
            }
        }

        @Override
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                mOnItemClickListener.onItemClickListener(v, getAdapterPosition());
            }
        }

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (mOnChangeListener != null) {
                mOnChangeListener.OnChangeListener(getAdapterPosition(), isChecked);
            }
        }

        public void cannotSelect() {
            imageView.setAlpha(0.3f);
            cbSelect.setClickable(false);
        }

        public void canSelect() {
            imageView.setAlpha(1.0f);
            cbSelect.setClickable(true);
        }
    }

    class CameraViewHolder extends RecyclerView.ViewHolder
            implements View.OnClickListener {
        private ImageView imageView;
        private OnItemClickListener mOnItemClickListener;

        public CameraViewHolder(View itemView, OnItemClickListener onItemClickListener) {
            super(itemView);
            this.mOnItemClickListener = onItemClickListener;
            imageView = (ImageView) itemView.findViewById(R.id.imageView);
//            适配imageView,正方形,宽和高都是屏幕宽度的1/3
            LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) imageView.getLayoutParams();
            params.width = (int) mScreenWidth / 3 - params.rightMargin - params.leftMargin;
            params.height = (int) mScreenWidth / 3 - params.topMargin - params.bottomMargin;
            imageView.setLayoutParams(params);
            if (onItemClickListener != null) {
                itemView.setOnClickListener(this);
            }
        }

        @Override
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                mOnItemClickListener.onItemClickListener(v, getAdapterPosition());
            }
        }
    }
}

很简单,onBindViewHolder里主要做的就是根据ImageBean里的isSelect来判断图片是否选中,在ViewHolder里对图片进行适配,选择完成之后,根据不同数量的图片显示不同的布局,来看看代码


NineImageView

package com.myimageselectcontainer.widget;


import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

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

public class NineImageView extends ViewGroup {


    /**
     * 存储所有的View,按行记录
     */
    private List<List<View>> mAllViews = new ArrayList<>();
    /**
     * 记录每一行的最大高度
     */
    private List<Integer> mLineHeight = new ArrayList<>();

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //总宽度和高度
        int totalWidth = 0;
        int totalHeight = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            // 得到child的lp
            MarginLayoutParams lp = (MarginLayoutParams) child
                    .getLayoutParams();
            // 当前子空间实际占据的宽度
            int childWidth = child.getMeasuredWidth() + lp.leftMargin
                    + lp.rightMargin;
            // 当前子空间实际占据的高度
            int childHeight = child.getMeasuredHeight() + lp.topMargin
                    + lp.bottomMargin;
            if (getChildCount() <= 3) {
                totalWidth += childWidth;
                totalHeight = childHeight;
            } else if (getChildCount() <= 6) {
                if (getChildCount() == 4) {
                    totalWidth = childWidth * 2;
                    totalHeight = childHeight * 2;
                } else {
                    totalWidth = childWidth * 3;
                    totalHeight = childHeight * 2;
                }
            } else if (getChildCount() <= 9) {
                totalWidth = childWidth * 3;
                totalHeight = childHeight * 3;
            }
        }
        setMeasuredDimension(totalWidth + getPaddingLeft() + getPaddingRight(),
                totalHeight + getPaddingTop() + getPaddingBottom());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //onLayout会被调用多次,为了预防重叠
        mAllViews.clear();
        mLineHeight.clear();

        //获取总宽度
        int width = getWidth();

        //单行宽度和当行高度
        int lineWidth = 0;
        int lineHeight = 0;
        // 存储每一行所有的childView
        List<View> childViews = new ArrayList<>();
        int childCount = getChildCount();
        // 遍历所有的子view
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            MarginLayoutParams lp = (MarginLayoutParams) child
                    .getLayoutParams();
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            // 如果已经需要换行
            if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width) {
                // 记录这一行所有的View以及最大高度
                mLineHeight.add(lineHeight);
                // 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView
                mAllViews.add(childViews);
                lineWidth = 0;// 重置行宽
                childViews = new ArrayList<>();
            }

            //  如果不需要换行,则累加
            lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
            lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
                    + lp.bottomMargin);
            childViews.add(child);
        }
        // 记录最后一行
        mLineHeight.add(lineHeight);
        mAllViews.add(childViews);

        int left = getPaddingLeft();
        int top = getPaddingTop();
        // 得到总行数
        int lineNum = mAllViews.size();
        for (int i = 0; i < lineNum; i++) {
            // 每一行的所有的views
            childViews = mAllViews.get(i);
            // 当前行的最大高度
            lineHeight = mLineHeight.get(i);

            // 遍历当前行所有的子View
            for (int j = 0; j < childViews.size(); j++) {
                View child = childViews.get(j);
                if (child.getVisibility() != View.GONE) {
                    MarginLayoutParams lp = (MarginLayoutParams) child
                            .getLayoutParams();

                    //计算childView的left,top,right,bottom
                    int childLeft = left + lp.leftMargin;
                    int childTop = top + lp.topMargin;
                    int childRight = childLeft + child.getMeasuredWidth();
                    int childBottom = childTop + child.getMeasuredHeight();

                    child.layout(childLeft, childTop, childRight, childBottom);

                    left += child.getMeasuredWidth() + lp.rightMargin
                            + lp.leftMargin;
                }
            }
            //换行后,重新从第一个开始,高度累加
            left = getPaddingTop();
            top += lineHeight;
        }

    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }
}

因为我们是根据数量来显示图片布局,也就是说需要自定义的说ViewGroup,onMeasure方法里,根据子view的数量来测量出布局的宽高,它的宽高说由子view的宽高以及数量决定,onLayout就是根据不同的位置来摆放子view,具体思路可以看看我的博客//www.greatytc.com/p/730333c61ea3
这篇博客就是当初写这个自定义ViewGroup时写的,感兴趣的可以去看看

最后我们来看看MainActivity


MainActivity

package com.myimageselectcontainer;

import android.app.Activity;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

import com.bumptech.glide.Glide;
import com.myimageselectcontainer.bean.ImageBean;
import com.myimageselectcontainer.widget.NineImageView;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {
    private NineImageView mNineImageView;
    private ImageView imageView;
    private Button button,button1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button = (Button) findViewById(R.id.button);
        button1= (Button) findViewById(R.id.button1);
        mNineImageView = (NineImageView) findViewById(R.id.nineImageView);
        imageView= (ImageView) findViewById(R.id.imageView);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, ImageSelectActivity.class);
                intent.putExtra("select",9);
                startActivityForResult(intent, 0);
            }
        });
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, ImageSelectActivity.class);
                intent.putExtra("select",1);
                startActivityForResult(intent, 1);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode==Activity.RESULT_OK){
            switch (requestCode){
                //多图
                case 0:
                    Bundle bundle = data.getExtras();
                    ArrayList<ImageBean> list = bundle.getParcelableArrayList("selectImages");
                    LayoutInflater inflater = LayoutInflater.from(this);
                    //避免重复添加
                    if (mNineImageView.getChildCount() > 0) {
                        mNineImageView.removeAllViews();
                    }
                    for (int i = 0; i < list.size(); i++) {
                        ImageView imageView = (ImageView) inflater.inflate(R.layout.nine_image, mNineImageView, false);
                        Glide.with(this).load(list.get(i).getPath()).into(imageView);
                        mNineImageView.addView(imageView);
                    }
                    break;
                //头像
                case 1:
                    byte [] bitmap=data.getByteArrayExtra("bitmap");
                    Glide.with(this).load(bitmap).into(imageView);
                    break;
            }
        }

    }
}

不难,相信大家都很容易理解,这里不做过多的解析


最后说说两点需要注意的地方:

  • 如何处理选中图片,图片数量达到最大可选数和取消选中图片的时候,RecyclerView刷新时数据不会错乱
  • 如何处理RecyclerView复用时,数据不会错乱

其实实现的思路时一样,给bean对象添加一个子段,记录当前图片等状态,根据状态来改变view等状态,相信有些人会遇到checkbox复用等时候到坑,当RecyclerView复用的时候,checkbox的OnChangeListener是一定会触发,它有两个状态,选中和没选中,所以,我们需要在复用等时候去做处理,个人认为最好的办法就是在bean中记录状态,不仅能保证数据的正确性,也是最容易处理,万物皆对象嘛

最后附上github地址,感兴趣的可以下载来看下,有问题欢迎提出
(ps:这里没有做6.0权限处理,自己可以添加上去,记得添加权限)
https://github.com/ReturnYhh/ImageSelectContainer

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,518评论 25 707
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,712评论 22 664
  • 这段时间一直在不停地为初中毕业的各种事而忙碌:给老师写信呐、准备班上的演讲、和小学同学组织聚会……终于在今天完成了...
    灵风Alex阅读 229评论 2 0
  • 年前,我的微信通讯录里有了小学同学群,也有了初中同学群。在春节期间,同学们都在商量同一件事情――什么时间聚一聚吧...
    静梦辰光阅读 652评论 0 0
  • 去年的四月三日,老爸离开了我们,回首过去的一年,还是能经常想起老爸在世时候,情景历历在目…… 老爸,我...
    阿勇的故事阅读 169评论 0 0