动画效果如上,主要是获取到系统所有的图片显示出来,在头部大图显示被选中的图片,底部系统图片能上滑覆盖大图,要记录图片被选中的顺序,最多只能选中9张图片,选中有数字提示。
先进行实现该效果的大概步骤分解。1.先实现UI效果,有表格首选RecyclerView;2.获取系统的所有图片;3.将获获取到的图片设置到RecyclerView中;4实现选中数字的标识和大图的显示。
1.UI效果
直接用RecyclerView来显示,直接看代码吧。
mRecyclerView = (RecyclerView) findViewById(R.id.recycler);
mPicContainer = (LinearLayout) findViewById(R.id.pic_container);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 4);
gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
if (position == 0) {
return 4;
} else {
return 1;
}
}
});
mRecyclerView.setLayoutManager(gridLayoutManager);
mRecyclerView.setAdapter(new MyAdapter(this, mData));
下面是Adapter
class MyAdapter extends RecyclerView.Adapter {
private Context mContext;
private List<String> mData;
public MyAdapter(Context context, List<String> data) {
mContext = context;
mData = data;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == 1) {
View view = LayoutInflater.from(mContext).inflate(R.layout.item_head, parent, false);
return new HeadViewHolder(view);
} else {
View view = LayoutInflater.from(mContext).inflate(R.layout.item_body, parent, false);
return new BodyViewHolder(view);
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof HeadViewHolder) {
return;
} if (holder instanceof BodyViewHolder){
setBodyViewHolder(holder, position);
}
}
private void setBodyViewHolder(RecyclerView.ViewHolder holder, int position) {
String path = mData.get(position - 1);
ImageUtil.getInstance().displayImage(mContext, path, ((BodyViewHolder) holder).image, R.color.colorPrimary);//显示出图片
}
@Override
public int getItemCount() {
return mData == null || mData.size() == 0 ? 0 : mData.size();
}
@Override
public int getItemViewType(int position) {
return position == 0 ? 1 : 2;
}
class HeadViewHolder extends RecyclerView.ViewHolder {
public HeadViewHolder(View view) {
super(view);
}
}
class BodyViewHolder extends RecyclerView.ViewHolder {
public ImageView image;
public BodyViewHolder(View view) {
super(view);
image = (ImageView) view.findViewById(R.id.item_body);
}
}
}
Adapter中主要是要将第一个item,及position为0时,显示一个4个item宽度的一个透明的head,这样就不会挡住大图显示的区域。
2.获取到系统所有的图片
private void getAllPics() {
mDataBeanList = new ArrayList<>();
mData = new ArrayList<>();
ContentResolver contentResolver = getContentResolver();
DataBean dataBean = null;
Cursor cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA}, MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?", new String[]{"image/jpeg", "image/png"}, MediaStore.Images.Media._ID + " DESC");
while (cursor.moveToNext()) {
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
File file = new File(path);
if (file.exists()) {
dataBean = new DataBean();
dataBean.filePath = path;
mData.add(path);
mDataBeanList.add(dataBean);
}
}
cursor.close();
}
是通过内容解析者来获取到图片,然后通过cursor去遍历。
3.将获取到的图片设置到RecyclerView中
这一步相对来说就比较简单了,我们在第2步中已经得到了系统中所有的图片的路径,现在只需要将图片显示出来就可以了。这里我借用了雷哥一个ImageUtil图片工具类,封装了显示各种图片(本地,网路等)的方法,内部是通过Glide来实现的,现在直接拿来用。
private void setBodyViewHolder(RecyclerView.ViewHolder holder, int position) {
String path = mData.get(position - 1);
ImageUtil.getInstance().displayImage(mContext, path, ((BodyViewHolder) holder).image, R.color.colorPrimary);//显示出图片
}
这里需要注意的是我们需要将position-1,因为我们留了一个透明的head。到这里为止我们就实现了显示系统所有的图片了。
4.实现选中数字的标识和大图的显示
点击MyAdapter中的数据,需要在MainActivity中去显示,我这里用了接口回调。
((BodyViewHolder) holder).image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(position - 1);
}
}
});
}
// 接口回调用来大图显示被选中的图片
OnItemClickListener mOnItemClickListener;
public interface OnItemClickListener {
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
mOnItemClickListener = onItemClickListener;
}
需要注意的事,这里需要考虑Head的position,所以将position-1。
在MAinActivity中调用该接口。
// 接口回调显示被选中的图片
myAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
// 这里的position已做了-1的处理
showBigPic(position);
}
});
}
private void showBigPic(int position) {
Bitmap bitmap = BitmapFactory.decodeFile(mData.get(position));
int height = bitmap.getHeight();
ViewGroup.LayoutParams layoutParams = mPicContainer.getLayoutParams();
// 计算缩放比例
float sca = layoutParams.height * 1.0f / height;
// 根据高度来确定宽度
layoutParams.width = (int) (bitmap.getWidth() * sca);
// 防止重复
mPicContainer.removeAllViews();
ImageView imageView = new ImageView(this);
imageView.setLayoutParams(layoutParams);
// imageView.setLayoutParams(new ViewGroup.LayoutParams(layoutParams));
ImageUtil.getInstance().displayImage(this,mData.get(position),imageView,R.color.colorPrimary);
mPicContainer.addView(imageView);
}
为了避免图片变形,这里进行了缩放比例的计算。到这里我们已经完成了大部分的功能了,可以显示所有的系统图片,也能大图显示选中的图片。
最后一步就是显示右上角的选中顺序了。
4.1完成选中的数字逻辑
为了完成相应的逻辑,每一个图片都需要赋值多个属性,为了方便,我选择创建了一个Bean对象,这样能够更加简洁。
public class DataBean {
public String filePath;
public boolean isSelected; //是否已经被选中
public int selectedNum; //被选中的顺序
}
在接口回调的点击事件中做判断,看被点击的dateBean的isSelected是否为true,如果是true代表是选中的状态,再次点击取消选中,需要注意的事MyAdapter中的position对应DataBean集合中的position相差1(因为MyAdapter中有一个透明head)。
mMyAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClick(int position) {
DataBean dataBean = mDataBeanList.get(position - 1);
// 这里的position已做了-1的处理
showBigPic(dataBean);
// 已经被选中,再次点击需要取消选中,数字也要相应改变
if (dataBean.isSelected) {
cancel(dataBean);
} else {
// 点击之前没有别选中
selected(dataBean);
// 更新被选中的item
mMyAdapter.notifyItemChanged(position);
}
}
});
private void selected(DataBean dataBean) {
selectedNum++;
// 将所有被选中的dataBean放在一个集合中,便于去比较selectedNum
mMyAdapter.selectedBean.add(dataBean);
dataBean.isSelected = true;
dataBean.selectedNum = selectedNum;
}
private void cancel(DataBean dataBean) {
selectedNum--;
// 遍历选中的dataBean,比较被点击的这个bean的selectedNum和其他selectedNum的大小,比它大的都要-1
for (int i = 0; i < mMyAdapter.selectedBean.size(); i++) {
int z = dataBean.selectedNum;
if (mMyAdapter.selectedBean.get(i).selectedNum > z) {
mMyAdapter.selectedBean.get(i).selectedNum -= 1;
}
}
dataBean.isSelected = false;
dataBean.selectedNum = -1;
mMyAdapter.selectedBean.remove(dataBean);
mMyAdapter.notifyDataSetChanged();
}
ok,最后需要在MyAdapter中做判断
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof HeadViewHolder) {
return;
}
if (holder instanceof BodyViewHolder) {
setBodyViewHolder(((BodyViewHolder) holder), position);
}
}
private void setBodyViewHolder(BodyViewHolder holder, final int position) {
String path = mData.get(position - 1).filePath;
DataBean dataBean = mData.get(position - 1);
ImageUtil.getInstance().displayImage(mContext, path, holder.image, R.color.colorPrimary);//显示出图片
if (dataBean.isSelected) {
// 被选中的item就显示右上角的数字
holder.tvNum.setVisibility(View.VISIBLE);
// 注意这里需要加“”转化为String
holder.tvNum.setText(dataBean.selectedNum + "");
}
holder.image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(position);
}
}
});
}
终于完工了,总结一下,主要是对RecyclerView的应用,需要去设置一个透明的Head,然后就是图片数据的获取,最后填充到MyAdapter中。
Just have a try.