控件一:Gallery之滑動不流暢的解決辦法,異步加載

Gallery滑動的時候之所以會卡,是因為當它滑動到中間的時候,默認會為選中狀態,那麼這個時候就要去加載圖片,如果當圖片比較大的時候,就會導致卡一下。
一、通過異步加載圖片的方式
其餘的地方不變,隻是在Adapter中使用異步加載的方式。
[java]
public class GalleryAdapter extends BaseAdapter { 
    private ImageView[] imageView;// 加載圖片的imageView數組 
    private Context context; 
    private Integer[] imagesId;// 要顯示的圖片 
 
    public GalleryAdapter(Context context) { 
        this.context = context; 
        imagesId = new Integer[] { R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, 
                R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.a, R.drawable.b, 
                R.drawable.c, R.drawable.d, R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, 
                R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.a, R.drawable.b, 
                R.drawable.c, R.drawable.d, R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d }; 
        imageView = new ImageView[imagesId.length]; 
    } 
 
    public int getCount() { 
        return imagesId.length; 
    } 
 
    public Object getItem(int position) { 
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), imagesId[position]); 
        return bitmap; 
    } 
 
    public long getItemId(int position) { 
        return position; 
    } 
 
    public View getView(int position, View convertView, ViewGroup parent) { 
        if (imageView[position] == null) 
            imageView[position] = new ImageView(context); 
        new MyTask().execute(position);//開啟異步任務加載圖片 
        imageView[position].setLayoutParams(new MyGallery.LayoutParams( 
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 
        return imageView[position]; 
    } 
 
    private class MyTask extends AsyncTask<Integer, Void, Void> { 
        private Bitmap bitmap; 
         
        private int position; 
        @Override 
        protected Void doInBackground(Integer… params) { 
                        //在這裡加載圖片 
                        position = params[0]; 
            BitmapFactory.Options options = new BitmapFactory.Options(); 
            options.inSampleSize = 2; 
            bitmap = BitmapFactory.decodeResource(context.getResources(), imagesId[params[0]], 
                    options); 
            return null; 
        } 
 
        @Override 
        protected void onPostExecute(Void result) { 
                        //因為該方法是由UI線程調用,所以在這裡設置ImageView的圖片 
                        super.onPostExecute(result); 
            imageView[position].setImageBitmap(bitmap); 
        } 
    } 

[html]
<p> 
</p><p>二、上面的方法,雖然是異步加載,可是,是先開啟加載,在顯示圖片,所以,被選中的ImageView會黑一下。而且不知道為什麼,理論上異步加載是不會卡的,可是,用這種方法加載更大一點的圖片,還是會卡一下,而且卡的很奇怪,總是在移到某一張圖片的時候,在往後面移動, 會先彈出下下一張圖片的一小部分,然後,才顯示下一張圖片。這中間會小卡一下,不明白原因啊!!所以想瞭下,采用瞭下面的方法改進。</p><p>每次緩沖3張,讓ImageView先顯示,再去異步加載圖片 
</p><p>先看Adapter的代碼:</p><p> 
</p> 

[html]
<pre> 
[java]
public class GalleryAdapter extends BaseAdapter { 
    private Context context; 
    private Integer[] imagesId;// 要顯示的圖片 www.aiwalls.com  
    private Bitmap[] nearBitmaps;// 自己所緩存的當前圖片附近的圖片,當前圖片是1,上一張是0,下一張是2 
    private int showingIndex;// 正在顯示的圖片是第幾張圖片 
 
    public GalleryAdapter(Context context, Integer[] imagesId) { 
        this.context = context; 
        this.imagesId = imagesId; 
        nearBitmaps = new Bitmap[3]; 
        // 最開始的時候,先初始化最開始的3張圖片 
        BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.inSampleSize = 2; 
        nearBitmaps[1] = BitmapFactory.decodeResource(context.getResources(), imagesId[0], options); 
        nearBitmaps[2] = BitmapFactory.decodeResource(context.getResources(), imagesId[1], options); 
    } 
 
    public int getCount() { 
        return imagesId.length; 
    } 
 
    public Object getItem(int position) { 
        return position; 
    } 
 
    public long getItemId(int position) { 
        return position; 
    } 
 
    public View getView(int position, View convertView, ViewGroup parent) { 
        if (convertView == null) { 
            convertView = new ImageView(context); 
            convertView.setLayoutParams(new MyGallery.LayoutParams(LayoutParams.WRAP_CONTENT, 
                    LayoutParams.WRAP_CONTENT)); 
        } 
        if (position == showingIndex) 
            ((ImageView)convertView).setImageBitmap(nearBitmaps[1]); 
        if (position == showingIndex – 1) {//說明是向前滑動 
            ((ImageView)convertView).setImageBitmap(nearBitmaps[0]); 
        } 
        if (position == showingIndex + 1) {//說明是向後滑動 
            ((ImageView)convertView).setImageBitmap(nearBitmaps[2]); 
        } 
        return convertView; 
    } 
 
    public Bitmap[] getNearBitmaps() { 
        return nearBitmaps; 
    } 
    public int getShowingIndex() { 
        return showingIndex; 
    } 
    public void setShowingIndex(int showingIndex) { 
        this.showingIndex = showingIndex; 
    } 

 

Activity中的代碼:
[html]
<pre name="code" class="java">public class ImageScanActivity extends Activity { 
    private MyGallery gallery; 
    private GalleryAdapter adapter; 
    //圖片數組 
    private Integer[] imagesId = new Integer[] { R.drawable.a, R.drawable.b, R.drawable.c, 
            R.drawable.d, R.drawable.a, R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.a, 
            R.drawable.b, R.drawable.c, R.drawable.d }; 
 
    private int motionStatus=0;//記錄到底是向前滑動,還是向後,-1向前,1向後 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
         
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        gallery = (MyGallery) findViewById(R.id.gallery); 
        adapter = new GalleryAdapter(this,imagesId); 
        gallery.setAdapter(adapter); 
        gallery.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 
                if(position==adapter.getShowingIndex()-1){//向前 
                    new MyTask().execute(); 
                    motionStatus=-1; 
                } 
                if(position==adapter.getShowingIndex()+1){//向後 
                    motionStatus=1; 
                    new MyTask().execute(); 
                } 
            } 
            public void onNothingSelected(AdapterView<?> parent) { 
 
            } 
        }); 
    } 
    private class MyTask extends AsyncTask<Void, Void, Void> { 
        @Override 
        protected Void doInBackground(Void… params) { 
            int showing = adapter.getShowingIndex();// 記錄當前正在顯示圖片的id 
            Bitmap[] bitmaps = adapter.getNearBitmaps();//獲得Adapter中的緩存圖片數組 
            BitmapFactory.Options options = new BitmapFactory.Options(); 
            options.inSampleSize = 2; 
            if(motionStatus==-1){//向前滑動,bitmaps[0]加載新的圖片 
                bitmaps[2]=bitmaps[1]; 
                bitmaps[1]=bitmaps[0]; 
                if(showing>=2) 
                bitmaps[0]=BitmapFactory.decodeResource(getResources(), imagesId[showing – 2], 
                        options); 
            } 
            if(motionStatus==1){//向後滑動,bitmaps[2]加載新的圖片 
                bitmaps[0]=bitmaps[1]; 
                bitmaps[1]=bitmaps[2]; 
                if(showing<=imagesId.length-3) 
                bitmaps[2]=BitmapFactory.decodeResource(getResources(), imagesId[showing + 2], 
                        options); 
            } 
            adapter.setShowingIndex(showing+motionStatus); 
            return null; 
        } 
    } 

反正,就是先緩存幾張圖片,然後,根據移動,在加載圖片,因為如果一次加載太多瞭,容易內存泄漏。
感覺這樣做,真是吃力不討好,可是又實在想不出什麼好的辦法。而且當快速滑動的時候,grallery還會黑,刷新不出圖片瞭,雖然可以解決,可是太麻煩瞭,而且又會搞的有點卡。
到底怎麼一勞永逸的解決這個卡的問題,求指教啊!!!

 

摘自 LonelyRoamer的專欄

發佈留言