Android模仿微信選擇圖片

前言

最近公司需要做一個類似微信那種選擇頭像和上傳圖片的功能轮傍,本想上github上找的,后來想了想首装,還是自己做一個创夜,不僅方便以后用(畢竟自己寫的修改起來也比較方便),還可以學到一些知識仙逻,廢話少說驰吓,先看效果圖:



(ps:大致的功能就這樣揍魂,拍照功能就是拍了照片之后跳到剪切的頁面,這里沒有截出來棚瘟,主要是為了壓縮下gif圖的大邢终)

現(xiàn)在來簡單的介紹下有什么功能:

  • 可根據(jù)傳入的值控制是選擇頭像還是上傳圖片,大于1就是選擇圖片偎蘸,等于1救是選擇頭像
  • 可根據(jù)傳入的值控制選擇圖片的數(shù)量庄蹋,并且當選中的圖片數(shù)量等于這個數(shù)量時,則其他沒有被選中的圖片變成不可選擇
  • 可選擇不同文件夾下的圖片
  • 自定義ViewGroup顯示選中的圖片數(shù)迷雪,不同的數(shù)量顯示的格式不一樣(gif最后那一幀錄制的不是很清楚)限书,類似微信那種,1張章咧,2張還是9張顯示的格式不同
  • 拍照剪切頭像

大概下就是這么些倦西,現(xiàn)在我們來說說實現(xiàn)的思路,主要分為幾步

  • 在子線程中讀出sd卡下所有的文件夾下的圖片赁严,并且在RecyclerView中顯示出來
  • RecyclerView采用多item布局方式扰柠,分開拍照和圖片,主要是方便修改拍照的view疼约,這里只是用圖片顯示
  • 適配每個圖片等寬度和高度為屏幕寬度等三分之一
  • 底部采用PopupWindow顯示出所有的圖片所在的文件夾
  • 如果是多選圖片卤档,則為每個view添加checkbox的選中監(jiān)聽,否則就調(diào)用系統(tǒng)的剪切圖片功能程剥,剪切完成之后顯示出來
  • 根據(jù)選中的圖片數(shù)劝枣,展示不同的布局

好了,現(xiàn)在我們來看看代碼


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;
    /**
     * 存儲文件夾中的圖片數(shù)量
     */
    private int mPicsSize;
    /**
     * 掃描拿到所有的圖片文件夾
     */
    private List<ImageDirBean> imageDirBeans = new ArrayList<>();
    /**
     * 圖片數(shù)量
     */
    private int totalCount = 0;
    /**
     * 臨時的輔助類织鲸,用于防止同一個文件夾的多次掃描
     */
    private HashSet<String> mDirPaths = new HashSet<>();
    /**
     * 所有的圖片
     */
    private List<ImageBean> mImages = new ArrayList<>();
    /**
     * 選中的圖片集合
     */
    private ArrayList<ImageBean> mSelectImages = new ArrayList<>();
    /**
     * 最大的圖片數(shù)
     */
    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();
            //綁定數(shù)據(jù)
            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 + "張");
    }

    //圖片文件數(shù)據(jù)
    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() {
        //判斷是否有內(nèi)存卡
        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;
//                    //獲取內(nèi)存卡路徑
//                    Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
//                    //通過內(nèi)容解析器解析出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);
            // 下面這句指定調(diào)用相機拍照后的照片存儲的路徑
            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是已經(jīng)選擇的圖片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;
            //獲取內(nèi)存卡路徑
            Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            //通過內(nèi)容解析器解析出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卡中的文件夾已經(jīng)文件夾下的圖片搂擦,并且用兩個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;
    //可選擇的圖片數(shù)采桃,動態(tài)控制選擇圖片
    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();
    }

    //當選擇的圖片大于可選擇圖片數(shù)的時候,就不能繼續(xù)選擇
    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里主要做的就是根據(jù)ImageBean里的isSelect來判斷圖片是否選中,在ViewHolder里對圖片進行適配,選擇完成之后舆驶,根據(jù)不同數(shù)量的圖片顯示不同的布局橱健,來看看代碼


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();
            // 當前子空間實際占據(jù)的寬度
            int childWidth = child.getMeasuredWidth() + lp.leftMargin
                    + lp.rightMargin;
            // 當前子空間實際占據(jù)的高度
            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會被調(diào)用多次沙廉,為了預防重疊
        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();

            // 如果已經(jīng)需要換行
            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();
        // 得到總行數(shù)
        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);
    }
}

因為我們是根據(jù)數(shù)量來顯示圖片布局,也就是說需要自定義的說ViewGroup巨税,onMeasure方法里蟋定,根據(jù)子view的數(shù)量來測量出布局的寬高,它的寬高說由子view的寬高以及數(shù)量決定草添,onLayout就是根據(jù)不同的位置來擺放子view驶兜,具體思路可以看看我的博客http://www.reibang.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;
            }
        }

    }
}

不難远寸,相信大家都很容易理解抄淑,這里不做過多的解析


最后說說兩點需要注意的地方:

  • 如何處理選中圖片,圖片數(shù)量達到最大可選數(shù)和取消選中圖片的時候而晒,RecyclerView刷新時數(shù)據(jù)不會錯亂
  • 如何處理RecyclerView復用時蝇狼,數(shù)據(jù)不會錯亂

其實實現(xiàn)的思路時一樣,給bean對象添加一個子段倡怎,記錄當前圖片等狀態(tài),根據(jù)狀態(tài)來改變view等狀態(tài)贱枣,相信有些人會遇到checkbox復用等時候到坑监署,當RecyclerView復用的時候,checkbox的OnChangeListener是一定會觸發(fā)纽哥,它有兩個狀態(tài)钠乏,選中和沒選中,所以春塌,我們需要在復用等時候去做處理晓避,個人認為最好的辦法就是在bean中記錄狀態(tài),不僅能保證數(shù)據(jù)的正確性只壳,也是最容易處理俏拱,萬物皆對象嘛

最后附上github地址,感興趣的可以下載來看下吼句,有問題歡迎提出
(ps:這里沒有做6.0權限處理锅必,自己可以添加上去,記得添加權限)
https://github.com/ReturnYhh/ImageSelectContainer

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惕艳,一起剝皮案震驚了整個濱河市搞隐,隨后出現(xiàn)的幾起案子驹愚,更是在濱河造成了極大的恐慌,老刑警劉巖劣纲,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逢捺,死亡現(xiàn)場離奇詭異,居然都是意外死亡癞季,警方通過查閱死者的電腦和手機蒸甜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門敢茁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來暮现,“玉大人,你說我怎么就攤上這事射沟』匝玻” “怎么了恨憎?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長郊楣。 經(jīng)常有香客問我憔恳,道長,這世上最難降的妖魔是什么净蚤? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任钥组,我火速辦了婚禮,結果婚禮上今瀑,老公的妹妹穿的比我還像新娘程梦。我一直安慰自己,他們只是感情好橘荠,可當我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布屿附。 她就那樣靜靜地躺著,像睡著了一般哥童。 火紅的嫁衣襯著肌膚如雪挺份。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天贮懈,我揣著相機與錄音匀泊,去河邊找鬼。 笑死朵你,一個胖子當著我的面吹牛各聘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撬呢,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼伦吠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起毛仪,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤搁嗓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后箱靴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腺逛,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年衡怀,在試婚紗的時候發(fā)現(xiàn)自己被綠了棍矛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡抛杨,死狀恐怖够委,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情怖现,我是刑警寧澤茁帽,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站屈嗤,受9級特大地震影響潘拨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜饶号,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一铁追、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧茫船,春花似錦琅束、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至濒生,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間幔欧,已是汗流浹背罪治。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留礁蔗,地道東北人觉义。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像浴井,于是被迫代替她去往敵國和親晒骇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,095評論 25 707
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標下拉刷新...
    皇小弟閱讀 46,756評論 22 665
  • 這段時間一直在不停地為初中畢業(yè)的各種事而忙碌:給老師寫信吶、準備班上的演講洪囤、和小學同學組織聚會……終于在今天完成了...
    靈風Alex閱讀 230評論 2 0
  • 年前,我的微信通訊錄里有了小學同學群剥啤,也有了初中同學群锦溪。在春節(jié)期間,同學們都在商量同一件事情――什么時間聚一聚吧...
    靜夢辰光閱讀 664評論 0 0
  • 去年的四月三日府怯,老爸離開了我們刻诊,回首過去的一年,還是能經(jīng)常想起老爸在世時候牺丙,情景歷歷在目…… 老爸则涯,我...
    阿勇的故事閱讀 172評論 0 0