編輯:關於Android編程
QAQ學Android真的還是要在項目中獲得鍛煉,脫離實際一切都是耍流氓哼唧~!
花了一下午時間搞定了項目中要實現的獲取本地圖片縮略圖並顯示在ListView上的,並且點擊要能獲得該圖片文件路徑功能,下面先上效果圖:
作為一個新手,大概碰到這種需求的思路就是:
首先,遞歸遍歷本地所有文件,然後按文件後綴名找出所有的圖片文件,更好的方式是在媒體庫裡查找所有的圖片(系統已經幫你過濾好了所有的圖片文件直接去調用就闊以了),再通過得到的文件對象file顯示圖像。
當然這種處理結果就是大概1~2張圖片就直接OOM了。(反正我手機裡圖片都是至少上MB的...)。
所以呢,必須對圖片進行壓縮,於是我又在網上找到了一個比較好的圖片壓縮方法(這裡沒有引用轉載地址了,1是因為是昨天找到的現在已經找不到網址了,2是因為很多篇博客都是相同的方法相同的注釋,我也不知道到底誰才是原作...):
/* *//** * 根據指定的圖像路徑和大小來獲取縮略圖 * 此方法有兩點好處: * 1. 使用較小的內存空間,第一次獲取的bitmap實際上為null,只是為了讀取寬度和高度, * 第二次讀取的bitmap是根據比例壓縮過的圖像,第三次讀取的bitmap是所要的縮略圖。 * 2. 縮略圖對於原圖像來講沒有拉伸,這裡使用了2.2版本的新工具ThumbnailUtils,使 * 用這個工具生成的圖像不會被拉伸。 * @param imagePath 圖像的路徑 * @param width 指定輸出圖像的寬度 * @param height 指定輸出圖像的高度 * @return 生成的縮略圖 *//* private Bitmap getImageThumbnail(String imagePath, int width, int height) { Bitmap bitmap = null; BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 獲取這個圖片的寬和高,注意此處的bitmap為null bitmap = BitmapFactory.decodeFile(imagePath, options); options.inJustDecodeBounds = false; // 設為 false // 計算縮放比 int h = options.outHeight; int w = options.outWidth; int beWidth = w / width; int beHeight = h / height; int be = 1; if (beWidth < beHeight) { be = beWidth; } else { be = beHeight; } if (be <= 0) { be = 1; } options.inSampleSize = be; // 重新讀入圖片,讀取縮放後的bitmap,注意這次要把options.inJustDecodeBounds 設為 false bitmap = BitmapFactory.decodeFile(imagePath, options); // 利用ThumbnailUtils來創建縮略圖,這裡要指定要縮放哪個Bitmap對象 bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT); return bitmap; }*/方法很簡單,然而可以發現這個方法其實是做了這麼幾件事情:
1、根據傳入的圖片路徑構造bitmap,得到原圖的寬高.
2、計算圖片合適的縮放比.
3、利用ThumbnailUtils來創建縮略圖,然後返回這個最終縮放以後的bitmap.
盡管像方法中說的那樣:使用較小的內存空間,第一次獲取的bitmap實際上為null,只是為了讀取寬度和高度,第二次讀取的bitmap是根據比例壓縮過的圖像,第三次讀取的bitmap是所要的縮略圖。然而整個方法還是開辟了3個bitmap對象的內存區域,這還不考慮ThumbnailUtils.extractThumbnail()方法所耗費的時空間。
所以當我使用了這個方法實現了在ListView中遍歷本地圖片的時候,上下滑起來是灰常卡滴(這裡就不貼gif圖了有興趣想知道的朋友可以自己嘗試一下)
Finaly,在踏破鐵鞋無覓處之後,終於找到了最終解決方法,也是一開始忽略的方法:
原來一直不知道的Thumbnails類,才是解決問題的關鍵。
在Android系統中也有對應的thumbnails文件,下面是百度百科對它的描述:
然後在MediaStore媒體庫類中,也是有Thumbnails這麼一個內部類的:
/** * This class allows developers to query and get two kinds of thumbnails: * MINI_KIND: 512 x 384 thumbnail * MICRO_KIND: 96 x 96 thumbnail */ public static class Thumbnails implements BaseColumns { public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) { return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER); } public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind, String[] projection) { return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER); } public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind, String[] projection) { return cr.query(EXTERNAL_CONTENT_URI, projection, IMAGE_ID + " = " + origId + " AND " + KIND + " = " + kind, null, null); } /** * This method cancels the thumbnail request so clients waiting for getThumbnail will be * interrupted and return immediately. Only the original process which made the getThumbnail * requests can cancel their own requests. * * @param cr ContentResolver * @param origId original image id */ public static void cancelThumbnailRequest(ContentResolver cr, long origId) { InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, InternalThumbnails.DEFAULT_GROUP_ID); } /** * This method checks if the thumbnails of the specified image (origId) has been created. * It will be blocked until the thumbnails are generated. * * @param cr ContentResolver used to dispatch queries to MediaProvider. * @param origId Original image id associated with thumbnail of interest. * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND. * @param options this is only used for MINI_KIND when decoding the Bitmap * @return A Bitmap instance. It could be null if the original image * associated with origId doesn't exist or memory is not enough. */ public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind, BitmapFactory.Options options) { return InternalThumbnails.getThumbnail(cr, origId, InternalThumbnails.DEFAULT_GROUP_ID, kind, options, EXTERNAL_CONTENT_URI, false); } /** * This method cancels the thumbnail request so clients waiting for getThumbnail will be * interrupted and return immediately. Only the original process which made the getThumbnail * requests can cancel their own requests. * * @param cr ContentResolver * @param origId original image id * @param groupId the same groupId used in getThumbnail. */ public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) { InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId); } /** * This method checks if the thumbnails of the specified image (origId) has been created. * It will be blocked until the thumbnails are generated. * * @param cr ContentResolver used to dispatch queries to MediaProvider. * @param origId Original image id associated with thumbnail of interest. * @param groupId the id of group to which this request belongs * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND. * @param options this is only used for MINI_KIND when decoding the Bitmap * @return A Bitmap instance. It could be null if the original image * associated with origId doesn't exist or memory is not enough. */ public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, int kind, BitmapFactory.Options options) { return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options, EXTERNAL_CONTENT_URI, false); } /** * Get the content:// style URI for the image media table on the * given volume. * * @param volumeName the name of the volume to get the URI for * @return the URI to the image media table on the given volume */ public static Uri getContentUri(String volumeName) { return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + "/images/thumbnails"); } /** * The content:// style URI for the internal storage. */ public static final Uri INTERNAL_CONTENT_URI = getContentUri("internal"); /** * The content:// style URI for the "primary" external storage * volume. */ public static final Uri EXTERNAL_CONTENT_URI = getContentUri("external"); /** * The default sort order for this table */ public static final String DEFAULT_SORT_ORDER = "image_id ASC"; /** * The data stream for the thumbnail *
Type: DATA STREAM
*/ public static final String DATA = "_data"; /** * The original image for the thumbnal *Type: INTEGER (ID from Images table)
*/ public static final String IMAGE_ID = "image_id"; /** * The kind of the thumbnail *Type: INTEGER (One of the values below)
*/ public static final String KIND = "kind"; public static final int MINI_KIND = 1; public static final int FULL_SCREEN_KIND = 2; public static final int MICRO_KIND = 3; /** * The blob raw data of thumbnail *Type: DATA STREAM
*/ public static final String THUMB_DATA = "thumb_data"; /** * The width of the thumbnal *Type: INTEGER (long)
*/ public static final String WIDTH = "width"; /** * The height of the thumbnail *Type: INTEGER (long)
*/ public static final String HEIGHT = "height"; } }我們可以從中找到幾個可用於Cursor查找的重要參數:
/** * The data stream for the thumbnail *
Type: DATA STREAM
*/ public static final String DATA = "_data"; /** * The original image for the thumbnal *Type: INTEGER (ID from Images table)
*/ public static final String IMAGE_ID = "image_id";/** * The content:// style URI for the "primary" external storage * volume. */ public static final Uri EXTERNAL_CONTENT_URI = getContentUri("external");有了這三個參數,我們就可以很輕松從本地媒體庫中獲得圖片縮略圖的ID和路徑。
//先得到縮略圖的URL和對應的圖片id Cursor cursor = cr.query( Thumbnails.EXTERNAL_CONTENT_URI, new String[]{ Thumbnails.IMAGE_ID, Thumbnails.DATA }, null, null, null);
/**
* The original image for the thumbnal
*
Type: INTEGER (ID from Images table)
ID from Images table!!!這個ID是跟多媒體庫中的images表的ID相對應的,由此,我們可以通過這個id來設置cursor的查找條件,從而找出images表中對應的真正的圖片文件的路徑!
從而完美地實現了文章開頭的功能需求。
下面是完整的代碼:
獲得一個HashMap參數的ArrayList,HashMap項的鍵"thumbnail_path"對應真實圖片路徑值,鍵"image_id_path"對應縮略圖路徑值,有了這兩個路徑,想干嘛干嘛了2333
/** * 得到本地圖片文件 * @param context * @return */ public static ArrayList<HashMap<String,String>> getAllPictures(Context context) { ArrayList<HashMap<String,String>> picturemaps = new ArrayList<>(); HashMap<String,String> picturemap; ContentResolver cr = context.getContentResolver(); //先得到縮略圖的URL和對應的圖片id Cursor cursor = cr.query( Thumbnails.EXTERNAL_CONTENT_URI, new String[]{ Thumbnails.IMAGE_ID, Thumbnails.DATA }, null, null, null); if (cursor.moveToFirst()) { do { picturemap = new HashMap<>(); picturemap.put("image_id_path",cursor.getInt(0)+""); picturemap.put("thumbnail_path",cursor.getString(1)); picturemaps.add(picturemap); } while (cursor.moveToNext()); cursor.close(); } //再得到正常圖片的path for (int i = 0;i<picturemaps.size();i++) { picturemap = picturemaps.get(i); String media_id = picturemap.get("image_id_path"); cursor = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{ MediaStore.Images.Media.DATA }, MediaStore.Audio.Media._ID+"="+media_id, null, null ); if (cursor.moveToFirst()) { do { picturemap.put("image_id",cursor.getString(0)); picturemaps.set(i,picturemap); } while (cursor.moveToNext()); cursor.close(); } } return picturemaps; }
在Android的實際開發中,我們Android系統本身已經給我們提供了很豐富的UI以及各種實用的控件,例如TextView,Button,ImageView等。用這些
在定義了將要被OpenGL繪制的形狀之後,你當然想要繪制它們。使用OpenGL ES 2.0繪制圖形需要的代碼可能比你想象的要多,因為API提供了大量的圖形渲染管道控制接
Android中有個我們熟悉又陌生的對象Context(上下文),當我們啟動Activity的時候需要上下文,當我們使用dialog的時候我們需要上下文,但是上下文對象到
Intent 的 ComponentName 廣播-BroadcastReceiver ContentProvider AIDLIntent 的 ComponentNam