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存储区域。
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);