編輯:關於Android編程
使用MediaStore檢索圖像
Android的共享內容提供者功能很強大,利用他們我們可以非常容易的創建類型畫廊(gallery)的應用。由於內容提供者,本例中是MediaStore,可以在應用間共享,當我們創建使自己的應用顯示圖像時,不必真去創建一個相機應用並保存圖像。既然大多數應用使用缺省的MediaStore,我們可以利用這個來創建我們自己的畫廊應用。
從MediaStore選擇是非常簡單的。我們使用與創建新記錄相同的URI,來選擇它裡面的記錄。
Media.EXTERNAL_CONTENT_URI
MediaStore,實際上,所有的內容提供者,都以一種類似數據庫的方式在運作。我們從中查詢記錄,並獲得Cursor對象,我們可以用它來迭代結果。
為了查詢,我們首先需要創建我們想要返回的列的字符串數組。MediaStore中圖像的標准列定義在MediaStore.Images.Media類裡。
String[] columns = { Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME };
為執行實際查詢,我們可以用Activity的managedQuery方法。第一個參數是URI,其次是列名數組,再次是限制WHERE子句,WHERE子句的參數,最後,ORDER BY子句。
下面將選擇最後一個小時內創建的記錄,並按最早到最新進行排序。
首先,我們創建一個名為oneHourAgo的變量,其值為從1970年1月1日到一小時之前所經過的秒數。System.currentTimeMillis()返回從那一天到現在的毫秒數,除以1000我們得到秒數,減去60分*60秒,我們就得到一個小時之前的值。
long oneHourAgo = System.currentTimeMillis()/1000 - (60 * 60);然後,我們把它的值放入一個字符串數組, 該數組將作為WHERE子句的參數。
String[] whereValues = {""+oneHourAgo};
接下來我們選擇我們想要返回的列。
String[] columns = { Media.DATA, Media._ID, Media.TITLE, Media.DISPLAY_NAME, Media.DATE_ADDED };
最後我們執行查詢。WHERE子句包含了一個?號, 執行時它將由下一個參數的值來代替。如果有多個?號,則傳入的數組也必須包含多個值。這裡使用的ORDER BY子句,指定返回的數據以創建時間的升序進行排序。
cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, Media.DATE_ADDED + " > ?", whereValues, Media.DATE_ADDED + " ASC");
當然,如果你想返回所有記錄,你可以傳遞null給最後三個參數。
Cursor cursor = managedQuery(Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
返回的游標可以告訴我們所選記錄的每個列的索引。
displayColumnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
我們需要索引來從游標中檢索字段的值。首先我們調用moveToFirst方法,確保游標有效而且包含了一些查詢結果。如果游標沒有包含任何結果,這個方法會返回false。我們使用Cursor類的幾個方法中的某個來取得實際數據。方法的選擇取決於數據的類型,字符串使用getString,整數使用getInt,等等。
if (cursor.moveToFirst()) {
String displayName = cursor.getString(displayColumnIndex);
}
創建圖像浏覽應用
接下來是一個完整的示例,查詢MediaStore取得圖像,然後以幻燈片的形式一張接著一張顯示給用戶。
package com.apress.proandroidmedia.ch1.mediastoregallery; 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;
Cursor cursor; Bitmap bmp; String imageFilePath; int fileColumn; int titleColumn; int displayColumn; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); titleTextView = (TextView) this.findViewById(R.id.TitleTextView); imageButton = (ImageButton) this.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);
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)); titleTextView.setText(cursor.getString(displayColumn)); imageFilePath = cursor.getString(fileColumn); bmp = getBitmap(imageFilePath); //顯示圖像 imageButton.setImageBitmap(bmp); }
imageButton.setOnClickListener( new OnClickListener() { public void onClick(View v) { if (cursor.moveToNext()) { //titleTextView.setText(cursor.getString(titleColumn)); titleTextView.setText(cursor.getString(displayColumn)); imageFilePath = cursor.getString(fileColumn); bmp = getBitmap(imageFilePath); imageButton.setImageBitmap(bmp); } } }); }
private Bitmap getBitmap(String imageFilePath) { // 加載圖像尺寸,非圖像自身 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); // 如果兩個比值都大於1,圖像的某一邊大於屏幕 if (heightRatio > 1 && widthRatio > 1) { if (heightRatio > widthRatio) { // 高度比較大,根據它進行縮放 bmpFactoryOptions.inSampleSize = heightRatio; } else { // 寬度比較大,根據它進行縮放 bmpFactoryOptions.inSampleSize = widthRatio; } } // 真正解碼 bmpFactoryOptions.inJustDecodeBounds = false; bmp = BitmapFactory.decodeFile(imageFilePath,bmpFactoryOptions); return bmp; } }
內部元數據
一般來說,EXIF數據是非常技術導向的;標准中的大部分標簽都是關於圖像自身采集的,比如曝光時間,快門速度。然而,還是有些標簽對我們而言是有意義的,我們可以填寫或者改動。下面是其中一些:
UserComment: 用戶寫的評論
ImageDescription: 標題
Artist: 圖片的創建者或者拍攝者
Copyright: 圖片的版權所有者
Software: 用於創建圖片的軟件
幸運的是,Android給我們提供了一個很好的方法用於讀寫EXIF數據。ExifInterface是其大類。
這是如何用ExifInterface從圖像文件中讀取特定的EXIF數據:
ExifInterface ei = new ExifInterface(imageFilePath); String imageDescription = ei.getAttribute("ImageDescription"); if (imageDescription != null) { Log.v("EXIF", imageDescription); }
ExifInterface ei = new ExifInterface(imageFilePath); ei.setAttribute("ImageDescription","Something New");
ExifInterface包含一組常量,定義了圖像的典型元數據,這些數據由相機應用在拍攝圖像時加入進來。
EXIF規格的最新版本是2.3, 2010年4月發布。這裡提供在線下載:http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf.
總結
在本章中,我們研究了Android的圖像采集和儲存基礎知識。我們看到了如何在Android中使用強大的內置相機應用程序以及如何通過一個Intent,有效地發揮其功能。我們看到了相機應用提供的精巧統一的接口,如何幫助其他Android應用增加圖像采集功能。
我們還看到了處理大圖像時,需要注意內存的使用。我們學到BitmapFactory類可以幫助我們加載圖像的縮小版本以節約內存。對內存的關注提醒我們,手機不是桌面電腦,有看似無限的內存。
我們練習了Android內置圖像內容提供者,MediaStore的使用。我們學會如何用它來將圖像保存到設備上的標准位置,以及如何快速建立一個應用程序,使用它來查詢已拍攝的圖像。
最後,我們看了一下如何用EXIF標准來關聯圖像的特定元數據。EXIF具有便攜性且用於各類設備我軟件應用程序。
這給了我們一個非常不錯的起點,去探索更多我們能在Android上做的媒體有關的東西。
我對此充滿期待期待!
本文Github代碼鏈接https://github.com/AndroidMsky/AndoirdIOSPicker 先上圖吧:這是筆者最近一個項目一直再用的一個選擇器
兩個人共嘗一個痛苦只有半個痛苦,兩個人共享一個歡樂卻有兩個歡樂。 本講內容:Gallery仿圖像集浏覽 一、基本原理 在 Activity 中實現 OnGest
昨天將框架整合完成,現在我主要實現昨天的需求,實現商品的列表查詢,這時要涉及到jsp和serveilet知識,不清楚的朋友可以趕緊去補充下知識。1、商品列表的實現1.1、
在Android開發時,有時因為需求,需要跳轉到系統的一些頁面,比如從UI中跳轉到系統設置項、WIFI設置等,那要如何返回到原來的Activity中呢? 我們可以通過Wi