[mono-android] Custom Gallery App
Ariandle
jebusinessmail at gmail.com
Thu Jan 26 15:52:55 UTC 2012
I'm working on a project that essentially is going to be a custom gallery
app. I am having problems with out of memory errors, as the user scrolled
through the images in the gallery.
I use a camera to take the pictures:
public class GalleryMenuActivity : Activity
{
private ImageButton _takePictureButton;
private ImageGalleryAdapter _imageAdapter;
private Gallery _recentPictureGallery;
private Android.Net.Uri _currentImageUri;
private List<ImageGalleryAdapter.ImageDetails> _imageList;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Request the feature to set a custom window title
RequestWindowFeature(WindowFeatures.CustomTitle);
// Load the UI defined in Second.axml
SetContentView(Resource.Layout.GalleryMenu);
// Set a custom title for the window
Window.SetFeatureInt(WindowFeatures.CustomTitle,
Resource.Layout.WindowTitle);
//Load existing images here if they are needed between runs.
_imageAdapter = new ImageGalleryAdapter(new
List<ImageGalleryAdapter.ImageDetails>());
_takePictureButton =
FindViewById<ImageButton>(Resource.Id.TakePictureButton);
_takePictureButton.Click += OnCaptureImageClicked;
}
private void OnCaptureImageClicked(object sender, EventArgs e)
{
TakePicture();
}
private void *TakePicture()*
{
// Specify some metadata for the picture
using (var contentValues = new ContentValues())
{
contentValues.Put(MediaStore.Images.ImageColumnsConsts.Description,
"Image");
// Specify where to put the image
_currentImageUri =
ContentResolver.Insert(MediaStore.Images.Media.ExternalContentUri,
contentValues);
}
// Specify the message to send to the OS
using (var takePictureIntent = new
Intent(MediaStore.ActionImageCapture))
{
takePictureIntent.PutExtra(MediaStore.ExtraOutput,
_currentImageUri);
// Start the requested activity
StartActivityForResult(takePictureIntent, 1001);
}
}
Then I capture the activity result. I can get the physical path and the
thumbnail location here.
ImageDetails is just a small object to hold some details about the image
that gets passed to the adapter:
protected override void *OnActivityResult*(int requestCode, Result
resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 1001 && resultCode == Result.Ok)
{
var imageDetail = new
Adapters.ImageGalleryAdapter.ImageDetails();
imageDetail.ThumbnailLocation = _currentImageUri.ToString();
String[] projection = { MediaStore.MediaColumns.Data };
String selection = "";
String[] selectionArgs = null;
Android.Database.ICursor mediaCursor =
ContentResolver.Query(_currentImageUri, projection, null, null, null);
if (mediaCursor != null)
{
mediaCursor.MoveToFirst();
String photoFilePath = mediaCursor.GetString(
mediaCursor.GetColumnIndex(MediaStore.MediaColumns.Data));
imageDetail.PhysicalLocation = photoFilePath;
}
_imageAdapter.Images.Add(imageDetail);
_imageAdapter.NotifyDataSetChanged();
}
}
}
On to the adapter, and where the problem is.
public class ImageGalleryAdapter : BaseAdapter
{
private List<ImageDetails> _images;
/// <summary>
/// Initializes a new instance of the ImageGalleryAdapter
/// </summary>
///
///
public *ImageGalleryAdapter*(List<ImageDetails> imageSources)
{
_images = imageSources;
}
/// <summary>
/// Gets a specific item from the list
/// </summary>
///
/// <returns></returns>
public override Object *GetItem*(int position)
{
// return new Java.Lang.String(_images.ElementAt(position));
return position;
}
/// <summary>
/// Returns the ImageDetails, which is an extention of details about
the loaded images
/// </summary>
///
/// <returns></returns>
public ImageDetails *GetItemAtPosition*(int position)
{
return _images[position];
}
/// <summary>
/// Gets the ID of a specific item
/// </summary>
///
/// <returns></returns>
public override long *GetItemId*(int position)
{
return position;
}
/// <summary>
/// Gets the view for a specific image
/// </summary>
///
///
///
/// <returns></returns>
public override View *GetView*(int position, View convertView,
ViewGroup parent)
{
var itemView = convertView;
// Create a new item view when an existing one could not be
recycled
if (itemView == null)
{
using (var inflater =
(LayoutInflater)Application.Context.GetSystemService(Context.LayoutInflaterService))
{
itemView =
inflater.Inflate(Resource.Layout.ImageGalleryItem, null);
}
}
using (var imageView =
itemView.FindViewById<ImageView>(Resource.Id.ContentImageView))
{
using (var bitmap =
*DecodeBitmapFile*(_images.ElementAt(position).ThumbnailLocation))
{
// Attach an image to the item
imageView.SetImageBitmap(bitmap);
}
}
return itemView;
}
/// <summary>
/// Gets the number of images
/// </summary>
public override int *Count*
{
get { return _images.Count; }
}
/// <summary>
/// Gets the images that are being displayed
/// </summary>
public List<ImageDetails> Images { get { return _images; } }
/// <summary>
/// Decodes a thumbnail for the specified image
/// </summary>
///
/// <returns></returns>
private Bitmap *DecodeBitmapFile*(string fileUri)
{
// The Image ID is at the end of the image URI.
int imageId =
Int32.Parse(fileUri.Substring(fileUri.LastIndexOf('/') + 1));
// *Request a small thumbnail Here is where the out of memory
Error happens*
return
MediaStore.Images.Thumbnails.GetThumbnail(Application.Context.ContentResolver,
imageId, ThumbnailKind.MiniKind, new BitmapFactory.Options() { InSampleSize
= 1 });
}
private Drawable *CreateDrawableFromUrl*(String url)
{
return Drawable.CreateFromPath(url);
}
public struct *ImageDetails*
{
public string ImageName { get; set; }
public string ThumbnailLocation { get; set; }
public string PhysicalLocation { get; set; }
}
So the error happens when *DecodeBitmapFile* is called on the
*imageView.SetImageBitmap*, but only after the user has either (a) scrolled
back and forth many times (which fires the *GetView*), or (b) adds too many
images (which fires the *GetView*). There seems to be some threshold that
gets exceeded with the method I am using here. Is there any other way to
load dynamic images without running into this? Or am I way off base with
this approach?
The images should be fairly small, as they come from
*MediaStore.Images.Thumbnails*, and I have set the *BitmapFactory.Options()
{ InSampleSize = 1})*. I believe it has something to do with
imageView.SetImageBitmap(bitmap) being called so many times, but I don't
know if there is a way around that, as I understand it the method is called
to generate the references for the images that appear on the screen as the
user scrolls.
I have even called itemView.DestroyDrawingCache(); within the using{} for
the inflater and called imageView.DestroyDrawingCache() in the imageView
using{}.
If anyone has any suggestions I would really appreciate some help here.
These out of memory errors are really becoming a roadblock for this app of
mine.
--
View this message in context: http://mono-for-android.1047100.n5.nabble.com/Custom-Gallery-App-tp5433213p5433213.html
Sent from the Mono for Android mailing list archive at Nabble.com.
More information about the Monodroid
mailing list