前言
最近公司需要做一個類似微信那種選擇頭像和上傳圖片的功能轮傍,本想上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