編輯:關於Android編程
package cn.edu.chd.datasource; /** * @author Rowand jj *提供圖片資源路徑的類 */ public class Images { public static final String[] imageThumbUrls = { "http://1.83.188.190:8080/1.jpg", ... ... "http://1.83.188.190:8080/28.jpg", "http://1.83.188.190:8080/29.bmp", }; }2.對Bitmap進行縮放的工具類:
package cn.edu.chd.utils; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; /** * @author Rowand jj * 壓縮圖片 */ public class BitmapUtils { /** * 根據資源id獲取到圖片,並進行壓縮 * @param res * @param resId * @param reqWidth * @param reqHeight * @return */ public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, opts); int inSampleSize = cacluateInSampleSize(opts, reqWidth, reqHeight); opts.inSampleSize = inSampleSize; opts.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeResource(res, resId, opts); return bitmap; } /** * 從byte數組中獲取圖片並壓縮 * @param data * @param reqWidth * @param reqHeight * @return */ public static Bitmap decodeSampledBitmapFromByteArray(byte[] data, int reqWidth, int reqHeight) { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(data, 0, data.length, opts); int inSampleSize = cacluateInSampleSize(opts, reqWidth, reqHeight); opts.inJustDecodeBounds = false; opts.inSampleSize = inSampleSize; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, opts); return bitmap; } private static int cacluateInSampleSize(BitmapFactory.Options opts, int reqWidth, int reqHeight) { if (opts == null) return 1; int inSampleSize = 1; int realWidth = opts.outWidth; int realHeight = opts.outHeight; if (realHeight > reqHeight || realWidth > reqWidth) { int heightRatio = realHeight / reqHeight; int widthRatio = realWidth / reqWidth; inSampleSize = (heightRatio > widthRatio) ? widthRatio : heightRatio; } return inSampleSize; } }3.使用lrucache對bitmap進行內存緩存的類
package cn.edu.chd.utils; import android.graphics.Bitmap; import android.support.v4.util.LruCache; import android.util.Log; /** * @author Rowand jj * *使用lrucache緩存圖片到內存,做成了單例模式 */ public class BitmapLruCacheHelper { private static final String TAG = null; private static BitmapLruCacheHelper instance = new BitmapLruCacheHelper(); private LruCache4.文件緩存的類cache = null; private BitmapLruCacheHelper() { int maxSize = (int) (Runtime.getRuntime().maxMemory()/8); cache = new LruCache (maxSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes()*value.getHeight(); } }; } /** *加入緩存 * @param key * @param value */ public void addBitmapToMemCache(String key,Bitmap value) { if(key == null || value == null) { return; } if(cache!=null && getBitmapFromMemCache(key)==null) { cache.put(key, value); Log.i(TAG,"put to lrucache success"); } } /** * 從緩存中獲取圖片 * @param key * @return */ public Bitmap getBitmapFromMemCache(String key) { if(key == null) { return null; } Bitmap bitmap = cache.get(key); Log.i(TAG,"from lrucache,bitmap="+bitmap); return bitmap; } /** * 獲取實例 * @return */ public static BitmapLruCacheHelper getInstance() { return instance; } }
package cn.edu.chd.utils; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.Comparator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.os.Environment; import android.os.StatFs; import android.util.Log; /** * @author Rowand jj * *文件緩存 */ public class FileCacheUtils { /** *圖片緩存的相對路徑 */ private static final String IMG_CACH_DIR = "/imgCache"; /** * 手機緩存目錄 */ private static String DATA_ROOT_PATH = null; /** * sd卡根目錄 */ private static String SD_ROOT_PATH = Environment.getExternalStorageDirectory().getAbsolutePath(); /** *緩存的擴展名 */ private static final String CACHE_TAIL = ".cach"; /** * 最大緩存空間,單位是mb */ private static final int CACHE_SIZE = 4; /** * sd卡內存低於此值時將會清理緩存,單位是mb */ private static final int NEED_TO_CLEAN = 10; /** * 上下文 */ private Context context; private static final String TAG = "BitmapFileCacheUtils"; public FileCacheUtils(Context context) { this.context = context; DATA_ROOT_PATH = context.getCacheDir().getAbsolutePath(); } /** * 從緩存中獲取一張圖片 */ public Bitmap getBitmapFromFile(String key) { if(key==null) { return null; } String filename = getCacheDirectory()+File.separator+convertKeyToFilename(key); File file = new File(filename); if(file.exists()) { Bitmap bitmap = BitmapFactory.decodeFile(filename); if(bitmap == null) { file.delete(); } else { updateFileModifiedTime(filename); Log.i(TAG,"get file from sdcard cache success..."); return bitmap; } } return null; } /** * 將圖片存入文件緩存 */ public void addBitmapToFile(String key,Bitmap bm) { if(bm == null || key == null) { return; } //視情況清除部分緩存 removeCache(getCacheDirectory()); String filename = convertKeyToFilename(key); File dir = new File(getCacheDirectory()); if(!dir.exists()) { dir.mkdirs(); } File file = new File(dir, filename); try { OutputStream out = new FileOutputStream(file);//這裡需要注意,如果指定目錄不存在,應該先調用mkdirs生成目錄,否則可能創建文件失敗 bm.compress(CompressFormat.JPEG,100, out); out.close(); Log.i(TAG,"add file to sdcard cache success..."); } catch (Exception e) { e.printStackTrace(); } } /** * 獲取文件緩存路徑 * @return */ private String getCacheDirectory() { String cachePath = null; if(isSdcardAvailable()) { cachePath = SD_ROOT_PATH+IMG_CACH_DIR; }else { cachePath = DATA_ROOT_PATH+IMG_CACH_DIR; } return cachePath; } /** * * 清除40%的緩存,這些緩存被刪除的優先級根據近期使用時間排列,越久沒被使用,越容易被刪除 */ private void removeCache(String dirPath) { File dir = new File(dirPath); File[] files = dir.listFiles(); if(files == null) { return; } double total_size = 0; for(File file : files) { total_size+=file.length(); } total_size = total_size/1024/1024; if(total_size > CACHE_SIZE || getSdCardFreeSpace() <= NEED_TO_CLEAN) { Log.i(TAG,"remove cache from sdcard cache..."); int removeFactor = (int) (files.length*0.4); Arrays.sort(files, new FileLastModifiedComparator()); for(int i = 0; i < removeFactor; i++) { files[i].delete(); } } } /** *獲取sd卡可用空間 */ private int getSdCardFreeSpace() { StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath()); double freespace = stat.getAvailableBlocks()*stat.getBlockSize(); return (int) (freespace/1024/1024); } /** *判斷sd卡是否可用 * @return */ private boolean isSdcardAvailable() { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } /** * 將關鍵字轉化為文件名 */ private String convertKeyToFilename(String key) { if(key == null) { return ""; } return key.hashCode()+CACHE_TAIL; } /** * 更新文件最後修改時間 */ private void updateFileModifiedTime(String path) { File file = new File(path); file.setLastModified(System.currentTimeMillis()); } private class FileLastModifiedComparator implements Comparator5.一個圖片下載器的類{ @Override public int compare(File lhs, File rhs) { if(lhs.lastModified() > rhs.lastModified()) { return 1; }else if(lhs.lastModified() == rhs.lastModified()) { return 0; }else { return -1; } } } }
package cn.edu.chd.myimageloader; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import android.content.Context; import android.graphics.Bitmap; import android.os.Handler; import android.os.Message; import cn.edu.chd.utils.BitmapLruCacheHelper; import cn.edu.chd.utils.BitmapUtils; import cn.edu.chd.utils.FileCacheUtils; /** * @author Rowand jj *下載圖片的工具類 * 通過downloadImage方法下載圖片,並將圖片保存到緩存中(使用線程池)。對下載得到的圖片交由一個回調接口OnImageDownloadListener處理 * 通過showCacheImage方法獲取緩存中的圖片 */ public class ImageDownloader { /** * 下載image的線程池 */ private ExecutorService mImageThreadPool = null; /** * 文件緩存的工具類 */ private FileCacheUtils fileCacheUtils = null; /** * 線程池中線程的數量 */ private static final int THREAD_NUM = 2; /** * 縮略圖的寬 */ private static final int REQ_WIDTH = 90; /** * 縮略圖的高 */ private static final int REQ_HEIGHT = 90; protected static final int DOWNLOAD = 1; private Context context; /** * 構造器 * @param context */ public ImageDownloader(Context context) { this.context = context; fileCacheUtils = new FileCacheUtils(context); } /** * 下載一張圖片,先從內存緩存中找,如果沒有則去文件緩存中找,如果還沒有就從網絡中下載 * @param url * @param listener * @return */ public Bitmap downloadImage(final String url,final OnImageDownloadListener listener) { final String subUrl = url.replaceAll("[^\\w]", ""); Bitmap bitmap = showCacheBitmap(subUrl); if(bitmap!=null)//緩存中找到 { return bitmap; }else//緩存中未找到,則開啟線程下載 { // new AsyncTask6.GridView的適配器: 當GridView滑動時停止下載圖片,GridView停止滑動時下載圖片。() // { // @Override // protected Bitmap doInBackground(String... params) // { // Bitmap bitmap = getImageFromUrl(url);//從網絡上下載圖片 // fileCacheUtils.addBitmapToFile(subUrl,bitmap);//加到文件緩存 // BitmapLruCacheHelper.getInstance().addBitmapToMemCache(subUrl, bitmap);//加到內存緩存 // return bitmap; // } // protected void onPostExecute(Bitmap result) // { // listener.onImageDownload(url, result); // } // }.execute(url); final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if(msg.what == DOWNLOAD) { listener.onImageDownload(url,(Bitmap)msg.obj);//對下載後的圖片的操作交由listener實現類處理 } } }; getThreadPool().execute(new Runnable()//從線程池中獲取一個線程執行下載操作並將下載後的圖片加到文件緩存和內存緩存 { @Override public void run() { Bitmap bitmap = getImageFromUrl(url);//從網絡上下載圖片 Message msg = Message.obtain(handler, DOWNLOAD, bitmap); msg.sendToTarget();//發送消息 //加到緩存中 fileCacheUtils.addBitmapToFile(subUrl,bitmap); BitmapLruCacheHelper.getInstance().addBitmapToMemCache(subUrl, bitmap); } }); } return null; } /** * 顯示緩存中的圖片 * @param url * @return */ public Bitmap showCacheBitmap(String url) { Bitmap bitmap = BitmapLruCacheHelper.getInstance().getBitmapFromMemCache(url); if(bitmap!=null)//首先從內存緩存中找 { return bitmap; }else { bitmap = fileCacheUtils.getBitmapFromFile(url); if(bitmap!=null)//在文件緩存中找到 { BitmapLruCacheHelper.getInstance().addBitmapToMemCache(url, bitmap);//加入內存緩存 return bitmap; } } return null; } /** * 獲取線程池實例 */ public ExecutorService getThreadPool() { if (mImageThreadPool == null) { synchronized (ExecutorService.class) { if (mImageThreadPool == null) { mImageThreadPool = Executors.newFixedThreadPool(THREAD_NUM); } } } return mImageThreadPool; } /** * 從url中獲取bitmap * @param url * @return */ public Bitmap getImageFromUrl(String url) { HttpURLConnection conn = null; try { URL target = new URL(url); conn = (HttpURLConnection) target.openConnection(); conn.setReadTimeout(3000); conn.setConnectTimeout(10 * 1000); conn.setDoInput(true); if (conn.getResponseCode() == 200) { InputStream is = conn.getInputStream(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); int len = 0; byte[] buf = new byte[1024]; while((len = is.read(buf))!=-1) { bout.write(buf, 0, len); } is.close(); byte[] data = bout.toByteArray(); return BitmapUtils.decodeSampledBitmapFromByteArray(data,REQ_WIDTH, REQ_HEIGHT);//返回的是壓縮後的縮略圖 } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 取消當前的任務 */ public synchronized void cancellTask() { if(mImageThreadPool != null) { mImageThreadPool.shutdownNow(); mImageThreadPool = null; } } /** *操作下載後的圖片的回調接口 */ public interface OnImageDownloadListener { void onImageDownload(String url,Bitmap bitmap); } }
package cn.edu.chd.myimageloader; import android.content.Context; import android.graphics.Bitmap; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.ImageView; import cn.edu.chd.myimageloader.ImageDownloader.OnImageDownloadListener; public class ImageAdapter extends BaseAdapter implements OnScrollListener { private GridView gridView; private Context context; private String[] imageThumUrls; private ImageDownloader mImageDownloader; private boolean isFirstEnter = true; private int mFirstVisibleItem; private int mVisibleItemCount; public ImageAdapter(Context context,String[] imageThumUrls,GridView gridView) { this.context = context; this.gridView = gridView; this.imageThumUrls = imageThumUrls; this.mImageDownloader = new ImageDownloader(context); gridView.setOnScrollListener(this); } @Override public int getCount() { return imageThumUrls.length; } @Override public Object getItem(int position) { return imageThumUrls[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView mImageView; String imageUrl = imageThumUrls[position]; if(convertView == null) { mImageView = new ImageView(context); }else { mImageView = (ImageView) convertView; } mImageView.setLayoutParams(new GridView.LayoutParams(90,90)); mImageView.setTag(imageUrl); //只顯示緩存圖片,如果緩存中沒有則設置一張默認的圖片 Bitmap bitmap = mImageDownloader.showCacheBitmap(imageUrl.replaceAll("[^\\w]","")); if(bitmap != null) { mImageView.setImageBitmap(bitmap); }else { mImageView.setImageResource(R.drawable.ic_launcher); } return mImageView; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if(scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE)//滑動停止時啟動下載圖片 { showImage(mFirstVisibleItem, mVisibleItemCount); }else { cancellTask(); } } /** * 滾動時執行此方法 * 第一次進入會調用showImage顯示圖片 * */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstVisibleItem = firstVisibleItem; mVisibleItemCount = visibleItemCount; if(isFirstEnter && visibleItemCount>0) { showImage(firstVisibleItem, visibleItemCount); isFirstEnter = false; } } /** * 顯示圖片,先從緩存中找,如果沒找到就開啟線程下載 * @param firstVisibleItem 第一個可見項的id * @param visibleItemCount 可見項的總數 */ private void showImage(int firstVisibleItem,int visibleItemCount) { for(int i = firstVisibleItem; i < firstVisibleItem+visibleItemCount;i++) { String mImageUrl = imageThumUrls[i]; final ImageView mImageView = (ImageView) gridView.findViewWithTag(mImageUrl); mImageDownloader.downloadImage(mImageUrl, new OnImageDownloadListener() { @Override public void onImageDownload(String url, Bitmap bitmap) { if(mImageView != null && bitmap!=null) { mImageView.setImageBitmap(bitmap);//下載後直接設置到view對象上 } } }); } } /** * 取消下載任務 */ public void cancellTask() { mImageDownloader.cancellTask(); } }7MainActivity
package cn.edu.chd.myimageloader; import cn.edu.chd.datasource.Images; import android.app.Activity; import android.os.Bundle; import android.widget.GridView; public class MainActivity extends Activity { private GridView gridView = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); gridView = (GridView) findViewById(R.id.gridView); gridView.setAdapter(new ImageAdapter(this, Images.imageThumbUrls, gridView)); } }8.布局:
9.權限:
大功告成~
前不久看到鴻洋大大的圓形菜單,就想開始模仿,因為實在是太酷了,然後自己根據別人(zw哥)給我講的一些思路、一些分析,就開始改造自己的圓形菜單了。文章結構:1.功能介紹以及
總結一下微信的本地圖片加載有以下幾個特點,也是提高用戶體驗的關鍵點1、縮略圖挨個加載,一個一個加載完畢,直到屏幕所有縮略圖都加載完成2、不等當前屏的所有縮略圖加載完,迅速
最近的項目,有個需求需要使用條狀圖顯示比例,並且右對齊,見下圖: 我想到了使用進度條,這樣不就不需要在代碼動態繪制條狀了,省了很多活。 那麼進度條怎樣從右向左顯示呢?
一、技術概覽1. Core Data 功能初窺對於處理諸如對象生命周期管理、對象圖管理等日常任務,Core Data框架提供了廣泛且自動化的解決方案。它有以下特性。(注: