1)获取图像的URI
要获得标准的图像存储路径,我们需要获得MediaStore的引用,而这是通过content resolver来实现的(因为使用Content resolver可以获取content provider,而MediaStore就是一个content provider)。
传递指定的URI给content resolver,可以得到对应的content provider,由于是新增一张图像,所以使用insert方法,相应的URI是android.provider.MediaStore.Images.Media类定义的常量EXTERNAL_CONTENT_URI。这个常量说明我们要将图像存储到主外部存储器中,通常就是SD卡;如果要将图像存储到设备内存中,则使用INTERNAL_CONTENT_URI。当然对于媒体文件的存储而言,由于尺寸一般都比较大,因此会优先考虑使用EXTERNAL_CONTENT_URI。
Content resolver类的insert函数返回值是URI类型:
- Uri imageFileUri = getContentResolver().insert(
- Media.EXTERNAL_CONTENT_URI, new ContentValues());
- // Start the Camera App
- Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
- it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);
- startActivityForResult(it, CAMERA_RESULT);
上面代码中的ContentValues对象是捕获的图像在创建时要关联的元数据,当然,上面的元数据是空的。我们可以使用put函数将元数据信息写入ContentValues中,ContentValues是以键值对的形式存储数据的,键名是定义在android.provider.MediaStore.Images.Media类中的常量:
- // Save the name and description of an image in a ContentValues map
- ContentValues contentValues = new ContentValues(3);
- contentValues.put(Media.DISPLAY_NAME, "ASCE1885_TITLE");
- contentValues.put(Media.DESCRIPTION, "ASCE1885_DESCRIPTION");
- contentValues.put(Media.MIME_TYPE, "image/jpeg");
- // Add a new recode without the bitmap, but with some values set.
- // insert() returns the URI of the new record
- Uri imageFileUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, contentValues);
上面获取的Uri可能类似于:
content://media/external/images/media/16
这里说明一点,以content开头的Uri一般都是被content provider使用的,例如上面的Uri是被MediaStore使用的一样。
反过来根据Uri,我们可以用来检索这个Uri对应路径中的图像数据,代码如下:
- Bitmap bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageFileUri),null,bmpFactory);
- <p> </p>
在我们捕获图像并存放在MediaStore中后,如果还想再增加元数据信息,那么可以使用ContentResolver的update函数来实现:
- // Update the MediaStore record with Title and Description
- ContentValues contentValues = new ContentValues(3);
- contentValues.put(Media.DISPLAY_NAME, "WEN1885_TITLE");
- contentValues.put(Media.DESCRIPTION, "WEN1885_DESCRIPTION");
- getContentResolver().update(imageFileUri, contentValues, null, null);
完整的代码例子如下,先看layout/main.xml文件:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <ImageView
- android:id="@+id/ReturnedImageView"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Title:"
- android:id="@+id/TitleTextView" />
- <EditText
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:id="@+id/TitleEditText"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Description"
- android:id="@+id/DescriptionTextView"/>
- <EditText
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:id="@+id/DescriptionEditText"/>
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/TakePictureButton"
- android:text="Take Picture"/>
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/SaveDataButton"
- android:text="Save Data"/>
- </LinearLayout>
完整的Java代码如下:
- package hust.iprai.asce1885.promedia;
- import java.io.FileNotFoundException;
- import android.app.Activity;
- import android.content.ContentValues;
- import android.content.Intent;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.net.Uri;
- import android.os.Bundle;
- import android.provider.MediaStore.Images.Media;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.EditText;
- import android.widget.ImageView;
- import android.widget.TextView;
- import android.widget.Toast;
- public class MediaStoreCameraActivity extends Activity {
- final static int CAMERA_RESULT = 0;
- Uri imageFileUri = null;
- // User interface elements, specified in res/layout/main.xml
- ImageView returnedImageView;
- Button takePictureButton;
- Button saveDataButton;
- TextView titleTextView;
- TextView descriptionTextView;
- EditText titleEditText;
- EditText descriptionEditText;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Set the content view to be what is defined in the res/layout/main.xml file
- setContentView(R.layout.main);
- // Get references to UI elements
- returnedImageView = (ImageView) findViewById(R.id.ReturnedImageView);
- takePictureButton = (Button) findViewById(R.id.TakePictureButton);
- saveDataButton = (Button) findViewById(R.id.SaveDataButton);
- titleTextView = (TextView) findViewById(R.id.TitleTextView);
- descriptionTextView = (TextView) findViewById(R.id.DescriptionTextView);
- titleEditText = (EditText) findViewById(R.id.TitleEditText);
- descriptionEditText = (EditText) findViewById(R.id.DescriptionEditText);
- // Set all except takePictureButton to not be visible initially
- // View.GONE is invisible and doesn't take up space in the layout
- returnedImageView.setVisibility(View.GONE);
- saveDataButton.setVisibility(View.GONE);
- titleTextView.setVisibility(View.GONE);
- descriptionTextView.setVisibility(View.GONE);
- titleEditText.setVisibility(View.GONE);
- descriptionEditText.setVisibility(View.GONE);
- // When the Take Picture Button is clicked
- takePictureButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- // Add a new record without the bitmap
- // return the URI of the new record
- imageFileUri = getContentResolver().insert(
- Media.EXTERNAL_CONTENT_URI, new ContentValues());
- // Start the Camera App
- Intent it = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
- it.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);
- startActivityForResult(it, CAMERA_RESULT);
- }
- });
- saveDataButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- // Update the MediaStore record with Title and Description
- ContentValues contentValues = new ContentValues(3);
- contentValues.put(Media.DISPLAY_NAME, titleEditText.getText().toString());
- contentValues.put(Media.DESCRIPTION, descriptionEditText.getText().toString());
- getContentResolver().update(imageFileUri, contentValues, null, null);
- // Tell the user
- Toast bread = Toast.makeText(MediaStoreCameraActivity.this, "Record Updated", Toast.LENGTH_LONG);
- bread.show();
- // Go back to the initial state, set Take Picture Button Visible
- // hide other UI elements
- takePictureButton.setVisibility(View.VISIBLE);
- returnedImageView.setVisibility(View.GONE);
- titleTextView.setVisibility(View.GONE);
- descriptionTextView.setVisibility(View.GONE);
- titleEditText.setVisibility(View.GONE);
- descriptionEditText.setVisibility(View.GONE);
- }
- });
- }
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (RESULT_OK == resultCode) {
- // The Camera App has returned
- // Hide the Take Picture Button
- takePictureButton.setVisibility(View.GONE);
- // Show the other UI elements
- saveDataButton.setVisibility(View.VISIBLE);
- returnedImageView.setVisibility(View.VISIBLE);
- titleTextView.setVisibility(View.VISIBLE);
- descriptionTextView.setVisibility(View.VISIBLE);
- titleEditText.setVisibility(View.VISIBLE);
- descriptionEditText.setVisibility(View.VISIBLE);
- // Scale the image
- int dw = 200; // Make it at most 200 pixels wide
- int dh = 200; // Make it at most 200 pixels tall
- BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
- bmpFactoryOptions.inJustDecodeBounds = true;
- Bitmap bmp = null;
- try {
- bmp = BitmapFactory.decodeStream(
- getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight/(float)dh);
- int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth/(float)dw);
- Log.v("HEIGHTRATIO", "" + heightRatio);
- Log.v("WIDTHRATIO", "" + widthRatio);
- // If both of the ratios are greater than 1
- // one of the sides of the image is greater than the screen
- if ((heightRatio > 1) && (widthRatio > 1)) {
- if (heightRatio > widthRatio) {
- // Height ratio is larger, scale according to it
- bmpFactoryOptions.inSampleSize = heightRatio;
- } else {
- // Width ratio is larger, scale according to it
- bmpFactoryOptions.inSampleSize = widthRatio;
- }
- }
- // Decode it for real
- bmpFactoryOptions.inJustDecodeBounds = false;
- try {
- bmp = BitmapFactory.decodeStream(
- getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- Log.v("ERROR", e.toString());
- }
- // Display it
- returnedImageView.setImageBitmap(bmp);
- }
- }
- }
2)使用MediaStore来检索图像数据
MediaStore,跟所有的content provider一样使用类似于数据库操作的方式来检索数据。从指定的Uri中选择数据记录,之后通过Cursor对象来对结果进行迭代处理。
首先需要创建一个字符串数组来表示希望返回的列类型,MediaStore中图像数据的标准列类型在MediaStore.Images.Media类中:
- String[] columns =
- {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME};
执行实际的查询操作使用Activity的managedQuery函数,第一个参数是URI,第二个参数是列名组成的字符串数组,第三个参数是WHERE语句,后面跟的参数是WHERE包含的参数,最后一个参数是ORDER BY语句:
- long oneHourAgo = System.currentTimeMillis()/1000 - (60*60);
- String[] whereValues = {"" + oneHourAgo};
- // 指定返回结果的列
- String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME, Media.DATE_ADDED};
- // 获得游标
- Cursor cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, Media.DATE_ADDED + " > ?",whereValues, Media.DATE_ADDED + " ASC");
- // 返回指定列的索引
- int displayColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
- // 移到游标的开始处
- if (cursor.moveToFirst()) {
- String displayName = cursor.getString(displayColumnIndex);
- }
完整的例子如下所示,先是layout/main.xml文件:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <ImageButton
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/ImageButton"/>
- <TextView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:id="@+id/TitleTextView"
- android:text="Image Title"/>
- </LinearLayout>
Java代码如下:
- package hust.iprai.asce1885.promedia;
- import android.app.Activity;
- import android.database.Cursor;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.os.Bundle;
- import android.provider.MediaStore;
- import android.provider.MediaStore.Images.Media;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.ImageButton;
- import android.widget.TextView;
- public class MediaStoreGallery extends Activity {
- public final static int DISPLAYWIDTH = 200;
- public final static int DISPLAYHEIGHT = 200;
- TextView titleTextView;
- ImageButton imageButton;
- Cursor cursor;
- Bitmap bmp;
- String imageFilePath;
- int fileColumn;
- int titleColumn;
- int displayColumn;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- titleTextView = (TextView) findViewById(R.id.TitleTextView);
- imageButton = (ImageButton) findViewById(R.id.ImageButton);
- String[] columns = {Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME};
- cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
- // 注意:Media.DATA是MediaStore.Images.Media.DATA的缩写
- fileColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
- titleColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.TITLE);
- displayColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME);
- if (cursor.moveToFirst()) {
- titleTextView.setText(cursor.getString(titleColumn));
- imageFilePath = cursor.getString(fileColumn);
- bmp = getBitmap(imageFilePath);
- // Display it
- imageButton.setImageBitmap(bmp);
- }
- imageButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- if (cursor.moveToNext()) {
- titleTextView.setText(cursor.getString(displayColumn));
- imageFilePath = cursor.getString(fileColumn);
- bmp = getBitmap(imageFilePath);
- imageButton.setImageBitmap(bmp);
- }
- }
- });
- }
- private Bitmap getBitmap(String imageFilePath) {
- // Load up the image's dimensions not the image itself
- BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
- bmpFactoryOptions.inJustDecodeBounds = true;
- Bitmap bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);
- int heightRatio = (int) Math.ceil(bmpFactoryOptions.outHeight/(float)DISPLAYHEIGHT);
- int widthRatio = (int) Math.ceil(bmpFactoryOptions.outWidth/(float)DISPLAYWIDTH);
- Log.v("HEIGHTRATIO", "" + heightRatio);
- Log.v("WIDTHRATIO", "" + widthRatio);
- // If both of the ratios are greater than 1, one of the sides of
- // the image is greater than the screen
- if ((heightRatio > 1) && (widthRatio > 1)) {
- if (heightRatio > widthRatio) {
- bmpFactoryOptions.inSampleSize = heightRatio;
- } else {
- bmpFactoryOptions.inSampleSize = widthRatio;
- }
- }
- // Decode it for real
- bmpFactoryOptions.inJustDecodeBounds = false;
- bmp = BitmapFactory.decodeFile(imageFilePath, bmpFactoryOptions);
- return bmp;
- }
- }
2)内部元数据
EXIF,可交换图像文件格式(Exchangeable Image File Format),是将元数据保存到图像文件里的标准格式。它的数据存储与JPEG格式是完全相同的,它就是在JPEG格式头部插入了数码照片的拍摄信息。
EXIF数据中包含很多与图像拍摄紧密相关的技术参数,例如曝光时间ExposureTime和快门速度ShutterSpeedValue等。还有一些参数是我们可以在后续进行填充或修改的,例如:
UserComment: 用户评论
ImageDescription:图像的描述
Artist:图像的创建者或者拍摄者
Copyright:版权
Software:创建图像使用的软件
Android提供了方便的接口ExifInterface来读写EXIF数据:
- ExifInterface ei = new ExifInterface(imageFilePath);
- String imageDescription = ei.getAttribute("ImageDescription");
- if (null != imageDescription) {
- Log.v("EXIF", imageDescription);
- }
保存EXIF数据到图像文件中的代码片段如下:
- ExifInterface ei = new ExifInterface(imageFilePath);
- ei.setAttribute("ImageDescription", "ASCE1885");