您好,登錄后才能下訂單哦!
實現二級緩存加載圖片的功能,在使用DiskLruCache時,需先在工程中添加名為libcore.io的包,并將DiskLruCache.Java文件放進去。DiskLruCache直接百度下載即可。
在GridView的適配器中,為ImageView添加圖片時,先從內存緩存中加載,內存中無緩存的話則在磁盤緩存中加載,磁盤緩存也沒有的話開啟線程下載,然后將下載的圖片緩存到磁盤,內存中。下載的圖片最好先進行壓縮,文章最后給出了壓縮代碼,但本例中并未實現壓縮。
/*二級緩存實現圖片墻功能,先在內存中加載緩存,內存中無緩存的話到磁盤緩存中加載,仍然沒有的話開啟線程下載圖片,下載后緩存到磁盤中,然后緩存到內存中*/
public class ErJiHuanCun extends ArrayAdapter<String> { /** * 記錄所有正在下載或等待下載的任務。 */ private Set<BitmapWorkerTask> taskCollection; /** * 圖片緩存技術的核心類,用于緩存所有下載好的圖片,在程序內存達到設定值時會將最少最近使用的圖片移除掉。 */ private LruCache<String, Bitmap> mMemoryCache; /** * 圖片硬盤緩存核心類。 */ private DiskLruCache mDiskLruCache; /** * GridView的實例 */ private GridView mPhotoWall; /** * 記錄每個子項的高度。 */ private int mItemHeight = 0; public ErJiHuanCun(Context context, int textViewResourceId, String[] objects, GridView photoWall) { super(context, textViewResourceId, objects); mPhotoWall = photoWall; taskCollection = new HashSet<BitmapWorkerTask>(); // 獲取應用程序最大可用內存 int maxMemory = (int) Runtime.getRuntime().maxMemory(); int cacheSize = maxMemory / 8; // 設置圖片緩存大小為程序最大可用內存的1/8 mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount(); } }; try { // 獲取圖片緩存路徑 File cacheDir = getDiskCacheDir(context, "thumb"); if (!cacheDir.exists()) { cacheDir.mkdirs(); } // 創建DiskLruCache實例,初始化緩存數據 mDiskLruCache = DiskLruCache .open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024); } catch (IOException e) { e.printStackTrace(); } } @Override public View getView(int position, View convertView, ViewGroup parent) { final String url = getItem(position); View view; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null); } else { view = convertView; } final ImageView imageView = (ImageView) view.findViewById(R.id.photo); if (imageView.getLayoutParams().height != mItemHeight) { imageView.getLayoutParams().height = mItemHeight; } // 給ImageView設置一個Tag,保證異步加載圖片時不會亂序 imageView.setTag(url); imageView.setImageResource(R.drawable.ic_launcher); loadBitmaps(imageView, url); return view; } /** * 將一張圖片存儲到LruCache中。 * * @param key * LruCache的鍵,這里傳入圖片的URL地址。 * @param bitmap * LruCache的鍵,這里傳入從網絡上下載的Bitmap對象。 */ public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemoryCache(key) == null) { mMemoryCache.put(key, bitmap); } } /** * 從LruCache中獲取一張圖片,如果不存在就返回null。 * * @param key * LruCache的鍵,這里傳入圖片的URL地址。 * @return 對應傳入鍵的Bitmap對象,或者null。 */ public Bitmap getBitmapFromMemoryCache(String key) { return mMemoryCache.get(key); } /** * 加載Bitmap對象。此方法會在LruCache中檢查所有屏幕中可見的ImageView的Bitmap對象, * 如果發現任何一個ImageView的Bitmap對象不在緩存中,就會開啟異步線程去下載圖片。 */ public void loadBitmaps(ImageView imageView, String imageUrl) { try { Bitmap bitmap = getBitmapFromMemoryCache(imageUrl); if (bitmap == null) { BitmapWorkerTask task = new BitmapWorkerTask(); taskCollection.add(task); task.execute(imageUrl); } else { if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); } } } catch (Exception e) { e.printStackTrace(); } } /** * 取消所有正在下載或等待下載的任務。 */ public void cancelAllTasks() { if (taskCollection != null) { for (BitmapWorkerTask task : taskCollection) { task.cancel(false); } } } /** * 根據傳入的uniqueName獲取硬盤緩存的路徑地址。 */ public File getDiskCacheDir(Context context, String uniqueName) { String cachePath; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) { cachePath = context.getExternalCacheDir().getPath(); } else { cachePath = context.getCacheDir().getPath(); } return new File(cachePath + File.separator + uniqueName); } /** * 獲取當前應用程序的版本號。 */ public int getAppVersion(Context context) { try { PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); return info.versionCode; } catch (NameNotFoundException e) { e.printStackTrace(); } return 1; } /** * 設置item子項的高度。 */ public void setItemHeight(int height) { if (height == mItemHeight) { return; } mItemHeight = height; notifyDataSetChanged(); } /** * 使用MD5算法對傳入的key進行加密并返回。 */ public String hashKeyForDisk(String key) { String cacheKey; try { final MessageDigest mDigest = MessageDigest.getInstance("MD5"); mDigest.update(key.getBytes()); cacheKey = bytesToHexString(mDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(key.hashCode()); } return cacheKey; } /** * 將緩存記錄同步到journal文件中。 */ public void flushCache() { if (mDiskLruCache != null) { try { mDiskLruCache.flush(); } catch (IOException e) { e.printStackTrace(); } } } private String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } /** * 異步下載圖片的任務。 * * */ class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> { /** * 圖片的URL地址 */ private String imageUrl; @Override protected Bitmap doInBackground(String... params) { imageUrl = params[0]; FileDescriptor fileDescriptor = null; FileInputStream fileInputStream = null; Snapshot snapShot = null; try { // 生成圖片URL對應的key final String key = hashKeyForDisk(imageUrl); // 查找key對應的緩存 snapShot = mDiskLruCache.get(key); if (snapShot == null) { // 如果沒有找到對應的緩存,則準備從網絡上請求數據,并寫入緩存 DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) { OutputStream outputStream = editor.newOutputStream(0); if (downloadUrlToStream(imageUrl, outputStream)) { editor.commit(); } else { editor.abort(); } } // 緩存被寫入后,再次查找key對應的緩存 snapShot = mDiskLruCache.get(key); } if (snapShot != null) { fileInputStream = (FileInputStream) snapShot.getInputStream(0); fileDescriptor = fileInputStream.getFD(); } // 將緩存數據解析成Bitmap對象 Bitmap bitmap = null; if (fileDescriptor != null) { // bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor); WindowManager wm= (WindowManager)getContext().getSystemService(Context.WINDOW_SERVICE); int width=wm.getDefaultDisplay().getWidth(); bitmap= ImageResizer.decodeSampleBithmapFromFileDescriptor(fileDescriptor,width/3,width/3); } if (bitmap != null) { // 將Bitmap對象添加到內存緩存當中 addBitmapToMemoryCache(params[0], bitmap); } return bitmap; } catch (IOException e) { e.printStackTrace(); } finally { if (fileDescriptor == null && fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { } } } return null; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); // 根據Tag找到相應的ImageView控件,將下載好的圖片顯示出來。 ImageView imageView = (ImageView) mPhotoWall.findViewWithTag(imageUrl); if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); } taskCollection.remove(this); } /** * 建立HTTP請求,并獲取Bitmap對象。 * * @param imageUrl * 圖片的URL地址 * @return 解析后的Bitmap對象 */ private boolean downloadUrlToStream(String urlString, OutputStream outputStream) { HttpURLConnection urlConnection = null; BufferedOutputStream out = null; BufferedInputStream in = null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024); out = new BufferedOutputStream(outputStream, 8 * 1024); int b; while ((b = in.read()) != -1) { out.write(b); } return true; } catch (final IOException e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } } return false; } } }
MainActivity
/** * 照片墻主活動,使用GridView展示照片墻。 * * */ public class MainActivity extends Activity { /** * 用于展示照片墻的GridView */ private GridView mPhotoWall; /** * GridView的適配器 */ private ErJiHuanCun mAdapter; private int mImageThumbSize; private int mImageThumbSpacing; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //mImageThumbSize = getResources().getDimensionPixelSize( // R.dimen.image_thumbnail_size); //mImageThumbSpacing = getResources().getDimensionPixelSize( // R.dimen.image_thumbnail_spacing); mPhotoWall = (GridView) findViewById(R.id.photo_wall); mAdapter = new ErJiHuanCun(this, 0, Images.imageThumbUrls, mPhotoWall); mPhotoWall.setAdapter(mAdapter); /* mPhotoWall.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { final int numColumns = (int) Math.floor(mPhotoWall .getWidth() / (mImageThumbSize + mImageThumbSpacing)); if (numColumns > 0) { int columnWidth = (mPhotoWall.getWidth() / numColumns) - mImageThumbSpacing; mAdapter.setItemHeight(columnWidth); mPhotoWall.getViewTreeObserver() .removeGlobalOnLayoutListener(this); } } });*/ } @Override protected void onPause() { super.onPause(); //將緩存記錄同步到journal文件中 mAdapter.flushCache(); } @Override protected void onDestroy() { super.onDestroy(); // // 退出程序時結束所有的下載任務 mAdapter.cancelAllTasks(); } }
/** * 自定義正方形的ImageView * */ public class MyImageView extends ImageView { public MyImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); // TODO Auto-generated constructor stub } public MyImageView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub } public MyImageView(Context context) { super(context); // TODO Auto-generated constructor stub } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub //將高度信息改成寬度即可 super.onMeasure(widthMeasureSpec, widthMeasureSpec); } }
主Activity的layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="5dp" > <GridView android:id="@+id/photo_wall" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:horizontalSpacing="5dp" android:verticalSpacing="5dp" android:numColumns="3" android:stretchMode="columnWidth" /> </LinearLayout>
GridView中的Item ImageView
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.imageloader.MyImageView android:layout_width="match_parent" android:layout_height="0dp" android:id="@+id/photo" /> </LinearLayout>
圖片壓縮實現
public class ImageResizer { private static final String TAG="ImageResizer"; public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,int reqWidth,int reqHeight){ final BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeResource(res,resId,options); options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight); options.inJustDecodeBounds=false; return BitmapFactory.decodeResource(res, resId, options); } public static Bitmap decodeSampleBithmapFromFileDescriptor(FileDescriptor fd, int reqWidth,int reqHeight){ final BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeFileDescriptor(fd, null,options); options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight); options.inJustDecodeBounds=false; return BitmapFactory.decodeFileDescriptor(fd, null,options); } public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth,int reqHeight){ if(reqWidth==0||reqHeight==0) return 1; final int width=options.outWidth; final int height=options.outHeight; int inSampleSize=1; if(height>reqHeight||width>reqWidth ){ final int halfHeight=height/2; final int halfWidth=width/2; //盡最大限度的壓縮圖片,不能讓圖片的寬高比ImageView的寬高小,否則在將 //圖片顯示到ImageView時,圖片會放大導致圖片失真 while(halfHeight/inSampleSize>reqHeight&&halfWidth/inSampleSize>reqWidth){ inSampleSize*=2; } } return inSampleSize; } }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。