編輯:關於Android編程
圖像存儲和元數據
Android提供了一個標准的方式在應用程序之間分享數據。負責此功能的那些類被稱為內容提供者(content provider)。內容提供者提供了一個存儲和檢索各類數據的標准接口。
圖像的標准內容提供者(同時也是音頻和視頻的)是MediaStore。MediaStore允許將文件配置到設備上的標准位置,並提供了便利的存儲和檢索文件元數據的方法。元數據是關於數據的數據。它可以包含它所在文件本身的信息,比如文件大小和名稱。但MediaStore還可以設置各種各樣的附加信息,例如標題,描述,緯度和經度。
我們來改變我們的 SizedCameraIntent activity,使用MediaStore來存儲圖像和相關元數據,代替之前將圖像隨意存儲到SD卡上。
為圖像獲取URI
要獲得存儲圖像的標准位置,我們首先需要得到一個MediaStore的引用。為此,我們使用內容解析器(content resolver)。內容解析器是獲取,諸如MediaStore這樣內容提供者的途徑。
通過傳入一個指定的URI,內容解析器知道提供一個接口給MediaStore,作為內容提供者。
因為我們要插入一個新圖像,我們要用的方法是insert,URI是定義在android.provider.MediaStore.Images.Media類的常量,名為EXTERNAL_CONTENT_URI.這表示我們想將圖像保存到設備的主外部存儲空間上,一般而言是SD卡。如果我們想保存到設備的內部存儲空間,我們可以用INTERNAL_CONTENT_URI.不過,
一般來說,媒體內容,如圖像,音頻,視頻都相當大,你更可能用EXTERNAL_CONTENT_URI.
之前所示的insert調用返回一個URI,我們可以用來寫入圖像文件的二進制數據。在我們的例子中,如同我們在CameraActivity所做的那樣,我們只是想要把它作為激活相機應用的Intent的一個extra。
Uri imageFileUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, new ContentValues()); Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri); startActivityForResult(i, CAMERA_RESULT);
預先填入關聯的元數據
如果我們想預先填寫元數據,我們可使用put方法來添加一些數據到其中。ContentValues以名稱-值對接收數據。名稱是標准的,以常量定義在android.provider.MediaStore.Images.Media類中。(某些常量實際上是存在於android.provider.MediaStore.MediaColumns接口,Media類所實現的。)
//保存圖像的名稱和描述到ContentValues的映射表。 ContentValues contentValues = new ContentValues(3); contentValues.put(Media.DISPLAY_NAME, "This is a test title"); contentValues.put(Media.DESCRIPTION, "This is a test description"); contentValues.put(Media.MIME_TYPE, "image/jpeg"); //添加一個新紀錄,不帶位圖,但是設置了一些值。 //insert()返回新紀錄的URI。 Uri imageFileUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, contentValues);
如果你通過Log來輸出這個URI,它可能看起來是這樣:
content://media/external/images/media/16
你可能首先發現它看起來像一個普通的URL,如同你在web浏覽器所用的;只不過開頭用Content替換了http,http是網頁傳輸協議。在Android中,如果一個URI以content開頭,那麼它必是用於content provider(如MediaStore)。
檢索已存圖像
之前取得的用於存儲圖像的URI,也能作為訪問圖像的路徑。但我們不再傳遞文件的全路徑給BitmapFactory,而是通過內容解析器(content provider)為該圖像打開一個InputStream,傳遞給BitmapFactory。
Bitmap bmp = BitmapFactory.decodeStream( getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions);
如果我們想在圖像采集到MediaStore之後,給它關聯更多的元數據,可以使用內容解析器(content provider)的update方法。這跟我們之前使用的insert非常類似,除了我們是通過圖像文件的URI來訪問它。
//更新記錄的標題和描述 ContentValues contentValues = new ContentValues(3); contentValues.put(Media.DISPLAY_NAME, "This is a test title"); contentValues.put(Media.DESCRIPTION, "This is a test description"); getContentResolver().update(imageFileUri,contentValues,null,null);
下面是對之前的例子的更新,新版本將圖像存儲到MediaStore,並給我們展示了如何添加標題和描述。另外,這個版本還有幾個用戶界面元素(UI element),它們的顯示和隱藏基於用戶對程序的操作。
package com.apress.proandroidmedia.ch1.mediastorecameraintent; import java.io.FileNotFoundException; import android.app.Activity; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; 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; import android.provider.MediaStore.Images.Media; import android.content.ContentValues; public class MediaStoreCameraIntent extends Activity { final static int CAMERA_RESULT = 0; Uri imageFileUri; // 用戶界面元素,定義在/res/layout/main.xml ImageView returnedImageView; Button takePictureButton; Button saveDataButton; TextView titleTextView; TextView descriptionTextView; EditText titleEditText; EditText descriptionEditText;
我們包含了一些用戶界面元素它們已經定義在layout/main.xml中,其對象在上述的代碼中做了聲明。
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //將Content View設置為定義在res/layout/main.xml內的視圖 setContentView(R.layout.main); //取得用戶界面元素的引用 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);
// 除了takePictureButton之外,所有界面元素初始化為不可見 //View.GONE 表示不可見,而且不占空間。 returnedImageView.setVisibility(View.GONE); saveDataButton.setVisibility(View.GONE); titleTextView.setVisibility(View.GONE); descriptionTextView.setVisibility(View.GONE); titleEditText.setVisibility(View.GONE); descriptionEditText.setVisibility(View.GONE);
接著,我們將設置所有的用戶界面元素不可見,同時不在布局中占用空間。為此我們傳遞常量View.GONE給setVisibility方法。另一個常量View.INVISIBLE,可以隱藏上述元素,但是仍要在布局中占用空間。
//當點擊拍照按鈕(Take Picture Button)時 takePictureButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { //添加一項不帶位圖的新記錄 //返回新記錄的URI imageFileUri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, new ContentValues()); // 啟動相機應用 Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri); startActivityForResult(i, CAMERA_RESULT); } });
在takePictureButton的點擊監聽類OnClickListener裡,我們為內置相機創建了一個標准的Intent,然後調用startAcitivityForResult。放在這裡比直接放在onCreate,用戶體驗要稍微好一些。
saveDataButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { //更新MediaStore記錄的標題和描述 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); // 通知用戶 Toast bread = Toast.makeText(MediaStoreCameraIntent.this, "Record Updated", Toast.LENGTH_SHORT); bread.show(); //回到初始狀態,設置拍照按鈕可見 //隱藏其他的用戶界面元素 takePictureButton.setVisibility(View.VISIBLE); returnedImageView.setVisibility(View.GONE); saveDataButton.setVisibility(View.GONE); titleTextView.setVisibility(View.GONE); descriptionTextView.setVisibility(View.GONE); titleEditText.setVisibility(View.GONE); descriptionEditText.setVisibility(View.GONE); } }); }
一旦相機應用返回圖像,saveDataButton變得可見。其監聽類OnClickListener為圖像關聯元數據。它接收用戶輸入到各個EditText元素的值,創建一個ContentValues對象,用來更新MediaStore中該圖像的記錄。
protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (resultCode == RESULT_OK) { // 相機應用返回了 // 隱藏拍照按鈕 takePictureButton.setVisibility(View.GONE); // 顯示其他用戶界面元素 saveDataButton.setVisibility(View.VISIBLE); returnedImageView.setVisibility(View.VISIBLE); titleTextView.setVisibility(View.VISIBLE); descriptionTextView.setVisibility(View.VISIBLE); titleEditText.setVisibility(View.VISIBLE); descriptionEditText.setVisibility(View.VISIBLE); // 縮放圖像 int dw = 200; //使其最多200像素寬 int dh = 200; //使其最多200像素高 try { // 加載圖像的尺寸,而非圖像本身 BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options(); bmpFactoryOptions.inJustDecodeBounds = true; Bitmap bmp = BitmapFactory.decodeStream( getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions); 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); // 如果兩個比值都大於1 那麼圖像的某一邊大於屏幕 //(譯注:此處注釋不恰當,把dh和dw當作了屏幕尺寸) if (heightRatio > 1 && widthRatio > 1) { if (heightRatio > widthRatio) { // 高度比較大,根據它進行縮放 bmpFactoryOptions.inSampleSize = heightRatio; } else { // 寬度比較大,根據它進行縮放 bmpFactoryOptions.inSampleSize = widthRatio; } } // 真正解碼圖像 bmpFactoryOptions.inJustDecodeBounds = false; bmp = BitmapFactory.decodeStream( getContentResolver().openInputStream(imageFileUri), null, bmpFactoryOptions); //顯示圖像 returnedImageView.setImageBitmap(bmp); } catch (FileNotFoundException e) { Log.v("ERROR",e.toString()); } } } }
recyclerView是android5.0之後推出的一款新的View布局,功能相較於ListView有過之而無不及,相信在以後的學習和工作中都將可能會用上,這兩天自己
上一篇遺留了兩個問題。1、還是無法卸載;2、必須手動去點擊應用程序進入程序,再點擊按鈕,這顯得很麻煩。這一篇就解決上面兩個問題,做出最好的效果。首先解決無法卸載問題:在清
以前在線性代數中學習了矩陣,對矩陣的基本運算有一些了解,前段時間在使用GDI+的時候再次學習如何使用矩陣來變化圖像,看了之後在這裡總結說明。首先大家看看下面這個3 x 3
使用Eclipse開發Android已經有些年頭了,然而Android Studio(後面簡稱AS)為谷歌自己推的IDE。現在AS已經出了2.0版本,其功能的確要比Ecl