android 图片选择器 图片预览 带源码下载 超级好用

需求:近段时间公司有要求写一个类似于微信发送图片时,用来选择照片的一个图片浏览器,本来想在网上找一个直接拿来用,找寻无果,只能自己写了。相信有很多网页也有这样的需求,这里我将写好的源码打包成library工程分享给大家!!



转载请注明出处:http://blog.csdn.net/a740169405/article/details/41622025



说明:

①本来打算自己写图片异步加载代码,后来因为赶时间,就改成直接引用开源框架universal-image-loader,一个             android上知名度很高的图片加载框架。

②预览图片的时候,使用到了一个开源框架,忘了叫什么啦,只记得包名叫polites.android,这里直接引入了源               码,对不住作者了 - _ - 。

③为了兼容低版本,library以及测试项目的开发平台均为android2.3.3。

④点击查看照片时,为了防止手机上照片太对,使用bundle在activity之间传递会导致崩溃,所以使用进入预览                 界面之后重新从本地读取图片信息。

⑤项目中使用了一个PhotoModel实体类,用来存放照片的路径信息,以及照片被选中的状态。



简单入门:

大家只需要引入library工程,并在清单文件中注册两个activity以及加入读取SD卡权限即可,使用例子以及源码放在文章最后



功能介绍:

大致实现了图片选择、预览、切换相册、拍照。并将选中的照片信息返回。



预览效果:


照片选择


预览


相册选择


关键实现:

〇:首先要说明的是如何使用我的源码,引入library,并在你自己的清单文件中加入读取SD卡权限,以及注册两个activity。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <activity  
  2.     android:name="com.photoselector.ui.PhotoSelectorActivity"  
  3.     android:label="图片选择" >  
  4. </activity>  
  5. <activity  
  6.     android:name="com.photoselector.ui.PhotoPreviewActivity"  
  7.     android:label="图片预览" >  
  8. </activity>  

①首先,一进入照片选择界面,需要将最近照片显示出来,使用ContentResolver查询最近照片,这里我采用了异步加载的方式获取数据,并调用主界面的回调函数对适配器adapter进行更新。

说明:过滤掉小于10kb的照片

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 获取最近照片列表 */  
  2. public List<PhotoModel> getCurrent() {  
  3.     Cursor cursor = resolver.query(Media.EXTERNAL_CONTENT_URI, new String[] { ImageColumns.DATA,  
  4.             ImageColumns.DATE_ADDED, ImageColumns.SIZE }, nullnull, ImageColumns.DATE_ADDED);  
  5.     if (cursor == null || !cursor.moveToNext())  
  6.         return new ArrayList<PhotoModel>();  
  7.     List<PhotoModel> photos = new ArrayList<PhotoModel>();  
  8.     cursor.moveToLast();  
  9.     do {  
  10.         if (cursor.getLong(cursor.getColumnIndex(ImageColumns.SIZE)) > 1024 * 10) {  
  11.             PhotoModel photoModel = new PhotoModel();  
  12.             photoModel.setOriginalPath(cursor.getString(cursor.getColumnIndex(ImageColumns.DATA)));  
  13.             photos.add(photoModel);  
  14.         }  
  15.     } while (cursor.moveToPrevious());  
  16.     return photos;  
  17. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public void getReccent(final OnLocalReccentListener listener) {  
  2.     final Handler handler = new Handler() {  
  3.         @SuppressWarnings("unchecked")  
  4.         @Override  
  5.         public void handleMessage(Message msg) {  
  6.             listener.onPhotoLoaded((List<PhotoModel>) msg.obj);  
  7.         }  
  8.     };  
  9.     new Thread(new Runnable() {  
  10.         @Override  
  11.         public void run() {  
  12.             List<PhotoModel> photos = albumController.getCurrent();  
  13.             Message msg = new Message();  
  14.             msg.obj = photos;  
  15.             handler.sendMessage(msg);  
  16.         }  
  17.     }).start();  
  18. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private OnLocalReccentListener reccentListener = new OnLocalReccentListener() {  
  2.     @Override  
  3.     public void onPhotoLoaded(List<PhotoModel> photos) {  
  4.         if (tvAlbum.getText().equals(RECCENT_PHOTO))  
  5.             photos.add(0new PhotoModel());  
  6.         photoAdapter.update(photos);  
  7.         gvPhotos.smoothScrollToPosition(0); // 滚动到顶端  
  8.         reset();  
  9.     }  
  10. };  

②在图片选择界面,新建一个ArrayList集合变量selected用来存放当前选中的照片信息。

接着将获取到的照片显示在GridView上,这里我对GridView的每一个Item进行了封装,大致就是一个ImageView上面浮着一个CheckBox。在每一个CheckBox状态改变时,调用回调函数往集合中插入当前选中照片的信息。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {  
  3.     if (!isCheckAll) {  
  4.         listener.onCheckedChanged(photo, buttonView, isChecked); // 调用主界面回调函数  
  5.     }  
  6.     // 让图片变暗或者变亮  
  7.     if (isChecked) {  
  8.         setDrawingable();  
  9.         ivPhoto.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);  
  10.     } else {  
  11.         ivPhoto.clearColorFilter();  
  12.     }  
  13.     photo.setChecked(isChecked);  
  14. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private ArrayList<PhotoModel> selected;  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. /** 照片选中状态改变之后 */  
  3. public void onCheckedChanged(PhotoModel photoModel, CompoundButton buttonView, boolean isChecked) {  
  4.     if (isChecked) {  
  5.         selected.add(photoModel);  
  6.         tvPreview.setEnabled(true);  
  7.     } else {  
  8.         selected.remove(photoModel);  
  9.     }  
  10.     tvPreview.setText("预览(" + selected.size() + ")");  //修改预览数量  
  11.   
  12.     if (selected.isEmpty()) {  
  13.         tvPreview.setEnabled(false);  
  14.         tvPreview.setText("预览");  
  15.     }  
  16. }  

③因为使用了图片加载框架,需要在使用之前进行配置。使用该框架加载图片的代码很简洁。

因为对GridView的每一个Item进行了封装,同一时刻加载太多个会导致内存崩溃,这里我采用在一定时间内随机选择一个时间进行加载,这也是为什么有时候后面的Item会先加载完成(不排除小图片先加载完成的可能)

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. DisplayImageOptions defaultDisplayImageOptions = new DisplayImageOptions.Builder() //  
  2.         .considerExifParams(true// 调整图片方向  
  3.         .resetViewBeforeLoading(true// 载入之前重置ImageView  
  4.         .showImageOnLoading(R.drawable.ic_picture_loading) // 载入时图片设置为黑色  
  5.         .showImageOnFail(R.drawable.ic_picture_loadfailed) // 加载失败时显示的图片  
  6.         .delayBeforeLoading(0// 载入之前的延迟时间  
  7.         .build(); //  
  8. ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())  
  9.         .defaultDisplayImageOptions(defaultDisplayImageOptions).memoryCacheExtraOptions(480800)  
  10.         .threadPoolSize(5).build();  
  11. ImageLoader.getInstance().init(config);  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. new Handler().postDelayed(new Runnable() {  
  2.     @Override  
  3.     public void run() {  
  4.         ImageLoader.getInstance().displayImage("file://" + photo.getOriginalPath(), ivPhoto);  
  5.     }  
  6. }, new Random().nextInt(10));  


④点击照片与选中对对张照片之后进行预览都会开启预览界面,但是传递给预览界面的数据是不一样的,这里需要进行处理。

说明:CommonUtils是我封装的一个常用工具类。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 预览照片 */  
  2. private void priview() {  
  3.     Bundle bundle = new Bundle();  
  4.     bundle.putSerializable("photos", selected);  
  5.     CommonUtils.launchActivity(this, PhotoPreviewActivity.class, bundle);  
  6. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. /** 点击查看照片 */  
  3. public void onItemClick(int position) {  
  4.     Bundle bundle = new Bundle();  
  5.     if (tvAlbum.getText().toString().equals(RECCENT_PHOTO))  
  6.         bundle.putInt("position", position - 1);  
  7.     else  
  8.         bundle.putInt("position", position);  
  9.     bundle.putString("album", tvAlbum.getText().toString());  
  10.     CommonUtils.launchActivity(this, PhotoPreviewActivity.class, bundle);  
  11. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @SuppressWarnings("unchecked")  
  2. protected void init(Bundle extras) {  
  3.     if (extras == null)  
  4.         return;  
  5.   
  6.     if (extras.containsKey("photos")) { // 预览图片  
  7.         photos = (List<PhotoModel>) extras.getSerializable("photos");  
  8.         current = extras.getInt("position"0);  
  9.         updatePercent();  
  10.         bindData();  
  11.     } else if (extras.containsKey("album")) { // 点击图片查看  
  12.         String albumName = extras.getString("album"); // 相册  
  13.         this.current = extras.getInt("position");  
  14.         if (!CommonUtils.isNull(albumName) && albumName.equals(PhotoSelectorActivity.RECCENT_PHOTO)) {  
  15.             photoSelectorDomain.getReccent(this);  
  16.         } else {  
  17.             photoSelectorDomain.getAlbum(albumName, this);  
  18.         }  
  19.     }  
  20. }  

⑤相册选择界面我采用的是一个ListView进行显示,并采用动画的形式弹出和收回。相册信息同样是使用 ContentResolver进行获取,这里同样是采用异步回去数据,接着调用主界面回调函数对adapter适配器进行更新

说明:这里的AnimationUtil是我封装的一个用来显示动画的工具类,具体代码可以看源代码。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 弹出相册列表 */  
  2. private void popAlbum() {  
  3.     layoutAlbum.setVisibility(View.VISIBLE);  
  4.     new AnimationUtil(getApplicationContext(), R.anim.translate_up_current).setLinearInterpolator().startAnimation(  
  5.             layoutAlbum);  
  6. }  
  7.   
  8. /** 隐藏相册列表 */  
  9. private void hideAlbum() {  
  10.     new AnimationUtil(getApplicationContext(), R.anim.translate_down).setLinearInterpolator().startAnimation(  
  11.             layoutAlbum);  
  12.     layoutAlbum.setVisibility(View.GONE);  
  13. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. private OnLocalAlbumListener albumListener = new OnLocalAlbumListener() {  
  2.     @Override  
  3.     public void onAlbumLoaded(List<AlbumModel> albums) {  
  4.         albumAdapter.update(albums);  
  5.     }  
  6. };  
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 获取所有相册列表 */  
  2. public List<AlbumModel> getAlbums() {  
  3.     List<AlbumModel> albums = new ArrayList<AlbumModel>();  
  4.     Map<String, AlbumModel> map = new HashMap<String, AlbumModel>();  
  5.     Cursor cursor = resolver.query(Media.EXTERNAL_CONTENT_URI, new String[] { ImageColumns.DATA,  
  6.             ImageColumns.BUCKET_DISPLAY_NAME, ImageColumns.SIZE }, null, null, null);  
  7.     if (cursor == null || !cursor.moveToNext())  
  8.         return new ArrayList<AlbumModel>();  
  9.     cursor.moveToLast();  
  10.     AlbumModel current = new AlbumModel("最近照片", 0, cursor.getString(cursor.getColumnIndex(ImageColumns.DATA)), true); // "最近照片"相册  
  11.     albums.add(current);  
  12.     do {  
  13.         if (cursor.getInt(cursor.getColumnIndex(ImageColumns.SIZE)) < 1024 * 10)  
  14.             continue;  
  15.   
  16.         current.increaseCount();  
  17.         String name = cursor.getString(cursor.getColumnIndex(ImageColumns.BUCKET_DISPLAY_NAME));  
  18.         if (map.keySet().contains(name))  
  19.             map.get(name).increaseCount();  
  20.         else {  
  21.             AlbumModel album = new AlbumModel(name, 1, cursor.getString(cursor.getColumnIndex(ImageColumns.DATA)));  
  22.             map.put(name, album);  
  23.             albums.add(album);  
  24.         }  
  25.     } while (cursor.moveToPrevious());  
  26.     return albums;  
  27. }  
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 获取对应相册下的照片 */  
  2. public List<PhotoModel> getAlbum(String name) {  
  3.     Cursor cursor = resolver.query(Media.EXTERNAL_CONTENT_URI, new String[] { ImageColumns.BUCKET_DISPLAY_NAME,  
  4.             ImageColumns.DATA, ImageColumns.DATE_ADDED, ImageColumns.SIZE }, "bucket_display_name = ?",  
  5.             new String[] { name }, ImageColumns.DATE_ADDED);  
  6.     if (cursor == null || !cursor.moveToNext())  
  7.         return new ArrayList<PhotoModel>();  
  8.     List<PhotoModel> photos = new ArrayList<PhotoModel>();  
  9.     cursor.moveToLast();  
  10.     do {  
  11.         if (cursor.getLong(cursor.getColumnIndex(ImageColumns.SIZE)) > 1024 * 10) {  
  12.             PhotoModel photoModel = new PhotoModel();  
  13.             photoModel.setOriginalPath(cursor.getString(cursor.getColumnIndex(ImageColumns.DATA)));  
  14.             photos.add(photoModel);  
  15.         }  
  16.     } while (cursor.moveToPrevious());  
  17.     return photos;  
  18. }  

⑥最后,如果不想选择图库里的照片,可以使用拍照获取照片并返回。这里的拍照按钮在适配器里进行了处理。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public View getView(int position, View convertView, ViewGroup parent) {  
  3.     PhotoItem item = null;  
  4.     TextView tvCamera = null;  
  5.     if (position == 0 && CommonUtils.isNull(models.get(position).getOriginalPath())) { // 当时第一个时,显示按钮  
  6.         if (convertView == null || !(convertView instanceof TextView)) {  
  7.             tvCamera = (TextView) LayoutInflater.from(context).inflate(R.layout.view_camera, null);  
  8.             tvCamera.setHeight(itemWidth);  
  9.             tvCamera.setWidth(itemWidth);  
  10.             convertView = tvCamera;  
  11.         }  
  12.         convertView.setOnClickListener(cameraListener);  
  13.     } else { // 显示图片  
  14.         if (convertView == null || !(convertView instanceof PhotoItem)) {  
  15.             item = new PhotoItem(context, listener);  
  16.             item.setLayoutParams(itemLayoutParams);  
  17.             convertView = item;  
  18.         } else {  
  19.             item = (PhotoItem) convertView;  
  20.         }  
  21.         item.setImageDrawable(models.get(position));  
  22.         item.setSelected(models.get(position).isChecked());  
  23.         item.setOnClickListener(mCallback, position);  
  24.     }  
  25.     return convertView;  
  26. }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 拍照 */  
  2. private void catchPicture() {  
  3.     CommonUtils.launchActivityForResult(thisnew Intent(MediaStore.ACTION_IMAGE_CAPTURE), REQUEST_CAMERA);  
  4. }  
  5.   
  6. @Override  
  7. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  8.     if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {  
  9.         PhotoModel photoModel = new PhotoModel(CommonUtils.query(getApplicationContext(), data.getData()));  
  10.         selected.clear();  
  11.         selected.add(photoModel);  
  12.         ok();  
  13.     }  
  14. }  

⑦接着,就是将选中或者拍摄的照片路径信息返回,这里我采用setResult的方式放回,所以在使用时需要在需要在调用照片选择器的Activity重写onActivityResult函数。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 完成 */  
  2. private void ok() {  
  3.     if (selected.isEmpty()) {  
  4.         setResult(RESULT_CANCELED);  
  5.     } else {  
  6.         Intent data = new Intent();  
  7.         Bundle bundle = new Bundle();  
  8.         bundle.putSerializable("photos", selected);  
  9.         data.putExtras(bundle);  
  10.         setResult(RESULT_OK, data);  
  11.     }  
  12.     finish();  
  13. }  

⑧最后,如何调用照片选择器以及在调用照片选择器的界面重写onActivityResult函数接受照片信息。

说明:这里的tvPath是我写的Demo项目里,一个用来显示照片路径的TextView。

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void onClick(View v) {  
  3.     //CommonUtils是library中的一个工具类  
  4.     CommonUtils.launchActivityForResult(this, PhotoSelectorActivity.class0);  
  5.     tvPath.setText("");  
  6. }  
  7.   
  8. @Override  
  9. protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  10.     super.onActivityResult(requestCode, resultCode, data);  
  11.     if (requestCode == 0 && resultCode == RESULT_OK) {  
  12.         if (data != null && data.getExtras() != null) {  
  13.             @SuppressWarnings("unchecked")  
  14.             List<PhotoModel> photos = (List<PhotoModel>) data.getExtras().getSerializable("photos");  
  15.             if (photos == null || photos.isEmpty())  
  16.                 return;  
  17.             StringBuffer sb = new StringBuffer();  
  18.             for (PhotoModel photo : photos) {  
  19.                 sb.append(photo.getOriginalPath() + "\r\n");  
  20.             }  
  21.             tvPath.setText(sb.toString());  
  22.         }  
  23.           
  24.     }  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值