2017-07-02 20 views
0

私はXamarin.Androidを使用して私の電話のすべての写真を表示するための簡単なカスタムギャラリーを実装しようとしています。そのために 私はアダプタにGridViewのを使用してXamarin Androidカスタムギャラリーgridview

MediaStore.Images.Thumbnails.GetThumbnail

でサムネイルを作成する。しかし、この方法は、写真の大きな量のために遅いので、私が作成しましたタスクを非同期にするためのタスク。複数の同じタスクを並行してキャンセルするには、GetViewメソッドでCancellationTokenを追加します。

しかし、何かがうまくいかず、私のアプリがメッセージなしでクラッシュするか、時には "outofmemory exeception"と表示されます。

EDITED CODE

ここに私のアダプタ:

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'にスクロールリスナーを使うことができます。ユーザーがUIをスクロールしているときにデータをロードするときにOOMエラーが発生することがよくあります。 UIが安定しているときにデータを読み込み、サムネイルがその時点で読み込まれていない場合は、デフォルトのものを使用して一時的に置き換えます。 –

+0

スクロールがアクティブ(安定)でない場合にのみ、サムネイルを取得するためにOnScrollListenerStateChangedを追加しようとしました。また、イメージがまだ読み込まれていないときは、デフォルトのビットマップを追加します。最後にOnScrollListenerStateChangedのNotifyDataSetChanged()を呼び出してUIを更新します。 – TGuerin

答えて

1

それは(8M程度)アンドロイドにBitmapための限られたメモリを持っています。したがって、イメージが多すぎると、OOM例外がスローされます。

私がコメントで示唆したように、デフォルトのイメージを使用してアンロードされたイメージを一時的に置き換えるだけでなく、ビットマップもキャッシュする必要があります。

ビットマップをキャッシュするには、Google Androidの公式ドキュメント:Use a Disk Cacheをご確認ください。

+0

しかし、私の新しい編集コードでは、膨大な量の画像を読み込まず、GetView()メソッドで可視画像を読み込むだけです。だから今、私はそれが非同期を再作成する画像のサムネイルが必要なたびに。最適化されていませんが、メモリのために重くはありません... – TGuerin

+1

@TGuerin、私はあなたの新しいコードをテストしました。他に何が役立つかわからない –

関連する問題