调用系统相机,截图工具与图库(调用系统的图片内容提供者)

文件存储

        android中有两块文件存储区域:internal与external。在早期android手机中,大部分设备含有一个内置的,固定的存储区域,即internal;另外还有一个可移动的存储区域(如sd卡等)即external。internal指的是/data/data/包名下的目录,而外部存储就是通过手机内存或者sd卡,用户可以通过文件管理等软件看到并且可以进行操作的存储区域。

        对于internal来说,当用户卸载app后,系统会将该区域的文件清空,并且一般来说是只有当前app才能访问得到的。
        对于external却不一样,用户,别的app都可以直接访问到,卸载时也不会自己清空。

        getFileDir():获取当前应用的位于internal区域的文件夹。

        getCacheDir():获取当前应用的位于internal区域的临时缓存文件夹。该区域中的文件在不使用时应立即删除掉,并且文件夹的大小应该限制在一个合理的范围内(如1M)。当系统内存不足时,系统会自动回收该区域中的文件。

        openFileOutput(String,int):以流的形式打开getFileDir()目录中某个指定的文件,其中第一个参数表示要打开的文件的名字。如下:

try {
            FileOutputStream temp = openFileOutput("temp", MODE_PRIVATE);
            temp.write("这是测试了啊啊啊".getBytes());
            temp.close();
            File f = new File(getFilesDir(),"temp");
            FileInputStream inputStream = new FileInputStream(f);
            byte[] buffer = new byte[1024*10];
            int read = inputStream.read(buffer);
            String s = new String(buffer,0,read);
            Log.e("tAG",s);//s的值就是temp.write中写入的string
        } catch (Exception e) {
            e.printStackTrace();
        } 
        getFilesDir(),getCacheDir(),getDatabasePath(String)获取的是internal存储区域。
        getExternalFilesDir(),getExternalCacheDir():得到的便是external。一般来讲,它指的是sd卡中的/Android/data/包名。当app被卸载时,该文件夹下的所有文件也会被删除。
        Environment.getExternalStorageDirectory():得到的是sd卡的根路径,自己可以在该路径下建立指定名称的文件夹。

        Environment.getExternalStoragePublicDirectory(String):获取系统生成的某个文件夹在external中的路径。如参数为Environment.DIRECTORY_DCIM便可能获取到DCIM文件夹。

        读取external区域的文件时需要添加WRITE_EXTERNAL_STORAGE权限,添加该权限后,默认的也会有READ_EXTERNAL_STORAGE权限。但是读取internal区域的文件时,不需要添加任何权限。

调用系统相机

具体见代码

Intent intent = new Intent();
				intent.setAction("android.media.action.IMAGE_CAPTURE");
				intent.addCategory("android.intent.category.DEFAULT");
				Uri uri = Uri.fromFile(file);
				intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
				startActivityForResult(intent, 100);

        其中intent.putExtra(MediaStore.EXTRA_OUTPUT,Uri),它是指定拍照后的照片存储到哪一个文件中。之所以设置它,是因为有可能拍照后无法获取拍照的图片,所以就可以把要图片存储到一个指定的文件中,然后通过流的方式进行操作,避免获取不到图片。

        上述的方法是自己指定图片的存储位置(包括图片名)。但是有一个弊端:没有办法通过ContentResolver访问系统的图片库得到自己调用相机所照的图片。为此,可以用下面一个更简单方法来完成

Intent intent = new Intent();
				intent.setAction("android.media.action.IMAGE_CAPTURE");
				intent.addCategory("android.intent.category.DEFAULT");
				/*
				 * 先向图片库中添加一条空数据,得到一个uri。然后再向这个uri中添加图片。
				 * 这样即省时,也安全。因为uri之类的都是由系统生在的。
				 * 而且也可以在图片库中得到刚照的图片。
				 */
				ContentValues values = new ContentValues();
				Uri uri = SelectActivity.this.getContentResolver().insert(
						MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
				intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
				intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
				
				startActivityForResult(intent, 100);

        但上述方法也有一个问题:调用系统相机时已经向数据库中插入了一条数据,当从照相界面直接按返回键时,这条数据也是存在的,而且系统也会在相应的位置创建一个文件(这个文件的大小是0kb,但是实实在在的存在)。因此,当用ContentResolver去访问图片库,可以获得相应的路径,id等属性。然后用BitmapFactory去解析路径时,返回的bitmap为null。故此,在解析的时候一定要判断该路径中的文件是不是存在

调用系统截图工具

先看代码(参见http://blog.csdn.net/floodingfire/article/details/8144604)

Intent intent = new Intent("com.android.camera.action.CROP");
		intent.setDataAndType(uri, "image/*");
		intent.putExtra("crop", true);
		intent.putExtra("aspectX", 2);
		intent.putExtra("aspectY", 1);
		intent.putExtra("outputX", 150);
		intent.putExtra("outputY", 150);
		//intent.putExtra("return-data", true);
		intent.putExtra("return-data", false);
		intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(fileCrop));
		startActivityForResult(intent, 50);
        其中setDataAndType()中第一个参数指的是要截取的图片的Uri。

        outputX与outputY指的是截取的图片在保存的时候的大小。如代码中设置的是150*150,那么该图片在保存的时候的大小就是150*150。

        最后一个和调用相机的时候传入的参数是一样的,也是指定截取的图片要保存的位置。

        return_data为true和false的意思,参见http://blog.csdn.net/floodingfire/article/details/8144587。

由于有可能无法获取到图片,所以最好是使用指定文件存储路径的方法,不要直接使用intent.getExtras().getParcelable("data")这种形式。

        aspectX与aspectY:在截图时x,y的比例。要注意:即使设置了不同的数字,但是图片保存时的大小还是按outputX,outputY来保存。

具体如下图:


        还可以再设置一个参数,intent.putExtra("noFaceDetection", true);这是为了防止人脸识别的

调用系统图库

代码:

Intent intent = new Intent();
				intent.setAction(Intent.ACTION_PICK);
				intent.addCategory("android.intent.category.DEFAULT");
				intent.setType("image/*");
				startActivityForResult(intent, 140);
这样会打开系统的图库,把所有的图片都会列出来。

获取相应的图片:

if(data != null){//data是Intent类型的
					Uri uri = data.getData();
					//startPhotoZoom(uri);
					/*Bundle extras = data.getExtras();
					Bitmap bitmap = extras.getParcelable("data");*/
				}
        其中得到的uri便是用户所选择的图片的uri。下面看一下ImageGallery.java中的源代码:
Intent result = new Intent(null, img.fullSizeImageUri());
            if (myExtras != null && myExtras.getBoolean("return-data")) {
                // The size of a transaction should be below 100K.
                Bitmap bitmap = img.fullSizeBitmap(
                        IImage.UNCONSTRAINED, 100 * 1024);
                if (bitmap != null) {
                    result.putExtra("data", bitmap);
                }
            }
            setResult(RESULT_OK, result);
            finish();
        首先,在result初始化的时候,便传入了一个uri(img.fullSizeImageUri()),而这个值正是我们通过data.getData()得到的,它代表的是原图的uri。

        其次,在源码的if判断中还为result传入了"data"等值。这个值就是通过

Bundle extras = data.getExtras();
Bitmap bitmap = extras.getParcelable("data");

        获取到的。但是是要注意:这里得到的bitmap是一个缩略图,并不是原图片。而且有些时候不一定可以得到。因此,不建议使用这种方法,而建议使用data.getData()直接获取原图的uri,然后通过查询数据库获取图片的地址。如下:

                    Uri uri = data.getData();//图片的uri
                    Cursor query = getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null);
                    query.moveToFirst();
                    String path = query.getString(0);//图片的文件路径
                    query.close();
        上述data.getData()获取的便是new Intent时传入的第二个参数,即图片在数据库中的uri。

访问系统的图片数据库

        android中为图片数据库设置了一个内容提供者。因此可以通过ContentResolver访问 。该数据库所在的位置为:/data/data/com.android.provider.media。

示例:

ContentResolver resolver = getContentResolver();//Context调用
		Cursor cursor = resolver.query(Media.EXTERNAL_CONTENT_URI,
				new String[] { Media.BUCKET_DISPLAY_NAME, Media.DISPLAY_NAME,
						Media.DATA }, null, null, null);
		if (cursor.moveToNext()) {
			String count = cursor.getString(0);
			String name = cursor.getString(1);
			//照片-/storage/sdcard1/相机/照片/IMG_20140327_125017.jpg--IMG_20140327_125017.jpg
			System.out.println(count + "-"+cursor.getString(2)+"--" + name);
		}
        其中Media为:android.provider.MediaStore.Images.Media;

由上面可知:

        Media.BUCKET_DISPLAY_NAME指的是图片所在的文件夹的名字,

        DATA指的是图片的绝对路径,

        DISPLAY_NAME图片名字(包含后缀名)。

        DATE_TAKEN:拍照时间

        TITLE:照片名(不包含后缀名)

        MIME_TYPE:类型

        SIZE:大小。以字节为单位(如:1474761约等于1.41M)。

数据库为



后面一部分:


        由于可以知道某一张图片所在的文件夹的名字(Media.BUCKET_DISPLAY_NAME)与该文件夹对应的id(Media.BUCKET_ID)。因此,可以得到手机中所有含有图片的文件夹。又由于也可以得到某个图片文件的绝对路径,因此可以做成QQ中图片的界面效果(先列出所有的含有图片的文件夹,然后点击进去查看该文件夹中所有的图片)。

         由于查询的时候有可能会很长,所以这里需要采用异步查询的方法。最好使用的是:AsyncQueryHandler。

代码:

//开启异步任务进行查询。使用系统自带的AsyncQueryHandler进行操作.它会自动的开启新线程进行查询,查询结束后,会执行onQueryComplete
		AsyncQueryHandler handler = new AsyncQueryHandler(getContentResolver()) {
			@Override
			protected void onQueryComplete(int token, Object cookie,
					Cursor cursor) {
				while (cursor.moveToNext()) {
					String id = cursor.getString(0);
					String name = cursor.getString(1);
					System.out.println(id + "----id----name:" + name);
					if (!bucketIds.contains(id)) {// 如果该id是第一次出现,就把该id给记录下来。并且不重复记录
						bucketIds.add(id);
						buckets.put(id, new BucketItem());
					}
					// 对同一个id的BucketItem进行赋值
					BucketItem item = buckets.get(id);
					// 对图片路径进行赋值
					List<ImageItem> images = item.getImages();
					if (images == null) {
						images = new ArrayList<ImageItem>();
					}
					ImageItem imageItem = new ImageItem();
					imageItem.setPath(cursor.getString(2));
					imageItem.setId(cursor.getString(3));

					images.add(imageItem);
					item.setImages(images);

					item.setId(id);
					item.setName(name);

					int count = item.getCount();
					item.setCount(++count);
					buckets.put(id, item);
				}
				cursor.close();
				begin();//数据封装完毕后,应该进行的操作。比如用ListView进行显示等。
			}
		};

		String[] projection = new String[] { Media.BUCKET_ID,
				Media.BUCKET_DISPLAY_NAME, Media.DATA, Media._ID };
		handler.startQuery(10, null, Media.EXTERNAL_CONTENT_URI,
				projection, null, null, null);





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值