08 February 2015

Using a full-size none-stretched background image in a Xamarin.Forms app

Intro

I always like to use a kind of a translucent background image to my app’s screens, that makes it look a bit more professional than just a plain single-colored screen – a trick I learned from my fellow MVP Mark Monster in the very early days of Windows Phone development. Now that I am trying to learn some Xamarin development, I want to do the same thing – but it turns out that works a bit different from what I am used to.

Setting up the basic application

I created a Xamarin Forms portable app “BackGroundImageDemo”, but when you create a new Xamarin Forms application using the newest templates in Xamarin 3.9, you get an application that uses forms, but no XAML. Having lived and dreamed XAML for the last 5 years I don’t quite like that, so start out with making the following changes:

1. Update all NuGet packages - this will get you (at the time of this writing) the 1.3.2 forms packages

2. Add StartPage.Xaml to BackGroundImageDemo (Portable)

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="BackGroundImageDemo.StartPage">
	<Label Text="Hello world from XAML" VerticalOptions="Center" 
         HorizontalOptions="Center" FontSize="30" />
</ContentPage>

3. Make some changes to the App.cs in BackGroundImageDemo (Portable) to make it use the XAML page:

namespace BackGroundImageDemo
{
  public class App : Application
  {
    public App()
    {
      // The root page of your application
      MainPage = new StartPage();
    }
    
    // stuff omitted
  }
}

And when you run that, for instance on Windows Phone, it looks like this:

image

JupiterAdding a background picture

Now suppose I want to make an app related to astronomy – then I might use this beautiful picture of Jupiter, that I nicked off Wikipedia, as a background image:

It has a nice transparent background, so that will do. And guess what, the ContentPage class has a nice BackgroundImage attribute, so we are nearly done, right?

As per instructions found on the Xamarin developer pages, images will need to be:

  • For Windows Phone, in the root
  • For Android, in the Resources/drawable folder
  • For iOS, in Resources folder

In addition, you must set the right build properties for this image:imageimage

  • For Windows Phone, set “Build Action” to “Content” (this is default) and “Copy to Output Directory” to “Copy if newer”
  • For Android, this is “AndroidResource” and “Do not copy”
  • For iOS, this is “BundleResource” and “Do not copy”

So I copy Jupiter.png three times in all folders (yeah I know, there are smarter ways to do that, that’s not the point here) add BackgroundImage=’'Jupiter.png” to the ContentPage tag and… the result, as we can see on the to the right, is not quite what we hoped for. On Android, Jupiter is looking like a giant Easter egg. Windows Phone gives the same display. On the Cupertino side, we get a different but equally undesirable effect.

RelativeLayout to the rescue

Using RelativeLayout and constraint expressions, we can more or less achieve the same result as Windows XAML’s “Uniform”.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="BackGroundImageDemo.StartPage" >
  <RelativeLayout>
    <Image Source="Jupiter.png" Opacity="0.3"
                RelativeLayout.WidthConstraint=
                  "{ConstraintExpression Type=RelativeToParent, Property=Width}"
                RelativeLayout.HeightConstraint=
                  "{ConstraintExpression Type=RelativeToParent, Property=Height}"/>
    <Grid RelativeLayout.WidthConstraint=
              "{ConstraintExpression Type=RelativeToParent, Property=Width}"
            RelativeLayout.HeightConstraint=
              "{ConstraintExpression Type=RelativeToParent, Property=Height}">

      <Label Text="Hello world from XAML" VerticalOptions="Center"
         HorizontalOptions="Center" FontSize="30"/>
    </Grid>
  </RelativeLayout>
</ContentPage>

All elements within a RelativeLayout will essentially be drawn on top of each other, unless you specify a BoundsConstraint. I don’t do that here, so essentially every object will drawn from 0,0. By setting width and height of the RelativeLayout’s children to essentially the width and height of the RelativeLayout itself, is will automatically stretch to fill the screen. And thus the image ends up in the middle, as does the Grid with the actual UI in it. Just make sure you put the image Image first and the Grid second, or else the image will appear over your text.

I also added Opacity = “0.3” to make the image translucent and not so bright that it actually wipes out your UI. The exactly value of the opacity is a matter of taste and you will need to determine how it affects the readability of the actual UI on a real device. Also, you might consider editing the image in Paint.Net or the like and set its to 0.3 opacity hard coded in the image, I guess that would save the device some work.

Anyway, net result:

imageimageimage

Demo solution, as always, can be downloaded here.

No comments: