Reputation: 3378
I want to choose a picture from library or take a picture with the camera and show the result to the view (ImageView)
But according to a few posts including this one, the MvxHttpImageView I use needs a Uri to show the image (wheter it comes from file systemor camera). This implies, converting the Stream into a file and getting the Uri back.
I wrote a Picture Service that does the job:
public class PictureService : IPictureService,
IMvxServiceConsumer<IMvxPictureChooserTask>,
IMvxServiceConsumer<IMvxSimpleFileStoreService>
{
private const int MaxPixelDimension = 1024;
private const int DefaultJpegQuality = 92;
public void TakeNewPhoto(Action<string> onSuccess, Action<string> onError)
{
this.GetService<IMvxPictureChooserTask>().TakePicture(
PictureService.MaxPixelDimension,
PictureService.DefaultJpegQuality,
pictureStream =>
{
var newPictureUri = this.Save(pictureStream);
if (!string.IsNullOrWhiteSpace(newPictureUri))
onSuccess(newPictureUri);
else
onError("No picture selected");
},
() => { /* cancel is ignored */ });
}
public void SelectExistingPicture(Action<string> onSuccess, Action<string> onError)
{
this.GetService<IMvxPictureChooserTask>().ChoosePictureFromLibrary(
PictureService.MaxPixelDimension,
PictureService.DefaultJpegQuality,
pictureStream =>
{
var newPictureUri = this.Save(pictureStream);
if (!string.IsNullOrWhiteSpace(newPictureUri))
onSuccess(newPictureUri);
else
onError("No photo taken");
},
() => { /* cancel is ignored */ });
}
private string Save(Stream stream)
{
string fileName = null;
try
{
fileName = Guid.NewGuid().ToString("N");
var fileService = this.GetService<IMvxSimpleFileStoreService>();
fileService.WriteFile(fileName, stream.CopyTo);
}
catch (Exception)
{
fileName = null;
}
return fileName;
}
}
But for privacy reason, I do not want to save the picture on filesystem. The workflow is:
My question is: how can I handle the Streams containing picture data without saving on filesystem?
Or
How to use a temporary storage system that is not accessible to user (ignore "rooted" device case)?
Thanks for your help.
Upvotes: 2
Views: 2376
Reputation: 66882
You could try creating your own custom ImageView
control:
1. Use a MemoryStream
to collect the received pictureStream
into a byte[]
property on the ViewModel, e.g. MyBytes
pictureStream => {
var memoryStream = new MemoryStream();
pictureStream.CopyTo(memoryStream);
TheRawImageBytes = memoryStream.GetBuffer()
}
where TheRawImageBytes
is:
private byte[] _theRawImageBytes;
public byte[] TheRawImageBytes
{
get { return _theRawImageBytes; }
set { _theRawImageBytes = value; RaisePropertyChanged(() => TheRawImageBytes); }
}
2. Create your own MyImageView
class derived from ImageView
, add the (context, attr)
constructor, then expose a byte[]
property on the MyImageView
- when that byte[]
is set then use BitmapFactory.DecodeByteArray
and SetBitmap
to render the picture from the incoming bytes
private byte[] _rawImage;
public byte[] RawImage
{
get { return _rawImage; }
set
{
_rawImage = value;
if (_rawImage == null)
return;
var bitmap = BitmapFactory.DecodeByteArray(_rawImage, 0,_rawImage.Length);
SetImageBitmap(bitmap);
}
}
3. Use <yourapp.namespace.to.MyImageView ... />
in the axml instead of the normal <ImageView ... />
4. In the axml bind the View byte[]
property to the source ViewModel byte[]
property.
local:MvxBind="{'RawImage':{'Path':'TheRawImageBytes'}}"
5. That's it - although you might want to add some error handling and do some testing
This approach is adapted from the answer to MvvmCross Android Bind Image from byte[]
As mentioned in that question, an alternative approach would be to use a standard ImageView
with a custom binding instead.
For more on creating custom views/widgets based on standard views/widgets - including on how to replace <yourapp.namespace.to.MyImageView ... />
with an abbreviation <MyApp.MyImageView ... />
, see http://slodge.blogspot.co.uk/2012/10/creating-custom-views-is-easy-to-do.html
Upvotes: 3