2017-07-02 107 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分钟。不知道还有什么可以帮助。 –