前言
最近公司需要做一个类似微信那种选择头像和上传图片的功能,本想上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