我是靠谱客的博主 迷路乐曲,最近开发中收集的这篇文章主要介绍Android实现本地图片选择及预览缩放效果仿春雨医生,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

在做项目时常常会遇到选择本地图片的需求。曾经都是懒得写直接调用系统方法来选择图片。可是这样并不能实现多选效果。近期又遇到了,所以还是写一个demo好了。以后也方便使用。还是首先来看看效果


显示的图片使用RecyclerView实现的,利用Glide来载入;以下弹出的图片目录效果是採用PopupWindow实现,这里比採用PopupWindow更方便,弹出显示的左边图片是这个目录里的第一张图片;选中的图片能够进行预览,使用网上一个大神写的来实现的;至于图片的获取是用ContentProvider。


看看主界面的布局文件。上面一栏是一个返回button和一个跳转预览界面的button。依据是否有选中的图片来设置它的点击和显示状态。中间就是一个用于显示图片的RecyclerView,左下角是显示目录的名字可点击切换。右下角就是确定button。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.cdxsc.imageselect_y.ImageSelecteActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@android:color/white">
<ImageButton
android:id="@+id/ib_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:background="@mipmap/action_bar_back_normal" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/ib_back"
android:text="选择图片"
android:textColor="#000"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:enabled="false"
android:text="预览"
android:textColor="#BEBFBF"
android:textSize="16sp" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="#eeeeee" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"></android.support.v7.widget.RecyclerView>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:id="@+id/tv_allPic"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:clickable="true"
android:gravity="center_vertical"
android:text="全部图片"
android:textColor="@android:color/black"
android:textSize="16sp" />
<Button
android:id="@+id/bt_confirm"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:background="@drawable/shape_disable"
android:enabled="false"
android:text="确定"
android:textColor="#676767"
android:textSize="16sp" />
</RelativeLayout>
</LinearLayout>


好了。如今看主界面的代码

public class ImageSelecteActivity extends AppCompatActivity {
private static final String TAG = "lzy";
@BindView(R.id.ib_back)
ImageButton mButtonBack;
@BindView(R.id.tv_preview)
TextView mTextViewPreview;
@BindView(R.id.rv)
RecyclerView mRecyclerView;
@BindView(R.id.tv_allPic)
TextView mTextViewAllPic;
@BindView(R.id.bt_confirm)
Button mButtonConfirm;
private GalleryPopupWindow mPopupWindow;
//存储每一个目录下的图片路径,key是文件名称
private Map<String, List<String>> mGroupMap = new HashMap<>();
private List<ImageBean> list = new ArrayList<>();
//当前目录显示的图片路径
private List<String> listPath = new ArrayList<>();
//所选择的图片路径集合
private ArrayList<String> listSelectedPath = new ArrayList<>();
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//扫描完毕后
getGalleryList();
listPath.clear();
listPath.addAll(mGroupMap.get("全部图片"));
adapter.update(listPath);
if (mPopupWindow != null)
mPopupWindow.notifyDataChanged();
}
};
private ImageSelectAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_selecte);
ButterKnife.bind(this);
init();
}
private void init() {
getImages();
mRecyclerView.setLayoutManager(new GridLayoutManager(ImageSelecteActivity.this, 3));
adapter = new ImageSelectAdapter(this, listPath);
mRecyclerView.setAdapter(adapter);
adapter.setOnCheckedChangedListener(onCheckedChangedListener);
}
@OnClick({R.id.ib_back, R.id.tv_preview, R.id.tv_allPic, R.id.bt_confirm})
public void onClick(View view) {
switch (view.getId()) {
case R.id.ib_back:
finish();
break;
case R.id.tv_preview://跳转预览界面
Intent intent = new Intent(ImageSelecteActivity.this, ImagePreviewActivity.class);
//把选中的图片集合传入预览界面
intent.putStringArrayListExtra("pic", listSelectedPath);
startActivity(intent);
break;
case R.id.tv_allPic://选择图片目录
if (mPopupWindow == null) {
//把目录列表的集合传入显示
mPopupWindow = new GalleryPopupWindow(this, list);
mPopupWindow.setOnItemClickListener(new GalleryPopupWindow.OnItemClickListener() {
@Override
public void onItemClick(String fileName) {
//切换了目录。清除之前的选择的信息
setButtonDisable();
listPath.clear();
listSelectedPath.clear();
//把当前选择的目录内图片的路径放入listPath,更新界面
listPath.addAll(mGroupMap.get(fileName));
adapter.update(listPath);
mTextViewAllPic.setText(fileName);
}
});
}
mPopupWindow.showAtLocation(mRecyclerView, Gravity.BOTTOM, 0, dp2px(50, ImageSelecteActivity.this));
break;
case R.id.bt_confirm://确定
for (int i = 0; i < listSelectedPath.size(); i++) {
//这里可通过Glide把它转为Bitmap
Glide.with(this).load("file://" + listSelectedPath.get(i)).asBitmap().into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
Log.i(TAG, "onResourceReady: " + resource);
}
});
}
break;
}
}
/**
* dp转px
*/
public static int dp2px(int dp, Context context) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
context.getResources().getDisplayMetrics());
}
//选择图片变化的监听
private ImageSelectAdapter.OnCheckedChangedListener onCheckedChangedListener = new ImageSelectAdapter.OnCheckedChangedListener() {
@Override
public void onChanged(boolean isChecked, String path, CheckBox cb, int position) {
if (isChecked) {//选中
if (listSelectedPath.size() == 9) {
Toast.makeText(ImageSelecteActivity.this, "最多选择9张图片", Toast.LENGTH_SHORT).show();
//把点击变为checked的图片变为没有checked
cb.setChecked(false);
adapter.setCheckedBoxFalse(position);
return;
}
//选中的图片路径加入集合
listSelectedPath.add(path);
} else {//取消选中
//从集合中移除
if (listSelectedPath.contains(path))
listSelectedPath.remove(path);
}
//假设没有选中的button不可点击
if (listSelectedPath.size() == 0) {
setButtonDisable();
} else {
setButtonEnable();
}
}
};
//选中图片时的button状态
private void setButtonEnable() {
mButtonConfirm.setBackgroundResource(R.drawable.selector_bt);
mButtonConfirm.setTextColor(Color.parseColor("#ffffff"));
mButtonConfirm.setEnabled(true);
mTextViewPreview.setEnabled(true);
mTextViewPreview.setTextColor(getResources().getColor(R.color.colorAccent));
mButtonConfirm.setText("确定" + listSelectedPath.size() + "/9");
}
//没有选择时button状态
private void setButtonDisable() {
mButtonConfirm.setBackgroundResource(R.drawable.shape_disable);
mButtonConfirm.setTextColor(Color.parseColor("#676767"));
mButtonConfirm.setEnabled(false);
mTextViewPreview.setEnabled(false);
mTextViewPreview.setTextColor(Color.parseColor("#BEBFBF"));
mButtonConfirm.setText("确定");
}
/**
* 利用ContentProvider扫描手机中的图片。此方法在执行在子线程中
*/
private void getImages() {
new Thread(new Runnable() {
@Override
public void run() {
Uri mImageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
ContentResolver mContentResolver = ImageSelecteActivity.this.getContentResolver();
//仅仅查询jpeg和png的图片
//
Cursor mCursor = mContentResolver.query(mImageUri, null,
//
MediaStore.Images.Media.MIME_TYPE + "=? or "
//
+ MediaStore.Images.Media.MIME_TYPE + "=? or " + MediaStore.Images.Media.MIME_TYPE + "=?",
//
new String[]{"image/jpeg", "image/png", "image/jpg"}, MediaStore.Images.Media.DATE_MODIFIED);
Cursor mCursor = mContentResolver.query(mImageUri, null, null, null,
MediaStore.Images.Media.DATE_MODIFIED);
if (mCursor == null) {
return;
}
//存放全部图片的路径
List<String> listAllPic = new ArrayList<String>();
while (mCursor.moveToNext()) {
//获取图片的路径
String path = mCursor.getString(mCursor
.getColumnIndex(MediaStore.Images.Media.DATA));
//获取该图片的父路径名
String parentName = new File(path).getParentFile().getName();
listAllPic.add(path);
//依据父路径名将图片放入到mGruopMap中
if (!mGroupMap.containsKey(parentName)) {
List<String> chileList = new ArrayList<String>();
chileList.add(path);
mGroupMap.put(parentName, chileList);
} else {
mGroupMap.get(parentName).add(path);
}
}
//加入全部图片
mGroupMap.put("全部图片", listAllPic);
//通知Handler扫描图片完毕
mHandler.sendEmptyMessage(0);
mCursor.close();
}
}).start();
}
//获取相冊目录列表
private void getGalleryList() {
Iterator<Map.Entry<String, List<String>>> iterator = mGroupMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<String>> next = iterator.next();
ImageBean imageBean = new ImageBean();
imageBean.setFileName(next.getKey());
imageBean.setFirstPicPath(next.getValue().get(0));
imageBean.setCount(next.getValue().size());
if (next.getKey().equals("全部图片"))
list.add(0, imageBean);
else
list.add(imageBean);
}
}
}

·mGroupMap:这个是以目录名为key,目录内的图片路径集合为value,也就是依照目录来分别存储了全部图片的路径。

·listPath:保存的是当前显示在界面上的目录内的图片路径集合

·listSelectedPath:保存用户选中的图片路径

·list:保存的是ImageBean的集合。ImageBean保存了目录名、里面首张图片的路径以及里面所包括图片的数量,当切换目录时用于显示

·getImages():这种方法就是用来扫描手机里图片并保存的,这是在子线程中执行的,显示这可能是一个耗时的任务。通过ContentProvider获取到一个包括全部图片的Cursor,然后遍历这个Cursor把所需的数据就保存在mGroupMap里面,最后利用Handler通知界面更新。
·getGalleryList():这种方法就是mGroupMap里面的数据来给list赋值,也就是产生一个现实目录列表所需的数据集合。

·GalleryPopupWindow也就是我们用于显示文件列表的,在67--84行就是一些GalleryPopupWindow的设置,调用showAtLocation方法把PopupWindow显示在距离底部50dp的位置,并设置了点击的回调,当切换了一个目录后要做的相关操作就在这里进行。GalleryPopupWindow再待会再详细看看


接下来再看看中间RecyclerView的Adapter

public class ImageSelectAdapter extends RecyclerView.Adapter<ImageSelectAdapter.NViewHolder> {
private Context context;
private List<String> list = new ArrayList<>();
private OnCheckedChangedListener onCheckedChangedListener;
private List<Boolean> listChecked = new ArrayList<>();
public ImageSelectAdapter(Context context, List<String> list) {
this.context = context;
this.list.addAll(list);
setListCheched(list);
}
public void update(List<String> list) {
this.list.clear();
this.list.addAll(list);
setListCheched(list);
notifyDataSetChanged();
}
/**
* 设置listChecked的初始值
*
* @param list
*/
private void setListCheched(List<String> list) {
listChecked.clear();
for (int i = 0; i < list.size(); i++) {
listChecked.add(false);
}
}
//当点击超过了九张图片,再点击的设置为false
public void setCheckedBoxFalse(int pos) {
listChecked.set(pos, false);
}
public interface OnCheckedChangedListener {
/**
* @param isChecked 是否选中
* @param path
点击的图片路径
* @param cb
点击的CheckBox
* @param pos
点击的位置
*/
void onChanged(boolean isChecked, String path, CheckBox cb, int pos);
}
public void setOnCheckedChangedListener(OnCheckedChangedListener onCheckedChangedListener) {
this.onCheckedChangedListener = onCheckedChangedListener;
}
@Override
public NViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new NViewHolder(LayoutInflater.from(context).inflate(R.layout.item_image_select, parent, false));
}
@Override
public void onBindViewHolder(final NViewHolder holder, final int position) {
Glide.with(context).load("file://" + list.get(position)).into(holder.iv);
holder.cb.setChecked(listChecked.get(position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.cb.setChecked(!holder.cb.isChecked());
if (holder.cb.isChecked()) {
listChecked.set(position, true);
} else {
listChecked.set(position, false);
}
if (onCheckedChangedListener != null) {
onCheckedChangedListener.onChanged(holder.cb.isChecked(), list.get(position), holder.cb, position);
}
}
});
}
@Override
public int getItemCount() {
return list.size();
}
public class NViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.iv_itemImageSelect)
ImageView iv;
@BindView(R.id.cb_itemImageSelect)
CheckBox cb;
public NViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}

这里Item的布局文件就是一个ImageView加一个CheckBox。依据选中状态改变CheckBox的状态,这里就不贴出来了。

·listChecked:这个集合是用来存储每一个位置是否Check的,假设在onBindViewHolder里面不设置CheckBox的状态的话,由于复用问题会出问题,所以想出了用一个集合来保存它们状态的方法,不知道大家有没有其它更好的方法。

·OnCheckedChangedListener:向外暴露的接口,把点击的位置等參数都传到Activity中去。

·update():这种方法用来更新界面的,没有採用直接调notifyDataSetChanged方法是由于,假设数据的数量变化了那么listChecked的数量也要发生变化才行这样才干相应。所以写了这种方法。


再接着看看GalleryPopupWindow

/**
* Created by lzy on 2017/2/8.
*/
public class GalleryPopupWindow extends PopupWindow {
private static final String TAG = "lzy";
RecyclerView mRecyclerView;
private Activity activity;
private GalleryPopupWindow.OnItemClickListener onItemClickListener;
private List<ImageBean> list;
private GalleryAdapter adapter;
public GalleryPopupWindow(Activity context, List<ImageBean> list) {
super(context);
this.activity = context;
this.list = list;
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View contentView = inflater.inflate(R.layout.popu_gallery, null);
initView(contentView);
int h = context.getWindowManager().getDefaultDisplay().getHeight();
int w = context.getWindowManager().getDefaultDisplay().getWidth();
this.setContentView(contentView);
this.setWidth(w);
this.setHeight(ImageSelecteActivity.dp2px(350, context));
this.setFocusable(false);
this.setOutsideTouchable(true);
this.update();
setBackgroundDrawable(new ColorDrawable(000000000));
}
public void notifyDataChanged() {
adapter.notifyDataSetChanged();
}
private void initView(View contentView) {
mRecyclerView = (RecyclerView) contentView.findViewById(R.id.rv_gallery);
mRecyclerView.setLayoutManager(new LinearLayoutManager(activity));
adapter = new GalleryAdapter(list, activity);
adapter.setOnItemClickListener(new GalleryAdapter.OnItemClickListener() {
@Override
public void onItemClick(String fileName) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(fileName);
dismiss();
}
}
});
mRecyclerView.setAdapter(adapter);
}
//暴露点击的接口
public interface OnItemClickListener {
/**
* @param keyValue
*/
void onItemClick(String keyValue);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
}


这个PopupWindow的布局文件就是一个RecyclerView,所以这里面也没什么。也就是设置RecyclerView。然后向外暴露一个点击的接口,用于Activity接收是点击了哪个目录,所以接口參数也就是目录名,再看看这个PopupWindow的Adapter

/**
* Created by lzy on 2017/2/8.
*/
public class GalleryAdapter extends RecyclerView.Adapter<GalleryAdapter.NViewHolder> {
private Context context;
private List<ImageBean> list;
private OnItemClickListener onItemClickListener;
//用于记录是选中的哪一个目录
private int selectedPos;
public GalleryAdapter(List<ImageBean> list, Context context) {
this.list = list;
this.context = context;
}
public interface OnItemClickListener {
void onItemClick(String fileName);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
@Override
public NViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new NViewHolder(LayoutInflater.from(context).inflate(R.layout.item_gallery, parent, false));
}
@Override
public void onBindViewHolder(NViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
selectedPos = position;
notifyDataSetChanged();
if (onItemClickListener != null) {
onItemClickListener.onItemClick(list.get(position).getFileName());
}
}
});
if (position == selectedPos) {
holder.ivCheck.setVisibility(View.VISIBLE);
} else {
holder.ivCheck.setVisibility(View.GONE);
}
holder.tvCount.setText(list.get(position).getCount() + "张");
holder.tvName.setText(list.get(position).getFileName());
Glide.with(context).load("file://" + list.get(position).getFirstPicPath()).into(holder.iv);
}
@Override
public int getItemCount() {
return list.size();
}
public class NViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.iv_itemGallery)
ImageView iv;
@BindView(R.id.tv_itemGallery_name)
TextView tvName;
@BindView(R.id.tv_itemGallery_count)
TextView tvCount;
@BindView(R.id.iv_itemGallery_check)
ImageView ivCheck;
public NViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}

这里有个接口是把点击的文件名称传递给PopupWindow。然后再给Activity。selectedPos是用来记录选择的是哪一个目录。显示相应的CheckBox。


这里就几乎相同完毕了,感兴趣的能够下载Demo来看看。

再说一下,这里显示图片都是採用的Glide。使用也非常方便,我们获取的图片路径都是文件路径。假设要转化为Bitmap也能够直接调用Glide的方法就能够轻松实现,例如以下所看到的:

Glide.with(this).load("file://" + listSelectedPath.get(i)).asBitmap().into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation<?

super Bitmap> glideAnimation) { Log.i(TAG, "onResourceReady: " + resource); } });


当中找寻控件都没有使用findViewById,而是採用的ButterKnife。节约了大量的时间,顺便说说导入的方法

在app以下的build.gradle中加入以下:

apply plugin: 'com.neenbedankt.android-apt'
apt 'com.jakewharton:butterknife-compiler:8.1.0'
compile 'com.github.bumptech.glide:glide:3.5.2'

项目以下的build.gradle

 //加入apt插件
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

加入插件

File->Setting->Plugins  搜索zelezny。例如以下所看到的


当须要使用的时候。直接在光标移动到布局文件,点击Alt+Insert。选择Generate ButterKnife Injections

就出现例如以下界面,能够自己主动生成了



源代码地址:http://download.csdn.net/detail/lylodyf/9768761


最后

以上就是迷路乐曲为你收集整理的Android实现本地图片选择及预览缩放效果仿春雨医生的全部内容,希望文章能够帮你解决Android实现本地图片选择及预览缩放效果仿春雨医生所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(42)

评论列表共有 0 条评论

立即
投稿
返回
顶部