私は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();
}
};
'GridView'にスクロールリスナーを使うことができます。ユーザーがUIをスクロールしているときにデータをロードするときにOOMエラーが発生することがよくあります。 UIが安定しているときにデータを読み込み、サムネイルがその時点で読み込まれていない場合は、デフォルトのものを使用して一時的に置き換えます。 –
スクロールがアクティブ(安定)でない場合にのみ、サムネイルを取得するためにOnScrollListenerStateChangedを追加しようとしました。また、イメージがまだ読み込まれていないときは、デフォルトのビットマップを追加します。最後にOnScrollListenerStateChangedのNotifyDataSetChanged()を呼び出してUIを更新します。 – TGuerin