日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android grideview 图片png透明,Android完美解决GridView异步加载图片和加载大量图片时出现Out Of Memory问题...

發布時間:2025/4/16 Android 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android grideview 图片png透明,Android完美解决GridView异步加载图片和加载大量图片时出现Out Of Memory问题... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

眾所周知,我們在使用GridView或者ListView時,通常會遇到兩個棘手的問題:

1.每個Item獲取的數據所用的時間太長會導致程序長時間黑屏,更甚會導致程序ANR,也就是Application No Responding

2.當每個Item中有圖片存在時,少量圖片不會出現問題,當有大量圖片存在時,就會出現Out Of Memory的錯誤,導致這個錯誤的原因是Android系統中讀取位圖Bitmap時.默認分給虛擬機中圖片的堆棧大小只有8M。

好了,話不多說,下面我們來一起解決這兩個棘手的問題。

一、解決第一個問題,這里我們采用異步加載圖片的方法,也就是先讓每個Item加載一張默認的drawable,在后臺處理獲取圖片的任務,等后臺處理完以后,提示前臺更新圖片。這里我們會遇到一個問題,就是在gridview或則listview等容器中,當用戶隨意滑動的時候,將會產生N個線程去加載圖片,這個是我們不想看到的。我們希望的是一個圖片只有一個線程去下載就行了。為了解決這個問題,我們應該做的是讓這個Item中p_w_picpathview記住它是否正在加載(或者說是下載)圖片資源。如果正在加載,或者加載完成,那么我就不應該再建立一個任務去加載圖片了。

二、第二個問題我們采用圖片緩存的方式,將已經加載完成的圖片保存到緩存中,然后通過監控gridview的滑動事件去釋放圖片,即調用bitmap.recycle()方法,從而保證不會出現Out Of Memory錯誤 。

好了,話不多說,我們一起到代碼中去尋找答案。

這個是MainActivity類:

public class MainActivity extends Activity implements OnScrollListener{

private static final String TAG = "MainActivity";

private TextView textview_show_prompt = null;

private GridView gridview_test = null;

private List mList = null;

//圖片緩存用來保存GridView中每個Item的圖片,以便釋放

public static Map gridviewBitmapCaches = new HashMap();

private MyGridViewAdapter adapter = null;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

findViews();

initData();

setAdapter();

}

private void findViews(){

textview_show_prompt = (TextView)findViewById(R.id.textview_show_prompt);

gridview_test = (GridView)findViewById(R.id.gridview_test);

}

private void initData(){

mList = new ArrayList();

String url = "/mnt/sdcard/testGridView/jay.png";//為sd卡下面創建testGridView文件夾,將圖片放入其中

//為了方便測試,我們這里只存入一張圖片,將其路徑后面添加數字進行區分,到后面要獲取圖片時候再處理該路徑。

for(int i=0;i<1000;i++){

mList.add(url+"/"+i);//區分路徑

}

}

private void setAdapter(){

adapter = new MyGridViewAdapter(this, mList);

gridview_test.setAdapter(adapter);

gridview_test.setOnScrollListener(this);

}

//釋放圖片的函數

private void recycleBitmapCaches(int fromPosition,int toPosition){

Bitmap delBitmap = null;

for(int del=fromPosition;del

delBitmap = gridviewBitmapCaches.get(mList.get(del));

if(delBitmap != null){

//如果非空則表示有緩存的bitmap,需要清理

Log.d(TAG, "release position:"+ del);

//從緩存中移除該del->bitmap的映射

gridviewBitmapCaches.remove(mList.get(del));

delBitmap.recycle();

delBitmap = null;

}

}

}

@Override

public void onScroll(AbsListView view, int firstVisibleItem,

int visibleItemCount, int totalItemCount) {

// TODO Auto-generated method stub

//注釋:firstVisibleItem為第一個可見的Item的position,從0開始,隨著拖動會改變

//visibleItemCount為當前頁面總共可見的Item的項數

//totalItemCount為當前總共已經出現的Item的項數

recycleBitmapCaches(0,firstVisibleItem);

recycleBitmapCaches(firstVisibleItem+visibleItemCount, totalItemCount);

}

@Override

public void onScrollStateChanged(AbsListView view, int scrollState) {

// TODO Auto-generated method stub

}

}

這個是MyGridViewAdapter類

public class MyGridViewAdapter extends BaseAdapter{

private Context mContext = null;

private LayoutInflater mLayoutInflater = null;

private List mList = null;

private int width = 120;//每個Item的寬度,可以根據實際情況修改

private int height = 150;//每個Item的高度,可以根據實際情況修改

public static class MyGridViewHolder{

public ImageView p_w_picpathview_thumbnail;

public TextView textview_test;

}

public MyGridViewAdapter(Context context,List list) {

// TODO Auto-generated constructor stub

this.mContext = context;

this.mList = list;

mLayoutInflater = LayoutInflater.from(context);

}

@Override

public int getCount() {

// TODO Auto-generated method stub

return mList.size();

}

@Override

public Object getItem(int arg0) {

// TODO Auto-generated method stub

return null;

}

@Override

public long getItemId(int position) {

// TODO Auto-generated method stub

return 0;

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

MyGridViewHolder viewHolder = null;

if(convertView == null){

viewHolder = new MyGridViewHolder();

convertView = mLayoutInflater.inflate(R.layout.layout_my_gridview_item, null);

viewHolder.p_w_picpathview_thumbnail = (ImageView)convertView.findViewById(R.id.p_w_picpathview_thumbnail);

viewHolder.textview_test = (TextView)convertView.findViewById(R.id.textview_test);

convertView.setTag(viewHolder);

}else{

viewHolder = (MyGridViewHolder)convertView.getTag();

}

String url = mList.get(position);

//首先我們先通過cancelPotentialLoad方法去判斷p_w_picpathview是否有線程正在為它加載圖片資源,

//如果有現在正在加載,那么判斷加載的這個圖片資源(url)是否和現在的圖片資源一樣,不一樣則取消之前的線程(之前的下載線程作廢)。

//見下面cancelPotentialLoad方法代碼

if (cancelPotentialLoad(url, viewHolder.p_w_picpathview_thumbnail)) {

AsyncLoadImageTask task = new AsyncLoadImageTask(viewHolder.p_w_picpathview_thumbnail);

LoadedDrawable loadedDrawable = new LoadedDrawable(task);

viewHolder.p_w_picpathview_thumbnail.setImageDrawable(loadedDrawable);

task.execute(position);

}

viewHolder.textview_test.setText((position+1)+"");

return convertView;

}

private Bitmap getBitmapFromUrl(String url){

Bitmap bitmap = null;

bitmap = MainActivity.gridviewBitmapCaches.get(url);

if(bitmap != null){

System.out.println(url);

return bitmap;

}

url = url.substring(0, url.lastIndexOf("/"));//處理之前的路徑區分,否則路徑不對,獲取不到圖片

//bitmap = BitmapFactory.decodeFile(url);

//這里我們不用BitmapFactory.decodeFile(url)這個方法

//用decodeFileDescriptor()方法來生成bitmap會節省內存

//查看源碼對比一下我們會發現decodeFile()方法最終是以流的方式生成bitmap

//而decodeFileDescriptor()方法是通過Native方法decodeFileDescriptor生成bitmap的

try {

FileInputStream is = new FileInputStream(url);

bitmap = BitmapFactory.decodeFileDescriptor(is.getFD());

} catch (FileNotFoundException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

bitmap = BitmapUtilities.getBitmapThumbnail(bitmap,width,height);

return bitmap;

}

//加載圖片的異步任務

private class AsyncLoadImageTask extends AsyncTask{

private String url = null;

private final WeakReference p_w_picpathViewReference;

public AsyncLoadImageTask(ImageView p_w_picpathview) {

super();

// TODO Auto-generated constructor stub

p_w_picpathViewReference = new WeakReference(p_w_picpathview);

}

@Override

protected Bitmap doInBackground(Integer... params) {

// TODO Auto-generated method stub

Bitmap bitmap = null;

this.url = mList.get(params[0]);

bitmap = getBitmapFromUrl(url);

MainActivity.gridviewBitmapCaches.put(mList.get(params[0]), bitmap);

return bitmap;

}

@Override

protected void onPostExecute(Bitmap resultBitmap) {

// TODO Auto-generated method stub

if(isCancelled()){

resultBitmap = null;

}

if(p_w_picpathViewReference != null){

ImageView p_w_picpathview = p_w_picpathViewReference.get();

AsyncLoadImageTask loadImageTask = getAsyncLoadImageTask(p_w_picpathview);

if (this == loadImageTask) {

p_w_picpathview.setImageBitmap(resultBitmap);

p_w_picpathview.setScaleType(ImageView.ScaleType.CENTER_INSIDE);

}

}

super.onPostExecute(resultBitmap);

}

}

private boolean cancelPotentialLoad(String url,ImageView p_w_picpathview){

AsyncLoadImageTask loadImageTask = getAsyncLoadImageTask(p_w_picpathview);

if (loadImageTask != null) {

String bitmapUrl = loadImageTask.url;

if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {

loadImageTask.cancel(true);

} else {

// 相同的url已經在加載中.

return false;

}

}

return true;

}

//當 loadImageTask.cancel(true)被執行的時候,則AsyncLoadImageTask 就會被取消,

//當AsyncLoadImageTask 任務執行到onPostExecute的時候,如果這個任務加載到了圖片,

//它也會把這個bitmap設為null了。

//getAsyncLoadImageTask代碼如下:

private AsyncLoadImageTask getAsyncLoadImageTask(ImageView p_w_picpathview){

if (p_w_picpathview != null) {

Drawable drawable = p_w_picpathview.getDrawable();

if (drawable instanceof LoadedDrawable) {

LoadedDrawable loadedDrawable = (LoadedDrawable)drawable;

return loadedDrawable.getLoadImageTask();

}

}

return null;

}

//該類功能為:記錄p_w_picpathview加載任務并且為p_w_picpathview設置默認的drawable

public static class LoadedDrawable extends ColorDrawable{

private final WeakReference loadImageTaskReference;

public LoadedDrawable(AsyncLoadImageTask loadImageTask) {

super(Color.TRANSPARENT);

loadImageTaskReference =

new WeakReference(loadImageTask);

}

public AsyncLoadImageTask getLoadImageTask() {

return loadImageTaskReference.get();

}

}

}

還定義了一個BitmapUtilities類來處理圖片,這里我們在p_w_picpathview顯示圖片之前就將圖片縮小,更好的節省內存

public class BitmapUtilities {

public BitmapUtilities() {

// TODO Auto-generated constructor stub

}

public static Bitmap getBitmapThumbnail(String path,int width,int height){

Bitmap bitmap = null;

//這里可以按比例縮小圖片:

/*BitmapFactory.Options opts = new BitmapFactory.Options();

opts.inSampleSize = 4;//寬和高都是原來的1/4

bitmap = BitmapFactory.decodeFile(path, opts); */

/*進一步的,

如何設置恰當的inSampleSize是解決該問題的關鍵之一。BitmapFactory.Options提供了另一個成員inJustDecodeBounds。

設置inJustDecodeBounds為true后,decodeFile并不分配空間,但可計算出原始圖片的長度和寬度,即opts.width和opts.height。

有了這兩個參數,再通過一定的算法,即可得到一個恰當的inSampleSize。*/

BitmapFactory.Options opts = new BitmapFactory.Options();

opts.inJustDecodeBounds = true;

BitmapFactory.decodeFile(path, opts);

opts.inSampleSize = Math.max((int)(opts.outHeight / (float) height), (int)(opts.outWidth / (float) width));

opts.inJustDecodeBounds = false;

bitmap = BitmapFactory.decodeFile(path, opts);

return bitmap;

}

public static Bitmap getBitmapThumbnail(Bitmap bmp,int width,int height){

Bitmap bitmap = null;

if(bmp != null ){

int bmpWidth = bmp.getWidth();

int bmpHeight = bmp.getHeight();

if(width != 0 && height !=0){

Matrix matrix = new Matrix();

float scaleWidth = ((float) width / bmpWidth);

float scaleHeight = ((float) height / bmpHeight);

matrix.postScale(scaleWidth, scaleHeight);

bitmap = Bitmap.createBitmap(bmp, 0, 0, bmpWidth, bmpHeight, matrix, true);

}else{

bitmap = bmp;

}

}

return bitmap;

}

}

這里我順便把用到的兩個XML文件代碼貼出來下:

這個是activity_main.xml文件:

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent" >

android:id="@+id/textview_show_prompt"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/textview_show_prompt"

android:textSize="24dp"

tools:context=".MainActivity" />

android:layout_below="@id/textview_show_prompt"

android:id="@+id/gridview_test"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:horizontalSpacing="10dp"

android:verticalSpacing="20dp"

android:numColumns="4"

android:gravity="center"

android:padding="10dp"

android:background="#FFFFFFFF">

這個是layout_my_gridview_item.xml文件

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="#88000000"

android:gravity="center"

>

android:id="@+id/p_w_picpathview_thumbnail"

android:layout_width="120dp"

android:layout_height="150dp"

android:padding="5dp"

android:scaleType="centerInside"

/>

android:id="@+id/textview_test"

android:layout_width="120dp"

android:layout_height="wrap_content"

android:gravity="center"

android:textSize="24dp"

android:textColor="#FFFF0000"

/>

好了,到此為止我們已經完成了任務,在這里我想說的一些心得體會:

本來三天前就打算寫這篇文章了,準備先寫好demo,然后再來發表,結果在寫demo的過程中發現了很多問題,比如AsyncTask這個類的用法,Bitmap的recycle方法的調用,以及面對嵌入式開發,怎么樣去節省內存空間的使用,從而確保不會出現Out Of Memory。大家可以去查看資料看看AsyncTask這個類的用法,至于怎么樣去節省內存使用,仁者見仁,智者見智吧。我在這里就單獨說下Bitmap的recycle方法的調用的注意事項吧。

首先,Bitmap是否有調用recycle方法的必要性,這個怎么說呢,每個人都有不同的答案。嵌入式系統總是格外注重空間的問題,不小心的話就會有OOM。但是應用層使用java的android平臺有其天然的優勢(java語言有自己的垃圾回收,android平臺上各個application有自己的process自己的空間)。

無需調用bitmap的理由有:

1. 垃圾回收會處理的;

2.當application關閉,process被殺掉,所有這個process占用的空間自然回歸系統;

bitmap的recycle函數的調用還是有必要的,理由有:

1.垃圾回收雖然好使,但是有可能的話,我們還是讓它少干點活吧。垃圾回收有很大的未來不確定性,會加重未來未知時間點的loading,若有大量bitmap需要垃圾回收處理,那必然垃圾回收需要做的次數就更多也發生地更頻繁,小心會造成ANR。但是,若是自己recycle,就可以可控制地分散處理了這些回收任務了。

2. 若是launcher那樣一直運行的application,它的process一直存在,memory問題還是多多注意下比較好

再來說下假如要調用bitmap的recycle方法,何時調用是個很重要的問題,早了就大事不好了,會有這樣的Exception:? ? java.lang.RuntimeException,Canvas: trying to use a recycled bitmap

所以,我們 怎樣才可以保證不會早了呢?

關于圖片顯示,重要的時間點:

step1: 設置進去的時間點;

Step2: 畫面畫出來的時間點;

最保險最笨的做法,在新的圖片設置進去以后再recycle掉老的圖片,這樣做的壞處在于,在某個時間段,你需要的空間是double的(新舊兩套都在);

如果你不偏向于那么做,可以考慮后面一個時間點,除了setImageBitmap以及其它代碼中顯示調用那個bitmap的時候我們會檢查bitmap,在acticvity變為visible的時候系統還是會去找之前設置進去的bitmap。所以,在UI線程里面,在一個不可能被打斷的方法里面,是先設置新的bitmap還是先recycle舊的圖片是沒有影響的。

譬如說? ???mBitmap.recycle();

mBitmap = .....? ?//設置

mImageView.setImageBitmap(mBitmap);

這樣的代碼是完全可以的。

后面這樣的做法,最重要的就是確保:在UI線程(因為設置UI顯示只能在UI主線程里)里面一個不可能被打斷的方法里面。這個是為了確保在兩者之間UI主線程不可能被打斷,不可能剛好從invisible變成visible。

所以,特別小心兩種東西:

1. 多線程(最好不要在多線程里面調用);

2. 非即時發生的方法:譬如,發intent,發notify去通知UI主線程去做UI重新刷新并不能替代mImageView.setImageBitmap(mBitmap)。完全有可能出現這種情況:你確實發了intent出去了,但是目標activity之一還沒有做UI重新設置,這個時候這個acitivity變成visible了,系統仍然試圖找舊的圖片,找不到了就會報exception了。

就說這么多吧,這里也參照一些網絡上的代碼,另外大家下載demo代碼運行的時候需要把壓縮包里面的jay.png圖片放到sd中testGridView文件夾 ,有些機器sd卡路徑可能會不同,大家只要到代碼中修改String url = "/mnt/sdcard/testGridView/jay.png"這行代碼就行。

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的android grideview 图片png透明,Android完美解决GridView异步加载图片和加载大量图片时出现Out Of Memory问题...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。