2017-07-02 3 views
0

Ich versuche, eine einfache benutzerdefinierte Galerie zu implementieren, um alle Fotos von meinem Telefon mit Xamarin.Android anzuzeigen. Dafür verwende ich eine Gridview mit einem Adapter und Thumbnails mitXamarin Android benutzerdefinierte Galerie Gridview

MediaStore.Images.Thumbnails.GetThumbnail

Aber diese Methode für eine große Menge von Fotos langsam ist, also habe ich erstellen eine Aufgabe, um es asynchron zu machen. Fügen Sie auch CanceltionToken in der GetView-Methode hinzu, um mehrere gleiche Aufgaben gleichzeitig abzubrechen.

Aber etwas schief geht und meine app stürzt ohne Nachrichten oder manchmal "outofmemory exceception".

EDITED CODE

Hier mein Adapter:

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); 
     } 
    } 
} 

Die GetImageThumbnailAsync ruft Miniatur aus der GetView Position.

Und nun die ViewHolder Klasse:

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; 
} 

Und der Scroll-Ereignis-Listener:

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

Sie können versuchen, einen Scroll-Listener für Ihre 'GridView' zu verwenden, es ist oft ein OOM-Fehler beim Laden von Daten während Benutzer die Benutzeroberfläche scrollen. Versuchen Sie, Daten zu laden, wenn die Benutzeroberfläche stabil ist und wenn das Thumbnail zu diesem Zeitpunkt nicht geladen ist, verwenden Sie einen Standard, um es vorübergehend zu ersetzen. –

+0

Ich habe versucht, OnScrollListenerStateChanged hinzuzufügen, um nur Thumbnails zu erhalten, wenn der Bildlauf nicht aktiv ist (stabil). Außerdem füge ich Standard-Bitmap hinzu, wenn das Bild noch nicht geladen ist. Und schließlich eine NotifyDataSetChanged() in der OnScrollListenerStateChanged, um die Benutzeroberfläche zu aktualisieren. (Editieränderung im ersten Post hinzugefügt) – TGuerin

Antwort

1

Es Speicher für Bitmap in android begrenzt hat (etwa 8 M). Wenn Sie also zu viele Bilder geladen haben, wird die OOM-Ausnahme ausgelöst.

Neben der Verwendung des Standardbildes, um das unloaded Bild vorübergehend zu ersetzen, wie ich im Kommentar vorgeschlagen habe, müssen wir auch Bitmaps zwischenspeichern.

Zum Zwischenspeichern von Bitmaps können Sie das offizielle Dokument von Google Android überprüfen: Use a Disk Cache.

+0

Aber in meinem neuen bearbeiteten Code lade ich nicht eine riesige Menge von Bildern, sondern lade nur sichtbare Bilder in die GetView() Methode. So, jetzt, jedes Mal, wenn ich ein Bild-Thumbnail brauche ich es async neu erstellen. Nicht optimiert, aber nicht schwer für den Speicher ... – TGuerin

+1

@TGuerin, ich habe deinen neuen Code getestet, eigentlich funktioniert dein Code gut von meiner Seite, nur wenn ich für 5-6 Minuten scrolle. Nicht sicher, was noch hilfreich sein kann. –

Verwandte Themen