image

Extending glidex.forms on Android to load a placeholder for images that fail to load

Extending glidex.forms on Android to load a placeholder for images that fail to load

What is glidex.forms?

The glidex.forms library is a Xamarin.Forms implementation of Glide, which is one of the quasi standards for imaging on Android (it is even recommended by Google). Luckily, Jonathan Peppers from Microsoft has a passion to improve Xamarin.Android, and Xamarin.Forms takes a big advantage from that as well. He made the Xamarin.Android Binding library as well as the Xamarin.Forms implementation. Like before with Xamarin.Forms.Nuke, I learned about that library because I am substituting my former image caching solution with Akavache.

Why do we need to extend the library?

If you want to load a placeholder that is stored in the resources of your Android project – there is no need. You could just implement a custom hook into the GlideExtensions class of glidex.forms (I’ll show you that one as well). But if you want to load an image from a Xamarin.Forms resource or even a from a font, we’ll need to extend the GlideExtensions class a little bit.

On a side note, I tried to use the existing mechanism of implementing an IGlideHandler for these purposes, but due to timing issues I never was able to load these kinds of placeholders and I moved on by extending the GlideExtensions class.

Show me some code!

Use Android resource

Let’s have a look at how to load an Android resource as a placeholder first (because it is the easiest way). To do so, we just need to create a custom implementation of an IGlideHandler in the Android project, which will then be called by the GlideExtensions implementation:

public class GlideWithAndroidResourcePlaceholder : IGlideHandler
{
    public GlideWithAndroidResourcePlaceholder()
    {
    }

    public bool Build(ImageView imageView, ImageSource source, RequestBuilder builder, CancellationToken token)
    {
        if (builder != null)
        {
            //easiest way - add the image to Android resources ....
            //general placeholder:
            //builder.Placeholder(Resource.Drawable.MSicc_Logo_Base_Blue_1024px_pad25).Into(imageView);

            //error placeholder:
            builder.Error(Resource.Drawable.MSicc_Logo_Base_Blue_1024px_pad25).Into(imageView);

            return true;
        }
        else
            return false;
    }
}

As you can see, the RequestBuilder has already a placeholder mechanism in place. We just hook into that. If you want a general placeholder (shows the image also during download), use the Placeholder method. For failed image loading only, use the Error method. That’s it.

Use Xamarin.Forms resouce/FontImageSource

As we want to be able to load FontImageSource and Xamarin.Forms resources as well however, we need to go another route. Like we did on iOS, we need to add a placeholder property to the Forms class:

public static ImageSource? PlaceholderImageSource { get; private set; }

To fill our ImageSource with either a Xamarin.Forms resource or a FontImageSource, add these two methods as well:

public static void PlaceholderFromResource (string resourceName, Assembly assembly) =>
	PlaceholderImageSource = ImageSource.FromResource (resourceName, assembly);

public static void PlaceholderFromFontImageSource (FontImageSource fontImageSource) =>
	PlaceholderImageSource = fontImageSource;

Now that we have the PlaceholderImageSource in place, we need to extend the GlideExtensions class. First, we implement a Handler for FontImageSource. The Xamarin.Forms handler for FontImageSource provides us a bitmap, which we are going to use as a placeholder. I only got this working with the AsDrawable method, the AsBitmap method always threw an exception because it couldn’t cast the Bitmap to Drawable (I did not investigate that further).

private static async Task<RequestBuilder?> HandleFontImageSource (RequestManager request, Context? context, FontImageSource fontImageSource, CancellationToken token, Func<bool> cancelled)
{
	if (context == null)
		return null;

	var defaultHandler = new Xamarin.Forms.Platform.Android.FontImageSourceHandler ();

	var bitmap = await defaultHandler.LoadImageAsync (fontImageSource, context, token);

	if (token.IsCancellationRequested || cancelled())
		return null;

	if (bitmap == null)
		return null;

	return request.AsDrawable().Load (bitmap);
}

As for the HandleFontImageSource method, I also needed to change the return value of the HandleStreamImageSource method to use the AsDrawable method, otherwise we would get the same exception as with the FontImageSource:

static async Task<RequestBuilder?> HandleStreamImageSource (RequestManager request, StreamImageSource source, CancellationToken token, Func<bool> cancelled)
{ 
        //code omitted for readability

	return request.AsDrawable().Load (memoryStream.ToArray ());
}

The final steps to make it all work are done in the LoadViaGlide method. First, add another RequestBuilder? variable and name it errorBuilder:

RequestBuilder? errorBuilder = null;

After the switch that handles the source parameter, add these lines of code:

switch(Forms.PlaceholderImageSource) {
	case StreamImageSource streamSource:
		errorBuilder = await HandleStreamImageSource (request, streamSource, token, () => !IsActivityAlive (imageView, Forms.PlaceholderImageSource));
		break;
	case FontImageSource fontImageSource:
		errorBuilder = await HandleFontImageSource(request, imageView.Context, fontImageSource, token, () => !IsActivityAlive (imageView, Forms.PlaceholderImageSource));
		break;
	default:
		errorBuilder = null;
		break;
}

if (errorBuilder != null)
	builder?.Error (errorBuilder);

We are handling our PlaceholderImageSource like we do with the ImageSource that we intend to load. If we have a valid errorBuilder, we pass that into the entire process. With that, we have already everything in place. Let’s see how to use it in your app.

How to use it in your Xamarin.Forms – Android project

Head over to the MainActivity class in your Android project and add the following method:

private void AttachGlide()
{
    Android.Glide.Forms.Init(this, null, false);

    //recommended way of loading resource images=>
    //Android.Glide.Forms.Init(this, new GlideWithAndroidResourcePlaceholder(), false);

    //Xamarin Forms resource image
    //Android.Glide.Forms.PlaceholderFromResource("CachedImageTest.MSicc_Logo_Base_Blue_1024px_pad25.png", Assembly.GetAssembly(typeof(MainViewModel)));

    //FontImageSource
    Android.Glide.Forms.PlaceholderFromFontImageSource(new FontImageSource
    {
        Glyph = XfNativeCachedImages.Resources.MaterialDesignIcons.ImageBroken,
        FontFamily = "MaterialDesignIcons",
        Color = Xamarin.Forms.Color.Red
    });
}

The Glide library needs to be initialized. Depending on the method you are using, there are separate ways to do so. The method above shows them all, you just need to change the commented code to play around with it in the sample (link at the end of the post). The mechanism is like the one I used for iOS and the Nuke package.

If you followed along (or downloaded the sample), you should see similar result to this:

Placeholder_Sample_Android_Title
Left: Single image with placeholder, Right: CollectionView placeholder

Conclusion

As we did on iOS, we now also use native caching and image handling on Android. It took me quite some time to get it up and running, but it was worth the effort. The sample has more than five hundred remote images loaded into a CollectionView. Check how smooth the scrolling is (even with DataBinding!).

You can find the sample here on Github, while the modified version of glidex.forms is available here. As always, I hope this post will be helpful for some of you.

Until the next post, happy coding, everyone!

Posted by msicc in Android, Dev Stories, Xamarin, 1 comment
Extending Xamarin.Forms.Nuke on iOS to load a placeholder for images that fail to load

Extending Xamarin.Forms.Nuke on iOS to load a placeholder for images that fail to load

What is Xamarin.Forms.Nuke?

Xamarin.Forms.Nuke is a Xamarin.Forms implementation of Nuke, one of the most advanced image libraries on iOS today. The Xamarin.Forms implementation focuses heavily on caching only, while the original library has a bunch of more features. I learned about this library when I started to look for an alternative to cache images via Akavache which I used before (I never blogged about that part because it wasn’t ready for that, tbh).

Why do we need to extend the library?

The library currently does only one thing (pretty well) – handling the caching of web images. It uses the default settings of the native Nuke library. The Xamarin.Forms implementation overwrites the ImageSourceHandler for UriImageSource and FileImageSource (optionally), but the case of placeholder loading is not intended in the original version. As I have multiple scenarios where a placeholder comes in handy, I decided to extend the library – and maintain my own fork of it from now on. (Maybe there will be a pull request to the original source, too).

Show me some code, finally!

For our extension, we modify the FormsHandler class as well as the ImageSourceHandler class. Let’s have a look at the FormsHandler class first. We are adding a new property for the placeholder:

public static ImageSource PlaceholderImageSource { get; private set; }

With that property in place, let’s add two methods. One method is for loading a placeholder image from an embedded resource file, while the second one is for specifying a FontImageSource.

public static void PlaceholderFromResource(string resourceName, Assembly assembly) =>
    PlaceholderImageSource = ImageSource.FromResource(resourceName, assembly);

public static void PlaceholderFromFontImageSource(FontImageSource fontImageSource) =>
    PlaceholderImageSource = fontImageSource;

I have chosen the FormsHandler class because setting the placeholder is a global thing (in my scenarios, your mileage may vary). That’s already everything in takes in the FormsHandler class, so let’s have a look at the ImageSourceHandler class.

As we are using the default Xamarin.Forms ImageSourceHandler for the resource image (which is a StreamImageSource) and the FontImageSource, we need to add the static fields for them first:

private static readonly StreamImagesourceHandler DefaultStreamImageSourceHandler = new StreamImagesourceHandler();

private static readonly FontImageSourceHandler DefaultFontImageSourcehandler = new FontImageSourceHandler();

Now let’s implement the loading of the placeholder in a separate method:

private static Task<UIImage> LoadPlaceholderAsync()
{
    switch (FormsHandler.PlaceholderImageSource)
    {
        case StreamImageSource streamImageSource:
            FormsHandler.Warn($"loading placeholder from resource");
            return DefaultStreamImageSourceHandler.LoadImageAsync(streamImageSource);
                    
        case FontImageSource fontImageSource:
            FormsHandler.Warn($"loading placeholder from Font");
            return DefaultFontImageSourcehandler.LoadImageAsync(fontImageSource);
        default:
            FormsHandler.Warn($"no valid placeholder found");
            return null;
    }
}

As you can see, there is nothing overly complicated in this method. Based on the type of the placeholder set in the FormsHandler class, we are calling the default Xamarin.Forms implementation for the placeholder image. Let’s take this code into action by changing the LoadImageAsync method of the ImageSourceHandler:

public async Task<UIImage> LoadImageAsync(
    ImageSource imageSource,
    CancellationToken cancellationToken = new CancellationToken(),
    float scale = 1)
{
    var result = await NukeHelper.LoadViaNuke(imageSource, cancellationToken, scale);
    if (result == null)
        result = await LoadPlaceholderAsync();

    return result;
}

As we need to know if the Nukehelper class is able to load the image, we are already running the code by awaiting it at this level. If the result is null, we are loading the placeholder image via our prior implemented method. That’s all we need to do in our forked Xamarin.Forms.Nuke repository.

How to use it in your Xamarin.Forms – iOS project

First, clone my fork (or fork it if you want) of the Xamarin.Forms.Nuke repository and import it into your Xamarin.Forms solution and reference it in your iOS project. Once that is done, we need to initialize the Nuke library (like in the original source) in the AppDelegate‘s FinishedLaunching method:

Xamarin.Forms.Nuke.FormsHandler.Init(true, false);

The second step is to define the placeholder image source. The FontImageSource should be defined after the LoadApplication method. This way, you can the Xamarin.Forms way of loading the font as a resource.

//Resource image
Xamarin.Forms.Nuke.FormsHandler.PlaceholderFromResource("CachedImageTest.MSicc_Logo_Base_Blue_1024px_pad25.png", Assembly.GetAssembly(typeof(MainViewModel)));

//FontImageSource
Xamarin.Forms.Nuke.FormsHandler.PlaceholderFromFontImageSource(new FontImageSource
{
    Glyph = CachedImageTest.Resources.MaterialDesignIcons.ImageBroken,
    FontFamily = "MaterialDesignIcons",
    Color = Color.Red
});

Now use the Xamarin.Forms Image control like you always do. If the image from the web cannot be loaded, you will see the placeholder, like in these two examples:

cached image placeholder sample
Left: Single image failed loading, right image in CollectionView failed loading

With a few additions to the Xamarin.Forms.Nuke library, we have implemented a placeholder mechanism for images that can’t be loaded. As always, I hope this post will be useful for some of you. Now that I have the iOS implementation of a fast cached image with placeholder loading in place, I will turn to Android, where I will try to achieve the same using the Glidex.Forms library and extend it to load a placeholder. There will be a full sample once that is implemented as well. Stay tuned for that one!

Until the next post, happy coding, everyone!
Posted by msicc in Dev Stories, iOS, Xamarin, 2 comments

How to generate a round image button for your Windows Phone 8.1 app (to use everywhere)

Recently, I experimented a bit because I wanted a round button that contains an image that can be used everywhere where I can add a standard button (and not just in the AppBar). I managed to get a simple style out of these experiments (sample at the end of this post).

First, you should check if you have already installed Syncfusion’s free Metro Studio (we will need it later). It is a powerful helper if you need icons, so if you do not have it, go straight ahead and download it here: http://www.syncfusion.com/downloads/metrostudio

Still here/back? Great! Ok, let’s start. In our project, generate a new button:

<Button Width="72" Height="72"></Button>

If you want your round button to have a smaller size, feel free to adjust the 72 pixels mine has to your preferred value.

The next step is to generate a new Style. Right click on the Button, and select ‘Edit Template’, followed by ‘Edit a Copy’.

Screenshot (407)

 

Set the name of your style in the next window, and save define it as an app-wide Style or on your page:

Screenshot (408)

This should open your App.xaml file and display the button as well as the newly generated style.

We are starting with our custom style modifications right at the top:

image

Set both Doubles to 0 and the Thickness to 0,0.

Next, scroll down to find the Border Element of the Button Template (closing ‘VisualStateManager.VisualStateGroups’ helps a lot).

Click on the Border element and add/adjust the ‘CornerRadius’ property. At a size of 72, the minimum value is 38 for the radius. This should be fine for most cases, but it may be higher/smaller if you are using another size. Don’t worry if your button looks like this at them moment:

image

We are going to fix it right now by setting the Height and Width properties of our Border element:

Height="{Binding Path=Height, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Width="{Binding Path=Width, RelativeSource={RelativeSource Mode=TemplatedParent}}"

This binds the Width and Height properties of our Button to the Style. Now we just need to define the Height and the Width of our Button to make it actually look really round. Setting both to 72 will result in a nice round button.

Like you can imagine, displaying text does not make a lot of sense in this case. Round Buttons should contain an image. You could add one through adding a background, but this will result in a strange looking button when it gets pressed. Also, it does not reflect changes like a color change. To solve this, we are going to add code that is able to draw a shape for us. This is achieved with the Path Class  in XAML. The Path class draws lines into a FrameworkElement like a Canvas or a Border.

To enable our Style to work with Path Data, we need to add some code before the ‘Template’ property Setter in our Style:

<Setter Property="ContentTemplate">
    <Setter.Value>
        <DataTemplate>
            <Path Stretch="Uniform"
                  RenderTransformOrigin="0.5,0.5"
                  Margin="2,6,2,2"
                  Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                  Data="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"></Path>
        </DataTemplate>
    </Setter.Value>
</Setter>

What does this code do? The ContentTemplate allows us to add rich content to our UIElement, the Button. To make it resuable, we are setting it up in our custom button style. The RenderTransforOrigin property value of 0.5,0.5 centers our Path drawn shape within the border. However, I found out that some shapes do not look good with that alone. That’s why I adjusted the Margin property together with it. This should fit most icon shapes, but you might adjust this for your own needs.

The most important aspects are the Fill property as well as the Data property. Binding the Fill Brush to the Foreground Brush property is necessary to reflect changes like theme changes as well as changes in the VisualState. Only this way it behaves like a native Button. Binding the Data property allows us to enter the Path string into the Content property of a button that uses our Style without any conversion. This makes it very simple to generate a button with our desired icon.

And this is where Syncfusion’s MetroStudio comes in handy. It allows you not only to generate icons as png, but also as shape in XAML. To get the relevant Data, open MetroStudio, search for your icon. Below the icon, there is an Edit Button. Tap it to open the icon settings page. On that settings page, you set up your button. Play around a little bit to get used to it (it’s pretty easy).

Once you have your desired icon on the screen, click on the </>XAML Button. Copy the highlighted part of the XAML code:

image

Back in Visual Studio, add this copied code to the Content property of our Button:

Content="F1M181.003,-1898.78L207.077,-1902.33 207.089,-1877.18 181.027,-1877.03 181.003,-1898.78z M207.065,-1874.28L207.085,-1849.1 181.023,-1852.69 181.022,-1874.45 207.065,-1874.28z M210.226,-1902.79L244.798,-1907.84 244.798,-1877.5 210.226,-1877.22 210.226,-1902.79z M244.807,-1874.04L244.798,-1843.84 210.226,-1848.72 210.177,-1874.1 244.807,-1874.04z" 
Height="72" 
Width="72"
Style="{StaticResource RoundButtonStyle}" 
VerticalAlignment="Center" 
HorizontalAlignment="Center"/>

Which will result in this nice looking round button with a Windows logo on it:

image

If you run the sample project, you can see that the Button behaves like a native Button with text. Download the sample project here.

I am pretty sure this can be improved. I will continue to play around with this, and if I have found enough optimizations, I will write another post about them. Until then, this should help you to get started with your own round button – and the best thing: you can use it like any standard button wherever you want in your Windows (Phone) 8.1 app!

Happy coding, everyone!

Posted by msicc in Archive, 1 comment

How to create a folder in Windows Phone 8.1 Pictures Library (and save/read files into/from it)

Some of you might have noticed that UniShare has its own folder in your devices picture library. Also some other apps like WhatsApp or Tweetium have it. The advantages of your app’s own folder are clear:

  • easier to get images into your app
  • user can always reflect which pictures come from your app
  • another presence of your app within the OS
  • higher remember rate for your app at the user site (which leads to more frequent usage of your app)

This post will show you how easy it is to generate a folder into the Pictures Library as well as save and read files into/from this folder.

Preparation

First, you need to add this using statements to your app:

using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;

Next, add the Picture Library capabilities to your app’s Package.appmanifest.

image

If you have a 8.1 Silverlight app, you need to add it to both the Package.appmanifest as well as the WMAppManifest.xml:

image

Then we are already able to generate our folder with this single line of code (counts for both Silverlight and Runtime apps):

StorageFolder appFolder= await KnownFolders.PicturesLibrary.CreateFolderAsync("myCustomAppFolder", CreationCollisionOption.OpenIfExists);

You should always use the CreateFolderAsync method together with the CollisionOption ‘OpenIfExists’. This way, your app will open it every time you are going to save a file, but creates the folder if it does not exist yet. If you now go to your pictures library, you will not see your folder yet, although it is there (use a File Manager app to check it if you want). Folders do only get populated when they have content. This is what the next step is about.

Save an image file

Saving an image is also pretty straight forward. First we are generating a StorageFile within our folder:

StorageFile myfile= await appFolder.CreateFileAsync("myfile.jpg", CreationCollisionOption.ReplaceExisting);

This generates a File Container that we can write our image to. To save the image we are going to asynchronously write the Stream of our image into it:

//asuming we have an Image control, replace this with your local code
var img = myImage.Source as WriteableBitmap;
//get fresh drawn image 
img.Invalidate();

using (Stream stream = await myfile.OpenStreamForWriteAsync())
{
   img.SaveJpeg(stream, img.PixelWidth, img.PixelHeight, 0, 100);
}

This code works for both a Windows Phone 8.1 Silverlight and Runtime apps. If you now go to your Pictures library, you will see your app’s folder as well as your saved image. Pretty easy, right?

Read images from our app’s folder

Reading an image file is pretty easy as well. Here is the code:

//open the picture library
StorageFolder libfolder = KnownFolders.PicturesLibrary;
//get all folders first
IReadOnlyList<StorageFolder> folderList = await libfolder.GetFoldersAsync();
//select our app's folder
var appfolder = folderList.FirstOrDefault(f => f.Name.Contains("myCustomAppFolder"));
//get the desired file (assuming you know the file name)
StorageFile picfile = await appPicturesFolder.GetFileAsync("myfile.jpg");
//generate a stream from the StorageFile
var stream = await picfile.OpenAsync(FileAccessMode.Read);
//generate a new image and set the source to our stream
BitmapImage img = new BitmapImage();
img.SetSource(stream);

//todo: work with the image

To get our generated folder, we need to fetch a list of folders in the library using the StorageFolder.GetFoldersAsync() method. We then query this list for our app’s folder. If you want to get a list of all pictures in your folder, you can use the StorageFile.GetFilesAsync() method. What I have done above is to load our saved single file. Finally, I opened a stream from this file and assigned it to a new BitmapImage, which can be used in our app.

There are also a lot of other options one can do with these folders and files, this is a very common scenario.

As always, I hope this post is helpful for some of you.

Posted by msicc in Archive, 0 comments