2017-07-02 109 views
0

我想實現一個簡單的自定義畫廊,以顯示我的手機使用Xamarin.Android的所有照片。 對於我使用GridView與適配器和Xamarin Android自定義畫廊gridview

MediaStore.Images.Thumbnails.GetThumbnail

創建縮略圖但這種方法對於照片數量較大慢,所以我創建一個任務,以使其異步。同時在GetView方法上添加CancellationToken以並行取消多個相同的任務。

但是出了點問題,我的應用程序崩潰沒有消息或有時「outofmemory exeception」。

編輯的代碼

這裏我適配器:

public class ImageAdapter : BaseAdapter 
{ 
    public bool IsScrolling = false; 

    private LayoutInflater mInflater; 
    private Context mContext; 
    private ICursor cursorImage; 
    private ViewHolder selectedItem; 
    private Bitmap blanckBitmap; 

    public ImageAdapter(Context context) 
    { 
     mInflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService); 
     mContext = context; 

     String[] columns = { MediaStore.Images.Media.InterfaceConsts.Id, MediaStore.Images.Media.InterfaceConsts.DateTaken }; 
     String orderBy = MediaStore.Images.Media.InterfaceConsts.DateTaken + " DESC"; 

     cursorImage = Application.Context.ContentResolver.Query(
      MediaStore.Images.Media.ExternalContentUri, 
      columns, 
      null, 
      null, 
      orderBy); 

     blanckBitmap = Bitmap.CreateBitmap(100, 100, Bitmap.Config.Argb4444); 
    } 

    public override int Count => cursorImage.Count; 

    public override Java.Lang.Object GetItem(int position) 
    { 
     return position; 
    } 

    public override long GetItemId(int position) 
    { 
     return position; 
    } 

    public override View GetView(int position, View convertView, ViewGroup parent) 
    { 
     ViewHolder holder; 
     CancellationTokenSource cts; 
     if (convertView == null) 
     { 
      holder = new ViewHolder(); 
      convertView = mInflater.Inflate(Resource.Layout.gallery_item, parent, false); 
      holder.Imageview = (ImageView)convertView.FindViewById(Resource.Id.gallery_item_thumbImage); 
     } 
     else 
     { 
      holder = (ViewHolder)convertView.Tag; 
      if (holder != null) 
      { 
       var wraper = holder.WrapperCancellation.JavaCast<Wrapper<CancellationTokenSource>>(); 
       wraper?.Data.Cancel(); 
       holder.WrapperCancellation = wraper; 
      } 
     } 
     holder?.Imageview.SetImageBitmap(blanckBitmap); // Set blanck bitmap 

     if (holder != null && !IsScrolling) 
     { 
      holder.Imageview.Id = position; 
      cts = new CancellationTokenSource(); 
      GetImageThumbnailAsync(holder.Imageview, position, cts.Token); 

      holder.WrapperCancellation = new Wrapper<CancellationTokenSource> { Data = cts }; 

      if (!holder.Imageview.HasOnClickListeners) 
      { 
       holder.Imageview.Click += (sender, args) => 
       { 
        if (selectedItem != null) 
        { 
         selectedItem.Imageview.CropToPadding = false; 
         selectedItem.Imageview.Background = null; 
        } 

        selectedItem = holder; 
        holder.Imageview.CropToPadding = true; 
        holder.Imageview.Background = mContext.GetDrawable(Resource.Drawable.image_border_selected); 
       }; 
      } 
     } 
     convertView.Tag = holder; 

     return convertView; 
    } 

    private async Task GetImageThumbnailAsync(ImageView imageView, int imgIndex, CancellationToken ct) 
    { 
     var bmp = await Task.Run(() => 
     { 
      if (ct.IsCancellationRequested) 
       return null; 

      cursorImage.MoveToPosition(imgIndex); 
      var columnIndex = cursorImage.GetColumnIndex(MediaStore.Images.Media.InterfaceConsts.Id); 
      var id = cursorImage.GetInt(columnIndex); 

      return MediaStore.Images.Thumbnails.GetThumbnail(
                Application.Context.ContentResolver, 
                id, 
                ThumbnailKind.MiniKind, 
                null); 
     }, ct); 

     if (!ct.IsCancellationRequested) 
     { 
      if (bmp != null) 
       imageView.SetImageBitmap(bmp); 
     } 
    } 
} 

GetImageThumbnailAsync從GetView位置檢索縮略圖。

而現在的ViewHolder類:

public class ViewHolder : Java.Lang.Object 
{ 
    public ImageView Imageview; 
    public int Id; 
    public Wrapper<CancellationTokenSource> WrapperCancellation; 
} 

public class Wrapper<T> : Java.Lang.Object 
{ 
    public T Data; 
} 

和滾動事件偵聽器:

imagegrid.ScrollStateChanged += (o, e) => 
     { 
      if (e.ScrollState != ScrollState.Idle) 
      { 
       imageAdapter.IsScrolling = true; 
      } 
      else 
      { 
       imageAdapter.IsScrolling = false; 
       imageAdapter.NotifyDataSetChanged(); 
      } 
     }; 
+0

您可以嘗試爲您的'GridView'使用滾動偵聽器,在用戶滾動用戶界面期間加載數據時,經常會出現OOM錯誤。嘗試在用戶界面穩定時加載數據,並且如果此時未加載縮略圖,請使用默認值暫時替換它。 –

+0

我試圖添加OnScrollListenerStateChanged只滾動縮略圖時,滾動未處於活動狀態(穩定)。此外,我還添加默認的位圖,當圖像尚未加載。最後在OnScrollListenerStateChanged中創建一個NotifyDataSetChanged()來更新UI。 (在第一篇文章中添加了編輯更改) – TGuerin

回答

1

它在android系統內存有限的Bitmap(約800萬)。因此,如果您加載了太多圖片,則會拋出OOM異常。

除了使用默認圖像臨時替換未加載的圖像,正如我在評論中所建議的,我們還需要緩存位圖。

對於緩存位圖,您可以查看Google Android的官方文檔:Use a Disk Cache

+0

但是在我新編輯的代碼中,我沒有加載大量圖像,只是在GetView()方法中加載可見圖像。所以,現在,每次我需要一個圖像縮略圖時,我都會重新創建一個異步圖像。沒有優化但對內存不重要... – TGuerin

+1

@TGuerin,我測試了你的新代碼,實際上你的代碼在我身邊工作的很好,只要我保持滾動5-6分鐘。不知道還有什麼可以幫助。 –