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。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<activity
    android:name="com.photoselector.ui.PhotoSelectorActivity"
    android:label="图片选择" >
</activity>
<activity
    android:name="com.photoselector.ui.PhotoPreviewActivity"
    android:label="图片预览" >
</activity>

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

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

/** 获取最近照片列表 */
public List<PhotoModel> getCurrent() {
	Cursor cursor = resolver.query(Media.EXTERNAL_CONTENT_URI, new String[] { ImageColumns.DATA,
			ImageColumns.DATE_ADDED, ImageColumns.SIZE }, null, null, ImageColumns.DATE_ADDED);
	if (cursor == null || !cursor.moveToNext())
		return new ArrayList<PhotoModel>();
	List<PhotoModel> photos = new ArrayList<PhotoModel>();
	cursor.moveToLast();
	do {
		if (cursor.getLong(cursor.getColumnIndex(ImageColumns.SIZE)) > 1024 * 10) {
			PhotoModel photoModel = new PhotoModel();
			photoModel.setOriginalPath(cursor.getString(cursor.getColumnIndex(ImageColumns.DATA)));
			photos.add(photoModel);
		}
	} while (cursor.moveToPrevious());
	return photos;
}
public void getReccent(final OnLocalReccentListener listener) {
	final Handler handler = new Handler() {
		@SuppressWarnings("unchecked")
		@Override
		public void handleMessage(Message msg) {
			listener.onPhotoLoaded((List<PhotoModel>) msg.obj);
		}
	};
	new Thread(new Runnable() {
		@Override
		public void run() {
			List<PhotoModel> photos = albumController.getCurrent();
			Message msg = new Message();
			msg.obj = photos;
			handler.sendMessage(msg);
		}
	}).start();
}
private OnLocalReccentListener reccentListener = new OnLocalReccentListener() {
	@Override
	public void onPhotoLoaded(List<PhotoModel> photos) {
		if (tvAlbum.getText().equals(RECCENT_PHOTO))
			photos.add(0, new PhotoModel());
		photoAdapter.update(photos);
		gvPhotos.smoothScrollToPosition(0); // 滚动到顶端
		reset();
	}
};

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

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

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
	if (!isCheckAll) {
		listener.onCheckedChanged(photo, buttonView, isChecked); // 调用主界面回调函数
	}
	// 让图片变暗或者变亮
	if (isChecked) {
		setDrawingable();
		ivPhoto.setColorFilter(Color.GRAY, PorterDuff.Mode.MULTIPLY);
	} else {
		ivPhoto.clearColorFilter();
	}
	photo.setChecked(isChecked);
}
private ArrayList<PhotoModel> selected;
@Override
/** 照片选中状态改变之后 */
public void onCheckedChanged(PhotoModel photoModel, CompoundButton buttonView, boolean isChecked) {
	if (isChecked) {
		selected.add(photoModel);
		tvPreview.setEnabled(true);
	} else {
		selected.remove(photoModel);
	}
	tvPreview.setText("预览(" + selected.size() + ")");  //修改预览数量

	if (selected.isEmpty()) {
		tvPreview.setEnabled(false);
		tvPreview.setText("预览");
	}
}

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

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

DisplayImageOptions defaultDisplayImageOptions = new DisplayImageOptions.Builder() //
		.considerExifParams(true) // 调整图片方向
		.resetViewBeforeLoading(true) // 载入之前重置ImageView
		.showImageOnLoading(R.drawable.ic_picture_loading) // 载入时图片设置为黑色
		.showImageOnFail(R.drawable.ic_picture_loadfailed) // 加载失败时显示的图片
		.delayBeforeLoading(0) // 载入之前的延迟时间
		.build(); //
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
		.defaultDisplayImageOptions(defaultDisplayImageOptions).memoryCacheExtraOptions(480, 800)
		.threadPoolSize(5).build();
ImageLoader.getInstance().init(config);
new Handler().postDelayed(new Runnable() {
	@Override
	public void run() {
		ImageLoader.getInstance().displayImage("file://" + photo.getOriginalPath(), ivPhoto);
	}
}, new Random().nextInt(10));

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

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

/** 预览照片 */
private void priview() {
	Bundle bundle = new Bundle();
	bundle.putSerializable("photos", selected);
	CommonUtils.launchActivity(this, PhotoPreviewActivity.class, bundle);
}
@Override
/** 点击查看照片 */
public void onItemClick(int position) {
	Bundle bundle = new Bundle();
	if (tvAlbum.getText().toString().equals(RECCENT_PHOTO))
		bundle.putInt("position", position - 1);
	else
		bundle.putInt("position", position);
	bundle.putString("album", tvAlbum.getText().toString());
	CommonUtils.launchActivity(this, PhotoPreviewActivity.class, bundle);
}
@SuppressWarnings("unchecked")
protected void init(Bundle extras) {
	if (extras == null)
		return;

	if (extras.containsKey("photos")) { // 预览图片
		photos = (List<PhotoModel>) extras.getSerializable("photos");
		current = extras.getInt("position", 0);
		updatePercent();
		bindData();
	} else if (extras.containsKey("album")) { // 点击图片查看
		String albumName = extras.getString("album"); // 相册
		this.current = extras.getInt("position");
		if (!CommonUtils.isNull(albumName) && albumName.equals(PhotoSelectorActivity.RECCENT_PHOTO)) {
			photoSelectorDomain.getReccent(this);
		} else {
			photoSelectorDomain.getAlbum(albumName, this);
		}
	}
}

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

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

/** 弹出相册列表 */
private void popAlbum() {
	layoutAlbum.setVisibility(View.VISIBLE);
	new AnimationUtil(getApplicationContext(), R.anim.translate_up_current).setLinearInterpolator().startAnimation(
			layoutAlbum);
}

/** 隐藏相册列表 */
private void hideAlbum() {
	new AnimationUtil(getApplicationContext(), R.anim.translate_down).setLinearInterpolator().startAnimation(
			layoutAlbum);
	layoutAlbum.setVisibility(View.GONE);
}
private OnLocalAlbumListener albumListener = new OnLocalAlbumListener() {
	@Override
	public void onAlbumLoaded(List<AlbumModel> albums) {
		albumAdapter.update(albums);
	}
};
/** 获取所有相册列表 */
public List<AlbumModel> getAlbums() {
	List<AlbumModel> albums = new ArrayList<AlbumModel>();
	Map<String, AlbumModel> map = new HashMap<String, AlbumModel>();
	Cursor cursor = resolver.query(Media.EXTERNAL_CONTENT_URI, new String[] { ImageColumns.DATA,
			ImageColumns.BUCKET_DISPLAY_NAME, ImageColumns.SIZE }, null, null, null);
	if (cursor == null || !cursor.moveToNext())
		return new ArrayList<AlbumModel>();
	cursor.moveToLast();
	AlbumModel current = new AlbumModel("最近照片", 0, cursor.getString(cursor.getColumnIndex(ImageColumns.DATA)), true); // "最近照片"相册
	albums.add(current);
	do {
		if (cursor.getInt(cursor.getColumnIndex(ImageColumns.SIZE)) < 1024 * 10)
			continue;

		current.increaseCount();
		String name = cursor.getString(cursor.getColumnIndex(ImageColumns.BUCKET_DISPLAY_NAME));
		if (map.keySet().contains(name))
			map.get(name).increaseCount();
		else {
			AlbumModel album = new AlbumModel(name, 1, cursor.getString(cursor.getColumnIndex(ImageColumns.DATA)));
			map.put(name, album);
			albums.add(album);
		}
	} while (cursor.moveToPrevious());
	return albums;
}
/** 获取对应相册下的照片 */
public List<PhotoModel> getAlbum(String name) {
	Cursor cursor = resolver.query(Media.EXTERNAL_CONTENT_URI, new String[] { ImageColumns.BUCKET_DISPLAY_NAME,
			ImageColumns.DATA, ImageColumns.DATE_ADDED, ImageColumns.SIZE }, "bucket_display_name = ?",
			new String[] { name }, ImageColumns.DATE_ADDED);
	if (cursor == null || !cursor.moveToNext())
		return new ArrayList<PhotoModel>();
	List<PhotoModel> photos = new ArrayList<PhotoModel>();
	cursor.moveToLast();
	do {
		if (cursor.getLong(cursor.getColumnIndex(ImageColumns.SIZE)) > 1024 * 10) {
			PhotoModel photoModel = new PhotoModel();
			photoModel.setOriginalPath(cursor.getString(cursor.getColumnIndex(ImageColumns.DATA)));
			photos.add(photoModel);
		}
	} while (cursor.moveToPrevious());
	return photos;
}

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
	PhotoItem item = null;
	TextView tvCamera = null;
	if (position == 0 && CommonUtils.isNull(models.get(position).getOriginalPath())) { // 当时第一个时,显示按钮
		if (convertView == null || !(convertView instanceof TextView)) {
			tvCamera = (TextView) LayoutInflater.from(context).inflate(R.layout.view_camera, null);
			tvCamera.setHeight(itemWidth);
			tvCamera.setWidth(itemWidth);
			convertView = tvCamera;
		}
		convertView.setOnClickListener(cameraListener);
	} else { // 显示图片
		if (convertView == null || !(convertView instanceof PhotoItem)) {
			item = new PhotoItem(context, listener);
			item.setLayoutParams(itemLayoutParams);
			convertView = item;
		} else {
			item = (PhotoItem) convertView;
		}
		item.setImageDrawable(models.get(position));
		item.setSelected(models.get(position).isChecked());
		item.setOnClickListener(mCallback, position);
	}
	return convertView;
}
/** 拍照 */
private void catchPicture() {
	CommonUtils.launchActivityForResult(this, new Intent(MediaStore.ACTION_IMAGE_CAPTURE), REQUEST_CAMERA);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {
		PhotoModel photoModel = new PhotoModel(CommonUtils.query(getApplicationContext(), data.getData()));
		selected.clear();
		selected.add(photoModel);
		ok();
	}
}

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

/** 完成 */
private void ok() {
	if (selected.isEmpty()) {
		setResult(RESULT_CANCELED);
	} else {
		Intent data = new Intent();
		Bundle bundle = new Bundle();
		bundle.putSerializable("photos", selected);
		data.putExtras(bundle);
		setResult(RESULT_OK, data);
	}
	finish();
}

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

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

@Override
public void onClick(View v) {
	//CommonUtils是library中的一个工具类
	CommonUtils.launchActivityForResult(this, PhotoSelectorActivity.class, 0);
	tvPath.setText("");
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
	if (requestCode == 0 && resultCode == RESULT_OK) {
		if (data != null && data.getExtras() != null) {
			@SuppressWarnings("unchecked")
			List<PhotoModel> photos = (List<PhotoModel>) data.getExtras().getSerializable("photos");
			if (photos == null || photos.isEmpty())
				return;
			StringBuffer sb = new StringBuffer();
			for (PhotoModel photo : photos) {
				sb.append(photo.getOriginalPath() + "\r\n");
			}
			tvPath.setText(sb.toString());
		}

	}
}

附上源码:library以及Demo

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

时间: 2024-08-09 15:46:03

android 图片选择器 图片预览的相关文章

Android实现本地图片选择及预览缩放效果仿春雨医生

在做项目时经常会遇到选择本地图片的需求,以前都是懒得写直接调用系统方法来选择图片,但是这样并不能实现多选效果,最近又遇到了,所以还是写一个demo好了,以后也方便使用.还是首先来看看效果 显示的图片使用RecyclerView实现的,利用Glide来加载:下面弹出的图片文件夹效果是采用PopupWindow实现,这里比采用PopupWindow更方便,弹出显示的左边图片是这个文件夹里的第一张图片:选中的图片可以进行预览,使用网上一个大神写的来实现的:至于图片的获取是用ContentProvide

玩转Android Camera开发(四):预览界面四周暗中间亮,只拍摄矩形区域图片(附完整源码)

杂家前文曾写过一篇关于只拍摄特定区域图片的demo,只是比较简陋,在坐标的换算上不是很严谨,而且没有完成预览界面四周暗中间亮的效果,深以为憾,今天把这个补齐了. 在上代码之前首先交代下,这里面存在着换算的两种模式.第一种,是以屏幕上的矩形区域为基准进行换算.举个例子,屏幕中间一个 矩形框为100dip*100dip.这里一定要使用dip为单位,否则在不同的手机上屏幕呈现的矩形框大小不一样.先将这个dip换算成px,然后根据屏幕的宽和高的像素计算出矩形区域,传给Surfaceview上铺的一层Vi

移动端图片上传预览

前天要做wap版的图片上传预览,找了好半天才找到比较适合的插件,我在该插件的基础上修改了一些东西,比如:上传后的图片删除后不能再添加.不能限制上传图片的数量. input虽然有multiple(多选),但是android目前是不支持的. 该插件控制不了不能上传同一张图片,暂时没有思路解决这个问题(:′д`)ゞ 1 <!DOCTYPE html> 2 <html lang="zh-cn"> 3 4 <head> 5 <meta charset=&

JS实现图片上传预览效果:方法一

<script type="text/javascript"> //处理file input加载的图片文件 $(document).ready(function(e) { //判断浏览器是否有FileReader接口 if(typeof FileReader =='undefined') { /*$("#images_show").css({'background':'none'}).html('亲,您的浏览器还不支持HTML5的FileReader接口

JavaScript 图片上传预览效果

图片上传预览是一种在图片上传之前对图片进行本地预览的技术.使用户选择图片后能立即查看图片,而不需上传服务器,提高用户体验.但随着浏览器安全性的提高,要实现图片上传预览也越来越困难.不过群众的智慧是无限的,网上已经有很多变通或先进的方法来实现.例如ie7/ie8的滤镜预览法,firefox 3的getAsDataURL方法.但在opera.safari和chrome还是没有办法实现本地预览,只能通过后台来支持预览.在研究了各种预览方法后,作为总结,写了这个程序,跟大家一起分享.上次写的简便无刷新文

搞清Image加载事件(onload)、加载状态(complete)后,实现图片的本地预览,并自适应于父元素内

onload与complete介绍 complete只是HTMLImageElement对象的一个属性,可以判断图片加载完成,不管图片是不是有缓存:而onload则是这个Image对象的load事件回调,当图片加载完成后执行onload绑定的函数. 给下面一个例子,解释下: document.getElementById('load').onclick = function() { var img = new Image(); img.src="images/avatar.png";

图片上传预览

例1: 1.先创建一个file表单域,我们需要用它来浏览本地文件. <form name="form1" id="form1" method="post" action="upload.php"> <input type="file" name="file1" id="file1" /> </form> 2.试下效果: 判断文件类型

js:s上次预览,上传图片预览,图片上传预览

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-

html,图片上传预览,input file获取文件等相关操作

input file常用方法: var obj=document.getElementById("upimage"); var file=obj.files[0];//获取文件数据 var path=obj.value;//获取文件当前路径 var size=obj.files[0].size;//获取文件大小 var prefix=path.substring( path.lastIndexOf('\\')+1 );//获取文件名的前缀名(文件格式) var suffix=path.