How to load an Image from Assets in Xamarin.Forms on Android? - xamarin

I am using the following code:
var baseUrl = DependencyService.Get<IBaseUrl> ().Get ();
var backgroundImage = new Image () {
Source = FileImageSource.FromFile (
System.IO.Path.Combine (baseUrl, "Content", "images", "background-2.jpg")
)
};
Where the DependencyServices for iOS and Androids are as below:
// iOS
[assembly: Xamarin.Forms.Dependency (typeof (BaseUrl_iOS))]
namespace TuneProtectApp.iOS
{
public class BaseUrl_iOS : IBaseUrl
{
public BaseUrl_iOS () { }
public string Get ()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
// Android
[assembly: Xamarin.Forms.Dependency (typeof (BaseUrl_Droid))]
namespace TuneProtectApp.Droid
{
public class BaseUrl_Droid : IBaseUrl
{
public BaseUrl_Droid () {}
public string Get ()
{
return "file:///android_asset/";
}
}
}
The backgroundImage loads fine on iOS but not on Android. How to load an Image from Assets in Xamarin.Forms on Android?

In my Xamarin.forms (shared) app I have a registration-page, where the user also have to select an image for his avatar. Based on the sex of the user, I show a male or a female symbol-image as default (the user then can select another, if he want to do).
I have implemented it as follows:
First created a sub-directory \Embedded for all projects (iOS, Android and WP) (directly in the project-root of each project-type).
Then added the two .jpg’s to the new directories in all projects.
In my app I have a global variable (GV.cEmbeddedAblage)
This is filled in startup-code:
string cNameSpace = "";
switch (Device.OS)
{
case TargetPlatform.WinPhone:
cNameSpace = "MatrixGuide.WinPhone";
break;
case TargetPlatform.iOS:
cNameSpace = "MatrixGuide.iOS";
break;
case TargetPlatform.Android:
cNameSpace = "MatrixGuide.Droid";
break;
//
}
GV.cEmbeddedAblage = cNameSpace + ".Embedded.";
Further, I create a global byte-array for the images (example to male):
static Byte[] _SymbolMann;
public static Byte[] ByteSymbolMann
{
get { return _SymbolMann; }
set { _SymbolMann = value; }
}
I then easily can access the images from shared code (on the registration-page) with (e.g.):
Generate the path, load image in byte-array (if not already loaded):
string cFilename = "";
if (GV.ByteSymbolMann == null) // not yet loaded - only load one time
{
cFilename = GV.cEmbeddedAblage + "SymbolMann.jpg";
var assembly = this.GetType().GetTypeInfo().Assembly;
byte[] buffer;
using (Stream s = assembly.GetManifestResourceStream(cFilename))
{
long length = s.Length;
buffer = new byte[length];
s.Read(buffer, 0, (int)length);
GV.ByteSymbolMann = buffer;
}
}
Fill another byte.array (with selected (loaded) male- / female- image):
AvatarErfassung = GV.ByteSymbolMann;
create the image on the page:
var Avatar = new Image { HeightRequest = 70, WidthRequest = 70, HorizontalOptions = LayoutOptions.Start };
Overtake the selected image as Source to the Image:
Avatar.Source = ImageSource.FromStream(() => new MemoryStream(AvatarErfassung));
You should be able to do it similar...

Related

Change DisplayImage while playing Mp3 file using CrossMediaManager in Xamarin forms IOS

I am trying to change the Display Image while playing mp3 file using CrossMediaManager, but It seems it doesn't change the image. It does change the title, id and so on, but I don't know why it can't change the display image.
async Task ExecutePlayChapterCommand()
{
if (IsBusy) return;
IsBusy = true;
try
{
await Task.Delay(TimeSpan.FromMilliseconds(10));
var reciter = Reciters.FirstOrDefault();
Device.BeginInvokeOnMainThread(async() => {
var mediaItem = await CrossMediaManager.Current.Play(reciter.DownloadURL);
CrossMediaManager.Current.Queue.Current.IsMetadataExtracted = false;
mediaItem.Title = reciter.Name;
var image = new Image() { Source = ImageSource.FromUri(new Uri(reciter.ImagePath)) };
CrossMediaManager.Current.Queue.Current.Title = string.Format("{0} - {1} ({2})", "1", "ChName", "English");
CrossMediaManager.Current.Queue.Current.Album = reciter.Name;
CrossMediaManager.Current.Queue.Current.Artist = reciter.Name;
CrossMediaManager.Current.Queue.Current.AlbumImage = image;
CrossMediaManager.Current.Queue.Current.AlbumImageUri = reciter.ImagePath;
CrossMediaManager.Current.Queue.Current.DisplayImage = image;
CrossMediaManager.Current.Queue.Current.DisplayImageUri = reciter.ImagePath;
CrossMediaManager.Current.Queue.Current.Image = image;
CrossMediaManager.Current.Queue.Current.ImageUri = reciter.ImagePath;
CrossMediaManager.Current.Notification.UpdateNotification();
});
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally { IsBusy = false; }
}
}
A sample project is uploaded in here
This answer might be a little bit late, but it worked for me perfectly.
First, the "Image, DisplayImage" properties of the media items are objects, that contain native representations of the cover image (UIImage and Bitmap) in the platform specific projects, so do not assign them any value.
Create your media item like this, without assigning the properties mentioned above.
new MediaItem(chapter.MediaUrl)
{
ImageUri = item.ImageUrl,
DisplayImageUri = item.ImageUrl,
AlbumImageUri = item.ImageUrl,
DisplayTitle = item.Title,
Title = item.Title,
Artist = item.Author,
Author = item.Author,
};
Then, on iOS, after setting the Queue items of the media items, in the platform specific project, create a method that takes the media item. This method will be called from the shared project.
Here is the method.
public void SetMediaImages(IMediaManager mediaManager)
{
if (mediaManager.Queue != null && mediaManager.Queue.Any())
{
foreach (var mediaItem in mediaManager.Queue)
{
if (!string.IsNullOrEmpty(mediaItem.ImageUri))
{
mediaItem.Image = UIImage.LoadFromData(NSData.FromUrl(new NSUrl(mediaItem.ImageUri)));
mediaItem.AlbumImage = mediaItem.Image;
}
}
}
}
Call this method in your shared project using Xamarin.Form's dependency service.

Xamarin Forms save image from an url into device's gallery

I am working on Xamarin Forms (with iOS and Android). What I want to do is to allow users to download image from an url by using DependencyService. I tried run in IOS emulator and the image did save into the emulator but does not show up in the gallery.
Appreciate help in that and following is my code:
In ViewModel:
public void DownloadImage()
{
IFileService fileSvc = DependencyService.Get<IFileService>();
WebClient wc = new WebClient();
byte[] bytes = wc.DownloadData(ImgUrl);
Stream stream = new MemoryStream(bytes);
fileSvc.SavePicture(DateTime.Now.ToString(), stream, "temp");
}
In Xamarin.iOS
public void SavePicture(string name, Stream data, string location = "temp")
{
var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string imageFilename = Path.Combine(documentsPath, "Images", location);
Directory.CreateDirectory(imageFilename);
string filePath = Path.Combine(documentsPath, name);
byte[] bArray = new byte[data.Length];
using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate))
{
using (data)
{
data.Read(bArray, 0, (int)data.Length);
}
int length = bArray.Length;
fs.Write(bArray, 0, length);
}
}
In Xamarin.Droid
public void SavePicture(string name, Stream data, string location = "temp")
{
var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
documentsPath = Path.Combine(documentsPath, "Images", location);
Directory.CreateDirectory(documentsPath);
string filePath = Path.Combine(documentsPath, name);
byte[] bArray = new byte[data.Length];
using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate))
{
using (data)
{
data.Read(bArray, 0, (int)data.Length);
}
int length = bArray.Length;
fs.Write(bArray, 0, length);
}
}
If you Want to save image into Gallery, please follow the code below.
Firstly, create Create the IMediaService Interface in PCL.
public interface IMediaService
{
void SaveImageFromByte(byte[] imageByte,string filename);
}
Then implement this interface in Platform-specific
Xamarin.Android Project
public class MediaService : IMediaService
{
Context CurrentContext => CrossCurrentActivity.Current.Activity;
public void SaveImageFromByte(byte[] imageByte, string filename)
{
try
{
Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
string path = System.IO.Path.Combine(storagePath.ToString(), filename);
System.IO.File.WriteAllBytes(path, imageByte);
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
CurrentContext.SendBroadcast(mediaScanIntent);
}
catch (Exception ex)
{
}
}
}
implement this interface in Platform-specific
Xamarin.iOS Project
public class MediaService : IMediaService
{
public void SaveImageFromByte(byte[] imageByte,string fileName)
{
var imageData = new UIImage(NSData.FromArray(imageByte));
imageData.SaveToPhotosAlbum((image, error) =>
{
//you can retrieve the saved UI Image as well if needed using
//var i = image as UIImage;
if (error != null)
{
Console.WriteLine(error.ToString());
}
});
}
}
For accessing the CurrentContext Install the NuGet Package (Plugin.CurrentActivity) from NuGet Package Manager, also check for the external storage permission.

How to get Image from directory path in xamarin forms

I want to retrieve image which i have it's path in phone
filepath = "/storage/emulated/0/Android/data/com.CommunicatorEye/files/Pictures/EmployeesCards/IMG_20190131_143513.jpg";
var image = DependencyService.Get<IDependency().RetriveImageFromLocation(filepath);
IDependency.cs
public interface IDependency
{
Task<Image> RetriveImageFromLocation(string location);
}
Android
DependencyImplementation.cs
public async Task<Image> RetriveImageFromLocation(string location)
{
Image image = new Image();
var memoryStream = new MemoryStream();
using (var source = System.IO.File.OpenRead(location))
{
await source.CopyToAsync(memoryStream);
}
image.Source = ImageSource.FromStream(() => memoryStream);
return image;
}
but it doesn't work for me , any sample ?
If that file is within your app's sandbox, there is no reason to use DI/DependencyService/etc... to obtain a stream to populate an ImageSource and then add that to an Image.
Use an FileImageSource (static ImageSource.FromFile) and supply it the path:
var image = new Image
{
Source = ImageSource.FromFile(filePath)
};
This is how you can get path of resources. keys should be declared in App.xaml file
public static String GetImagePath(string AppResourceName)
{
return (Application.Current.Resources[AppResourceName] as FileImageSource).File;
}
public static Color GetColor(string AppResourceName)
{
return (Color)Application.Current.Resources[AppResourceName];
}

How to select multiple picture from gallery using GMImagePicker in xamarin IOS?

I am following this blog for selecting multiple pictures from the gallery. For IOS I am Using GMImagePicker for selecting multiple pictures from the gallery.(In the blog suggesting elcimagepicker, but that is not available in Nuget Store now)
I go through the GMImagePicker usage part but didn't find how to add the selected images to List and pass that value in MessagingCenter(like the android implementation). In that usage part only telling about the picker settings. Anyone please give me any sample code for doing this feature?
Hi Lucas Zhang - MSFT, I tried your code but one question. Here you are passing only one file path through the messagecenter, so should I use a List for sending multiple file paths?
I am passing the picture paths as a string List from android. Please have a look at the android part code added below. Should I do like this in IOS?
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (resultCode == Result.Ok)
{
List<string> images = new List<string>();
if (data != null)
{
ClipData clipData = data.ClipData;
if (clipData != null)
{
for (int i = 0; i < clipData.ItemCount; i++)
{
ClipData.Item item = clipData.GetItemAt(i);
Android.Net.Uri uri = item.Uri;
var path = GetRealPathFromURI(uri);
if (path != null)
{
//Rotate Image
var imageRotated = ImageHelpers.RotateImage(path);
var newPath = ImageHelpers.SaveFile("TmpPictures", imageRotated, System.DateTime.Now.ToString("yyyyMMddHHmmssfff"));
images.Add(newPath);
}
}
}
else
{
Android.Net.Uri uri = data.Data;
var path = GetRealPathFromURI(uri);
if (path != null)
{
//Rotate Image
var imageRotated = ImageHelpers.RotateImage(path);
var newPath = ImageHelpers.SaveFile("TmpPictures", imageRotated, System.DateTime.Now.ToString("yyyyMMddHHmmssfff"));
images.Add(newPath);
}
}
MessagingCenter.Send<App, List<string>>((App)Xamarin.Forms.Application.Current, "ImagesSelected", images);
}
}
}
Also, I am getting an error, screenshot adding below:
GMImagePicker will return a list contains PHAsset .So you could firstly get the filePath of the images and then pass them to forms by using MessagingCenter and DependencyService.Refer the following code.
in Forms, create an interface
using System;
namespace app1
{
public interface ISelectMultiImage
{
void SelectedImage();
}
}
in iOS project
using System;
using Xamarin.Forms;
using UIKit;
using GMImagePicker;
using Photos;
using Foundation;
[assembly:Dependency(typeof(SelectMultiImageImplementation))]
namespace xxx.iOS
{
public class SelectMultiImageImplementation:ISelectMultiImage
{
public SelectMultiImageImplementation()
{
}
string Save(UIImage image, string name)
{
var documentsDirectory = Environment.GetFolderPath
(Environment.SpecialFolder.Personal);
string jpgFilename = System.IO.Path.Combine(documentsDirectory, name); // hardcoded filename, overwritten each time
NSData imgData = image.AsJPEG();
if (imgData.Save(jpgFilename, false, out NSError err))
{
return jpgFilename;
}
else
{
Console.WriteLine("NOT saved as " + jpgFilename + " because" + err.LocalizedDescription);
return null;
}
}
public void SelectedImage()
{
var picker = new GMImagePickerController();
picker.FinishedPickingAssets += (s, args) => {
PHAsset[] assets = args.Assets;
foreach (PHAsset asset in assets)
{
PHImageManager.DefaultManager.RequestImageData(asset, null, (NSData data, NSString dataUti, UIImageOrientation orientation, NSDictionary info) =>
{
NSUrl url = NSUrl.FromString(info.ValueForKey(new NSString("PHImageFileURLKey")).ToString());
string[] strs = url.Split("/");
UIImage image = UIImage.LoadFromData(data);
string file = Save(UIImage.LoadFromData(data), strs[strs.Length - 1]);
MessagingCenter.Send<Object, string>(this, "ImagesSelected", file);
}
);
}
};
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(picker, true,null);
}
}
}
in your contentPages
...
List<string> selectedImages;
...
public MyPage()
{
selectedImages = new List<string>();
InitializeComponent();
MessagingCenter.Subscribe<Object,string>(this, "ImagesSelected",(object arg1,string arg2) =>
{
string source = arg2;
selectedImages.Add(source);
});
}
If you want to select the images ,call the method
DependencyService.Get<ISelectMultiImage>().SelectedImage();

Can one use system icons on tabs in Xamarin Forms?

Is there a way to specify a "system" icon to be displayed on a tab when using Xamarin Forms? I would like to use icons such as Favourites, Bookmark, History etc but I do not want to supply all my own images for the various platforms.
Using Xamarin.iOS one can use this syntax:
tab1.TabBarItem = new UITabBarItem (UITabBarSystemItem.Favorites, 0);
I can however not find how to do this in the cross-platform Xamarin.Forms project.
This is my current code:
var profilePage = new ContentPage {
Title = "Profile",
//This requires my own images to be added to the project whereas
//I wish to use built-in images which are platform specific for
//Favourites, Bookmark, More, etc...
//Icon = "Profile.png",
Content = new StackLayout {
Spacing = 20, Padding = 50,
VerticalOptions = LayoutOptions.Center,
Children = {
new Entry { Placeholder = "Username" },
new Entry { Placeholder = "Password", IsPassword = true },
new Button {
Text = "Login",
TextColor = Color.White,
BackgroundColor = Color.FromHex("77D065") }}}};
var settingsPage = new ContentPage {
Title = "Settings",
Content = new StackLayout {
Spacing = 20, Padding = 50,
VerticalOptions = LayoutOptions.Center,
Children = {
new Entry { Placeholder = "Username" },
new Entry { Placeholder = "Password", IsPassword = true }}}
};
MainPage = new TabbedPage { Children = {profilePage, settingsPage} };
For iOS
you need a custom renderer for your page. In my example, it is CustomTabsPage class. You cannot just use system icons to create a UIImage. We need to use UITabBarItem. The problem is that UITabBarItem doesn't allow changes to either title or image/icon. But, we can copy an image from it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIKit;
using ButtonRendererDemo;
using ButtonRendererDemo.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(CustomTabsPage), typeof(CustomTabsPageRenderer))]
namespace ButtonRendererDemo.iOS
{
public class CustomTabsPageRenderer : TabbedRenderer
{
#region Sytem Image with custom title
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
foreach (var item in TabBar.Items)
{
item.Image = GetTabIcon(item.Title);
}
}
private UIImage GetTabIcon(string title)
{
UITabBarItem item = null;
switch (title)
{
case "Profile":
item = new UITabBarItem(UITabBarSystemItem.Search, 0);
break;
case "Settings":
item = new UITabBarItem(UITabBarSystemItem.Bookmarks, 0);
break;
}
var img = (item != null) ? UIImage.FromImage(item.SelectedImage.CGImage, item.SelectedImage.CurrentScale, item.SelectedImage.Orientation) : new UIImage();
return img;
}
#endregion
}
}
For Android
things are easier
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ButtonRendererDemo;
using ButtonRendererDemo.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms.Platform.Android.AppCompat;
using Android.Support.Design.Widget;
[assembly: ExportRenderer(typeof(CustomTabsPage), typeof(CustomTabsPageRenderer))]
namespace ButtonRendererDemo.Droid
{
public class CustomTabsPageRenderer : TabbedPageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TabbedPage> e)
{
base.OnElementChanged(e);
//var layout = (TabLayout)ViewGroup.GetChildAt(1); //should be enough but just for robustness we use loop below
TabLayout layout = null;
for (int i = 0; i < ChildCount; i++)
{
layout = GetChildAt(i) as TabLayout;
if (layout != null)
break;
}
if (layout != null)
{
for (int tabIndex = 0; tabIndex < layout.TabCount; tabIndex++)
SetTabIcon(layout, tabIndex);
}
}
private void SetTabIcon(TabLayout layout, int tabIndex)
{
var tab = layout.GetTabAt(tabIndex);
switch (tabIndex)
{
case 0:
tab.SetIcon(Resource.Drawable.icon2);//from local resource
break;
case 1:
tab.SetIcon(Resource.Drawable.ic_media_play_dark);//from Android system, depends on version !
break;
}
}
}
}
In order to use icons, you need a platform-specific structure. In Xamarin.Forms, for this to work:
Icon = "youricon.png";
You need:
iOS: put the image in /Resources/youricon.png
Android: put the image in /Resources/drawable/youricon.png
Win Phone: put the image in the Windows Phone application project root.
You should write custom TabbedPage renderer for iOS project:
public class YourTabbedRenderer : TabbedRenderer {
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
var items = TabBar.Items;
for (var i = 0; i < items.Length; i++) {
// set your icons here, you could do some string comparison (check tab title or tab icon resource name)
// items[i].Image =
// items[i].SelectedImage =
}
}
}
http://developer.xamarin.com/guides/cross-platform/xamarin-forms/custom-renderer/

Resources