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

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


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


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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<?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>


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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/** * 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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/** * 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的方法就能够轻松实现,例如以下所看到的:

复制代码
1
2
3
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中加入以下:

复制代码
1
apply plugin: 'com.neenbedankt.android-apt'
复制代码
1
2
apt 'com.jakewharton:butterknife-compiler:8.1.0' compile 'com.github.bumptech.glide:glide:3.5.2'

项目以下的build.gradle

复制代码
1
2
//加入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实现本地图片选择及预览缩放效果仿春雨医生内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部