系統相冊分析(GallerPicker)

編譯系統相冊



    這是我已經編輯事後的代碼,可直接編譯: https://github.com/sevenler/Android_GalleryPicker_Custom [項目已經私有化了,要索取代碼給我發郵件. johnnyxyzw@gmail.com]

    系統相冊會調用android的隱藏api。所以,直接編譯會報錯。須要導入系統包:framework_intermediates/classes.jar (android的框架類)  網上一搜就能搜到。把包做爲user liblary引用到項目中來,在解決一些版本兼容問題,就能夠編譯了。不過上面我編譯的代碼,把調用系統隱藏api的地方改爲了替代代碼,其餘兼容錯誤也解決了,能夠直接編譯,運行起來可能會有問題。能夠根據相應問題來解決。


1.獲取數據

    a.文件夾列表

    相冊的數據固然是媒體數據,包括圖片和視頻。相冊的文件夾列表除了實際的圖片文件夾分類,相冊還添加了相機圖片、相機視頻、全部圖片、全部視頻幾個分類。
    這些分類數據也以圖片文件夾的形式列出來。文件夾列表的數據是怎麼獲取的呢,固然,能夠本身到sdcard中去找,固然這就2了。系統將這個工做已經作了,系統會維護一個Media數據庫來存儲這些媒體數據信息。之前本身也使用過Media數據庫來查找媒體數據信息,看了相冊使用Media數據庫,瞬間感受本身的方式弱爆了。

    相冊的列表頁面是GalleryPicker,當加載列表數據的時候會執行下面這個方法: java

private void workerRun() {
	ArrayList<Item> allItems = new ArrayList<Item>();

	checkScanning();//檢查系統Media庫掃描後臺是否還在執行
	.....

	checkImageList(allItems);//獲取系統媒體數據列表,這裏就包括全部的圖片文件夾列表、相機圖片、相機視頻、全部圖片、全部視頻
	.....

	checkBucketIds(allItems);//下面是獲取sdcard文件夾列表數據 ps:我的以爲這個方法和上面的checkImageList方法的名字頗有歧義啊。根據方法名很容易理解爲checkImageList是先獲取整個列表,checkBucketIds方法是獲取列表須要的數據。被名字忽悠了。應該叫作checkInternalList 和 checkExternalList嘛。

	checkThumbBitmap(allItems);

	checkLowStorage();
}
    下面是獲取系統媒體列表數據,列表有5類,包括相機圖片、相機視頻、相機媒體(相機圖片+相機視頻)、全部圖片、全部視頻
private void checkImageList(ArrayList<Item> allItems) {
	int length = IMAGE_LIST_DATA.length;//5類列表的數據所有定義在IMAGE_LIST_DATA裏面
	IImageList[] lists = new IImageList[length];
	for (int i = 0; i < length; i++) {
		ImageListData data = IMAGE_LIST_DATA[i];
		lists[i] = createImageList(data.mInclude, data.mBucketId,
				getContentResolver());
		.....		
		Item item = new Item(data.mType, data.mBucketId, getResources()
				.getString(data.mStringId), lists[i]);

		allItems.add(item);

		final Item finalItem = item;
		mHandler.post(new Runnable() {
			public void run() {
				updateItem(finalItem);
			}
		});
	}
}
    裏面抽象定義的數據比較多,這裏介紹不可能太清楚。能夠取看看代碼,仔細研究一下。整體說來,這裏獲得相機圖片、相機視頻、相機媒體(相機圖片+相機視頻)、全部圖片、全部視頻這幾種數據的列表。列表的結構是ImageList、VideoList等,它們都是BaseImageList的子類,代碼在com.android.camera.gallery包下面。

    下面是獲取sdcard文件夾列表數據 android

private void checkBucketIds(ArrayList<Item> allItems) {
	final IImageList allImages;
	//獲取全部數據列表
	if (!mScanning && !mUnmounted) {
		allImages = ImageManager.makeImageList(getContentResolver(),
				ImageManager.DataLocation.ALL, ImageManager.INCLUDE_IMAGES
						| ImageManager.INCLUDE_VIDEOS,
				ImageManager.SORT_DESCENDING, null);
	} else {
		allImages = ImageManager.makeEmptyImageList();
	}
	.....

	//這個地方這句話,是比較屌的,直接就查到了sdcard中的文件夾列表。之前,本身寫這一塊。就很2地取Media數據庫,把全部的圖片文件路徑讀出來,在來截取生成列表。相冊是否是這樣寫的,具體,下面會介紹
	HashMap<String, String> hashMap = allImages.getBucketIds();
	allImages.close();

	for (Map.Entry<String, String> entry : hashMap.entrySet()) {
		String key = entry.getKey();
		if (!key.equals(CAMERA_BUCKET)) {
			IImageList list = createImageList(ImageManager.INCLUDE_IMAGES
					| ImageManager.INCLUDE_VIDEOS, key,
					getContentResolver());
			.....
			Item item = new Item(Item.TYPE_NORMAL_FOLDERS, key,
					entry.getValue(), list);

			allItems.add(item);

			final Item finalItem = item;
			mHandler.post(new Runnable() {
				public void run() {
					updateItem(finalItem);
				}
			});
		}
	}

	mHandler.post(new Runnable() {
		public void run() {
			checkBucketIdsFinished();
		}
	});
}

上面的所有都是招式,下面纔是內功,也就是獲取列表的核心部分,因爲相冊把這一塊作了不少抽象,所以,我把核心的代碼整理出來 ios

package com.example.demo;

import java.util.HashMap;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Images.Media;

/**
 * 獲取系統Midia數據庫數據,Midia數據庫要這麼用
 * 
 * @author johnnyxyz
 * @mail johnnyxyzw@gmail.com
 */
public class MediaListGetter {
	public static final Uri EXTERNAL_STORAGE_URI = Images.Media.EXTERNAL_CONTENT_URI;
	public static final Uri INTERNAL_STORAGE_URI = Images.Media.INTERNAL_CONTENT_URI;
	public static final Uri VIDEO_STORAGE_URI = Uri.parse("content://media/external/video/media");

	private static final String[] ACCEPTABLE_IMAGE_TYPES = new String[] { "image/jpeg",
			"image/png", "image/gif" };

	private static final String WHERE_CLAUSE = "(" + Media.MIME_TYPE + " in (?, ?, ?))";
	private static final String WHERE_CLAUSE_WITH_BUCKET_ID = WHERE_CLAUSE + " AND "
			+ Media.BUCKET_ID + " = ?";

	protected String whereClause(String bucketId) {
		return bucketId == null ? WHERE_CLAUSE : WHERE_CLAUSE_WITH_BUCKET_ID;
	}

	private String[] whereClauseArgs(String bucketId) {
		if (bucketId != null) {
			int count = ACCEPTABLE_IMAGE_TYPES.length;
			String[] result = new String[count + 1];
			System.arraycopy(ACCEPTABLE_IMAGE_TYPES, 0, result, 0, count);
			result[count] = bucketId;
			return result;
		}
		return ACCEPTABLE_IMAGE_TYPES;
	}

	public static final String CAMERA_IMAGE_BUCKET_NAME = Environment.getExternalStorageDirectory()
			.toString() + "/DCIM/Camera";
	public static final String CAMERA_IMAGE_BUCKET_ID = String.valueOf(CAMERA_IMAGE_BUCKET_NAME
			.toLowerCase().hashCode());

	public MediaListGetter(ContentResolver mContentResolver) {
		super();
		this.mContentResolver = mContentResolver;
	}

	private final ContentResolver mContentResolver;

	/**
	 *  獲取全部Camera圖片文件夾列表
	 * @return
	 */
	public HashMap<String, String> getCameraImages() {
		// 內置存儲的圖片
		Uri baseUri = INTERNAL_STORAGE_URI;
		Uri uri = baseUri.buildUpon().appendQueryParameter("distinct", "true").build();
		Cursor internal = Media.query(mContentResolver, uri, new String[] {
				Media.BUCKET_DISPLAY_NAME, Media.BUCKET_ID }, whereClause(CAMERA_IMAGE_BUCKET_ID),
				whereClauseArgs(CAMERA_IMAGE_BUCKET_ID), null);

		// 外置存儲的圖片
		baseUri = EXTERNAL_STORAGE_URI;
		uri = baseUri.buildUpon().appendQueryParameter("distinct", "true").build();
		Cursor external = Media.query(mContentResolver, uri, new String[] {
				Media.BUCKET_DISPLAY_NAME, Media.BUCKET_ID }, whereClause(CAMERA_IMAGE_BUCKET_ID),
				whereClauseArgs(CAMERA_IMAGE_BUCKET_ID), null);

		try {
			HashMap<String, String> hash = new HashMap<String, String>();
			while (internal.moveToNext()) {
				hash.put(internal.getString(1), internal.getString(0));
			}
			while (external.moveToNext()) {
				hash.put(external.getString(1), external.getString(0));
			}
			return hash;
		} finally {
			internal.close();
			external.close();
		}
	}

	/**
	 *  獲取全部Camera視頻文件夾列表
	 * @return
	 */
	public HashMap<String, String> getCameraVidios() {
		Uri baseUri = VIDEO_STORAGE_URI;
		Uri uri = baseUri.buildUpon().appendQueryParameter("distinct", "true").build();
		Cursor cursor = Media.query(mContentResolver, uri, new String[] {
				Media.BUCKET_DISPLAY_NAME, Media.BUCKET_ID },
				(CAMERA_IMAGE_BUCKET_ID != null ? Images.Media.BUCKET_ID + " = '"
						+ CAMERA_IMAGE_BUCKET_ID + "'" : null), null, null);

		try {
			HashMap<String, String> hash = new HashMap<String, String>();
			while (cursor.moveToNext()) {
				hash.put(cursor.getString(1), cursor.getString(0));
			}
			return hash;
		} finally {
			cursor.close();
		}
	};

	/**
	 *  獲取全部Camera媒體文件夾列表
	 * @return
	 */
	public void getCameraMedias() {
		// getCameraVidios + getCameraImages
	};

	/**
	 *  獲取全部圖片的文件夾列表
	 * @return
	 */
	public HashMap<String, String> getAllImages() {
		Uri baseUri = EXTERNAL_STORAGE_URI;
		Uri uri = baseUri.buildUpon().appendQueryParameter("distinct", "true").build();
		// 外置存儲camera圖片
		Cursor camera = Media.query(mContentResolver, uri, new String[] {
				Media.BUCKET_DISPLAY_NAME, Media.BUCKET_ID }, whereClause(CAMERA_IMAGE_BUCKET_ID),
				whereClauseArgs(CAMERA_IMAGE_BUCKET_ID), null);
		// 外置存儲非camera圖片
		Cursor external = Media.query(mContentResolver, uri, new String[] {
				Media.BUCKET_DISPLAY_NAME, Media.BUCKET_ID }, whereClause(null),
				whereClauseArgs(null), null);
		// 內置圖片
		baseUri = INTERNAL_STORAGE_URI;
		uri = baseUri.buildUpon().appendQueryParameter("distinct", "true").build();
		Cursor internal = Media.query(mContentResolver, uri, new String[] {
				Media.BUCKET_DISPLAY_NAME, Media.BUCKET_ID }, whereClause(null),
				whereClauseArgs(null), null);

		try {
			HashMap<String, String> hash = new HashMap<String, String>();
			while (camera.moveToNext()) {
				hash.put(camera.getString(1), camera.getString(0));
			}
			while (external.moveToNext()) {
				hash.put(external.getString(1), external.getString(0));
			}
			while (internal.moveToNext()) {
				hash.put(internal.getString(1), internal.getString(0));
			}
			return hash;
		} finally {
			camera.close();
			external.close();
			internal.close();
		}
	};

	/**
	 *  獲取全部視頻的文件夾列表
	 * @return
	 */
	public HashMap<String, String> getAllVidios() {
		Uri baseUri = VIDEO_STORAGE_URI;
		Uri uri = baseUri.buildUpon().appendQueryParameter("distinct", "true").build();
		Cursor cursor = Media.query(mContentResolver, uri, new String[] {
				Media.BUCKET_DISPLAY_NAME, Media.BUCKET_ID }, null, null, null);
		try {
			HashMap<String, String> hash = new HashMap<String, String>();
			while (cursor.moveToNext()) {
				hash.put(cursor.getString(1), cursor.getString(0));
			}
			return hash;
		} finally {
			cursor.close();
		}
	};

	/**
	 *  獲取外置存儲的全部文件夾
	 * @return
	 */
	public HashMap<String, String> getExternalFolders() {
		Uri baseUri = EXTERNAL_STORAGE_URI;
		Uri uri = baseUri.buildUpon().appendQueryParameter("distinct", "true").build();
		Cursor cursor = Media.query(mContentResolver, uri, new String[] {
				Media.BUCKET_DISPLAY_NAME, Media.BUCKET_ID }, null, null, null);
		try {
			HashMap<String, String> hash = new HashMap<String, String>();
			while (cursor.moveToNext()) {
				if (CAMERA_IMAGE_BUCKET_ID.equals(cursor.getString(1)))
					continue;
				hash.put(cursor.getString(1), cursor.getString(0));
			}
			return hash;
		} finally {
			cursor.close();
		}
	}
}

 若是想要下載完整示例代碼,可戳這裏 https://github.com/sevenler/Android_Demos.git 。這個Demos裏面有其餘示例代碼。跑起來仍是很容易找到本示例的代碼的。 git

....未完待續.... github

相關文章
相關標籤/搜索