編輯:關於Android編程
從Android爆發以後,自定義的控件如EditTextWithDelete、ActionBar、PullToRresh逐步進入開發者的視野,想起11年的時候,基本項目中使用的UI還全都是Android提供的基礎控件,少一點的動畫+布局,下載和網絡請求都是用HttpClient,圖片加載當然也是下載後再使用,當時的程序資源可沒有現在這麼豐富,看看Github上開源的項目,現在的程序員應該感到幸福。
項目開發從程序上來講,萬古不變兩大事情,一是網絡通信框架,二是圖片加載框架,有關網絡框架上一篇已經介紹了async-http和okhttp,而其他如volly同時擁有網絡請求和圖片加載兩個框架,很多人圖省事就一次性使用了,當然facebook自己的開源框架也是寫的非常不錯,接下來再一一介紹;先貼一張11年我們自己寫的imageloader
public class ImageDownloader { private static ImageDownloader instance = null; private static File cacheDir; public static Map當年為了防止圖片爆掉出現OOM,使用了軟引用,覺得還不錯;圖片加載的基本原理就是,把url+imageview拋過來,然後開啟異步線程加載,根據獲取的byte流decode成bitmap,最後在UI線程將圖片加載到imageview上;也沒有說做本地緩存,僅做了應用緩存;沒有對圖片進行壓縮或者設置格式,使占用內存更小,展示也更合理;LruCache基本原理跟上面使用軟引用的過程差不多,只不過多限制了圖片占用內存的大小,計算圖片使用的頻率,對應用層、SD卡層均做了封裝。> bitMapCache = new HashMap >(); public static ImageDownloader getInstance() { if (instance == null) { instance = new ImageDownloader(); } return instance; } private ImageDownloader() { // Find the dir to save cached images if (android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)) cacheDir = new File( android.os.Environment.getExternalStorageDirectory(), "WholeMag"); else cacheDir = WholeMagApplication.getInstance().getCacheDir(); if (!cacheDir.exists()) cacheDir.mkdirs(); } public void download(String actName, String url, ImageView imageView) { BitmapDownloaderTask task = new BitmapDownloaderTask(imageView, actName); task.execute(url); // return task.doInBackground(url); } class BitmapDownloaderTask extends AsyncTask { // private String url; // private boolean flag; private final WeakReference imageViewReference; // 使用WeakReference解決內存問題 private String actName; public BitmapDownloaderTask(ImageView imageView, String actName) { imageViewReference = new WeakReference (imageView); this.actName = actName; } @Override protected Bitmap doInBackground(String... params) { // 實際的下載線程,內部其實是concurrent線程,所以不會阻塞 Bitmap rebmp = getLocalBitmap(params[0], actName); if (rebmp == null) rebmp = downloadBitmap(params[0], actName); if (rebmp == null) { doInBackground(params[0]); } return rebmp; } @Override protected void onPostExecute(Bitmap bitmap) { // 下載完後執行的 if (isCancelled()) { bitmap = null; } if (imageViewReference != null) { ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setDrawingCacheEnabled(true); Bitmap temp = imageView.getDrawingCache(); imageView.setDrawingCacheEnabled(false); if (temp != null) { temp.recycle(); } double widthX = (float) WholeMagDatas.getDeviceWidth() / bitmap.getWidth(); // 圖片寬度拉伸比例 int bitmapHight = bitmap.getHeight();// 圖片高度 imageView.setImageBitmap(bitmap); // 下載完設置imageview為剛才下載的bitmap對象 if(actName.equals(AppData.NEWS_DETAIL_ACT)){ FrameLayout.LayoutParams ll = new FrameLayout.LayoutParams( android.view.ViewGroup.LayoutParams.FILL_PARENT, (int) (bitmapHight * widthX), Gravity.CENTER); imageView.setLayoutParams(ll); } AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);// 創建一個AlphaAnimation對象 alphaAnimation.setDuration(500);// 設置動畫執行的時間(單位:毫秒) imageView.startAnimation(alphaAnimation); } } } } static Bitmap getLocalBitmap(String url, String actName) { if (bitMapCache.containsKey(url)) { return bitMapCache.get(url).get(); } // String tmp = url; // String first = url.substring(url.lastIndexOf("/") + 1); // tmp = tmp.substring(0, tmp.lastIndexOf("/")); // String second = tmp.substring(tmp.lastIndexOf("/") + 1); // tmp = tmp.substring(0, tmp.lastIndexOf("/")); // String third = tmp.substring(tmp.lastIndexOf("/") + 1); // String filename = third + second + first; // File f = new File(cacheDir, filename); File f = Tools.getFile(actName, url); InputStream inputStream = null; try { // decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inPreferredConfig = Bitmap.Config.RGB_565; o.inDither = false; o.inPurgeable = true; // o.inTempStorage = new byte[12 * 1024]; inputStream = new FileInputStream(f); // Bitmap bitmap = BitmapFactory.decodeFile(f.getAbsolutePath()); Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, o); bitMapCache.put(url, new SoftReference (bitmap)); return bitmap; } catch (Exception e) { } finally { if (null != inputStream) { try { inputStream.close(); } catch (Exception ex) { } } } return null; } static Bitmap downloadBitmap(String url, String actName) { final AndroidHttpClient client = AndroidHttpClient.newInstance("linux"); final HttpGet getRequest = new HttpGet(url); try { HttpResponse response = client.execute(getRequest); final int statusCode = response.getStatusLine().getStatusCode(); if (statusCode != HttpStatus.SC_OK) { // Log.e("cwjDebug", "Error " + statusCode // + " while retrieving bitmap from " + url); return null; } final HttpEntity entity = response.getEntity(); if (entity != null) { // String tmp = url; // String first = url.substring(url.lastIndexOf("/") + 1); // tmp = tmp.substring(0, tmp.lastIndexOf("/")); // String second = tmp.substring(tmp.lastIndexOf("/") + 1); // tmp = tmp.substring(0, tmp.lastIndexOf("/")); // String third = tmp.substring(tmp.lastIndexOf("/") + 1); // String filename = third + second + first; // File f = new File(cacheDir, filename); File f = Tools.getFile(actName, url); OutputStream os = new FileOutputStream(f); InputStream inputStream = null; try { inputStream = entity.getContent(); BitmapFactory.Options o = new BitmapFactory.Options(); o.inPreferredConfig = Bitmap.Config.RGB_565; o.inDither = false; o.inPurgeable = true; final Bitmap bitmap = BitmapFactory.decodeStream( inputStream, null, o); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os); bitMapCache.put(url, new SoftReference (bitmap)); return bitmap; } finally { if (inputStream != null) { inputStream.close(); } if (null != os) { os.close(); } entity.consumeContent(); } } } catch (Exception e) { getRequest.abort(); } finally { if (client != null) { client.close(); } } return null; } }
上面介紹完,相信你也對圖片加載有個大概的輪廓,我們拿開源的imageloader為例,來講講圖片加載框架的一些細節
public final class ImageLoaderConfiguration { final Resources resources;//主要給圖片設計寬高時,獲得屏幕寬高使用
final int maxImageWidthForMemoryCache;//內存中最大的圖片寬度 final int maxImageHeightForMemoryCache;//內存中最大的圖片高度
final int maxImageWidthForDiskCache;//SD卡中最大的圖片寬度 final int maxImageHeightForDiskCache;//SD卡中最大的圖片高度 final BitmapProcessor processorForDiskCache;//從SD卡獲得Bitmap的加載器 final Executor taskExecutor;//加載圖片時的執行器 final Executor taskExecutorForCachedImages;//加載緩存時的執行器 final boolean customExecutor;//是否使用默認執行器 final boolean customExecutorForCachedImages;//是否使用默認緩存執行器 final int threadPoolSize;//線程數,可以用來控制展示當前界面的item圖片 final int threadPriority;//線程的執行優先級 final QueueProcessingType tasksProcessingType;//是LILO還是LIFO,默認是前者,但一般喜歡後者 final MemoryCache memoryCache;//內存緩存對象,如不寫可用默認 final DiskCache diskCache;//SD卡緩存對象,如不寫可用默認 final ImageDownloader downloader;//圖片加載器,根據網絡(http/s)、file、content、drawable、asset來加載 final ImageDecoder decoder;//圖片解析器,根據獲取的圖片參數拿到Bitmap final DisplayImageOptions defaultDisplayImageOptions;//設置圖片加載狀態和結果,見下面源碼 final ImageDownloader networkDeniedDownloader;//不用網絡下載圖片的下載器,可理解為加載SD卡圖片的加載器 final ImageDownloader slowNetworkDownloader;//僅網絡下載圖片的下載器,支持斷點續傳
public final class DisplayImageOptions { private final int imageResOnLoading;//圖片是否加載中 private final int imageResForEmptyUri;//圖片是否來自於空url private final int imageResOnFail;//圖片是否加載失敗 private final Drawable imageOnLoading;//加載中的圖片 private final Drawable imageForEmptyUri;//空數據的圖片 private final Drawable imageOnFail;//加載失敗的圖片 private final boolean resetViewBeforeLoading;//加載完是否重置(意味著放棄之前的加載) private final boolean cacheInMemory;//是否緩存在內存中 private final boolean cacheOnDisk;//是否緩存在SD卡中 private final ImageScaleType imageScaleType;//要多大的圖片,統一設置 private final Options decodingOptions;//Bitmap的options對象 private final int delayBeforeLoading;//是否延遲加載,可用於非當前頁面圖片 private final boolean considerExifParams;//是否支持jpeg圖片的rotate和flip等方法 private final Object extraForDownloader;//額外數據 private final BitmapProcessor preProcessor;//加載不在內存中的圖片 private final BitmapProcessor postProcessor;//加載在圖片中的圖片 private final BitmapDisplayer displayer;//展示圖片 private final Handler handler;//這個就不用講了吧,跟主線程交互必不可少的工具 private final boolean isSyncLoading;//是否同步加載
補充一下,上面框架還支持給圖片設置像素點占位大小;看到這麼多功能,對於現在的項目基本滿足要求,因此就不打算換了,再看看其他幾種圖片加載框架的異同
fresco,facebook出品,最大的優勢在於可展示加載過程,即加載進度、加載前圖片、加載中圖片、加載後圖片、加載失敗圖片等,還可以設置圖片的形狀
picasso,加載更快,因為默認設置圖片格式占內存小
Glide是升級版本的piccaso,支持跟fragment和activity生命周期綁定
volly,基於老的imageloader又做了次封裝,差別不是太大,功能弱化一些
而後面這幾種框架,都是在imageloader興起之後出現的,所以也基本支持它,在我看來也僅有fresco和glide是真正寫出了跟原框架不同的東西。
什麼是SplashSplash也就是應用程序啟動之前先啟動一個畫面,上面簡單的介紹應用程序的廠商,廠商的LOGO,名稱和版本等信息,多為一張圖片,顯示幾秒鐘後會自動消息,
本文實例講述了Android實現帶磁性的懸浮窗體效果。分享給大家供大家參考,具體如下:帶磁性的懸浮窗體,類似於360綠色小人主要實現的是:1.懸浮所有窗體之上2.有吸引力
前言先看看效果怎麼樣不錯吧?別急下面我就一步一步的教你實現。用到的知識點總結:1.Canvas和pint的使用,我們用它畫點,線,字2.View的基本用法其實做這個東西還
Android屏幕適配出現的原因在我們學習如何進行屏幕適配之前,我們需要先了解下為什麼Android需要進行屏幕適配。由於Android系統的開放性,任何用戶、開發者、O
關於Android View 事件分發過程的文章網絡上可以搜到一把大,這