Cross Platform C#

Understanding the Android Resource System

Learn how to leverage the powerful resource system of Android to support a variety of Android devices and languages.

As a .NET developer getting up to speed with the Android platform, some things will feel pretty familiar. The XML used to define layouts won't throw off a developer familiar with other markup-based layout systems such as XAML and HTML. The Activity lifecycle is also pretty reminiscent of what you'd find in Web Forms or Windows Forms. On the other hand, the way Android manages resources can feel a bit strange at first until you get your head around how it works.

A large part of any Android application falls under the category of resources. In this context, resources can include things like layouts, images, audio, video, language definitions, styles and so on. The resource system in Android is quite powerful, and while it may seem odd at first, there's a method to its madness. In this article, I'll walk through the basics of how this system works, and how you can take advantage of it in your apps.

When you create a new Xamarin.Android application, some resources are provided by default, and can be found in several subfolders under the main Resources folder. I'll start by taking a look at those folders and files provided in the default project.

Drawable
Drawables, as the name implies, are resources that can be drawn to the screen. I know that probably sounds redundant, but this actually goes further than simply being images. While bitmap graphics are indeed drawables, this category can also include other resources defined in XML, such as shapes, state lists, transitions and more. For example, the following XML defines a white circle shape:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:innerRadiusRatio="3"
  android:shape="ring"
  android:useLevel="false" >

    <solid android:color="#fff" />

    <size
      android:height="20dp"
      android:width="20dp" />

</shape>

Another thing to note is the way sizes are defined in Android XML files. In this case, the circle is given a diameter of 20dp. This can also be written as 20dip, as Android will accept either one. Because Android devices can come in all sorts of different sizes and screen resolutions, dealing in raw pixels doesn't scale very well. Instead, you can specify sizes in device-independent pixels, or dip, which can help make your layouts more flexible and easier to maintain.

Xamarin.Android projects provide a default image named Icon.png in the drawables folder, which is used as the icon for the application.

Values
The values folder can contain numerous types of value definitions, including language strings, styles, colors and dimensions. New Android projects will have a default Strings.xml file that contains some sample strings that can be referenced throughout the application:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="Hello">Hello World, Click Me!</string>
  <string name="ApplicationName">My Awesome App</string>
</resources>

This will feel pretty familiar to anyone with experience using resource files in the Microsoft .NET Framework to define language strings.

Layout
UIs for Android apps are typically defined in XML files, and are put in the Layout folder. Android has a very rich layout system, somewhat similar to XAML but without data binding.

The Main.axml file here is used as the default sample UI in a new Xamarin.Android app:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <Button
    android:id="@+id/MyButton"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/Hello" />
</LinearLayout>

A LinearLayout is essentially the Android equivalent to a StackPanel, and Button is pretty self-explanatory. In this case, the button's text is set to the string named Hello, defined in the string's file from earlier.

There are many other types of resources that can be used in Android apps, but for now I'll stick with these to help demonstrate how it all fits together.

Designer File
As part of the build process, Android will actually go through all resources included in the app and statically check them for problems. This allows you to find many potential problems at compilation time, such as syntax errors in layout files. If everything looks good, it will then generate a file named Resource.designer.cs. This gets regenerated during each build, so it isn't something you want to be editing because any changes will get lost the next time the project is built.

Inside this file you'll see that Android has generated several classes, and assigned unique identifiers to each resource found in the app (see Listing 1). If for some reason you do find you want to make changes to the Resource file, it's defined as a partial class, so you have the freedom to extend it in another file, outside of the one generated in each build. These identifiers are what you use throughout the app to reference a resource, rather than its file name. This might seem odd, but it will make a lot more sense in a bit.

Listing 1: The Android Resource File
[System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "1.0.0.0")]
public partial class Resource
{
  static Resource()
  {
    global::Android.Runtime.ResourceIdManager.UpdateIdValues();
  }
		
  public static void UpdateIdValues()
  {
  }
		
  public partial class Attribute
  {
			
    static Attribute()
    {
      global::Android.Runtime.ResourceIdManager.UpdateIdValues();
    }
			
    private Attribute()
    {
    }
  }
		
  public partial class Drawable
  {
			
    // aapt resource value: 0x7f020000
    public const int Icon = 2130837504;
			
    static Drawable()
    {
      global::Android.Runtime.ResourceIdManager.UpdateIdValues();
    }
			
    private Drawable()
    {
    }
  }
		
  public partial class Id
  {
			
    // aapt resource value: 0x7f050000
    public const int myButton = 2131034112;
			
    static Id()
    {
      global::Android.Runtime.ResourceIdManager.UpdateIdValues();
    }
			
    private Id()
    {
    }
  }
		
  public partial class Layout
  {
			
    // aapt resource value: 0x7f030000
    public const int Main = 2130903040;
			
    static Layout()
    {
      global::Android.Runtime.ResourceIdManager.UpdateIdValues();
    }
			
    private Layout()
    {
    }
  }
		
  public partial class String
  {
			
    // aapt resource value: 0x7f040001
    public const int app_name = 2130968577;
			
    // aapt resource value: 0x7f040000
    public const int hello = 2130968576;
			
    static String()
    {
      global::Android.Runtime.ResourceIdManager.UpdateIdValues();
    }
			
    private String()
    {
    }
  }
}

Resource Conventions
I mentioned earlier that the Android resource system is powerful, and that power comes in the form of the conventions it supports for providing multiple versions of a resource within an application. This is also why Android makes you reference resources by their identifier rather than their name.

Alternative versions of resources can be provided by creating new subfolders under Resources using the same root names, but also appending one or more qualifiers to the end. The list of available qualifiers allowed by Android is lengthy, but include:

  • language
  • screen size
  • screen orientation
  • screen density
  • version of Android

At runtime, Android will automatically determine the most specific resource it can use, based on the current device and what qualifiers are provided by the application. For example, let's say I'm writing an app that needs to support English by default, and also Spanish if that's what the device is set to.

To achieve this, I'd need to provide a version of Strings.xml for each language. In Values/Strings.xml, I'd provide all my English strings. Next, I'd create a new folder named Values-es, because es is the language code for Spanish. In that folder, I'd provide a version of Strings.xml with the same keys, except in this version the values are all in Spanish.

Qualifiers can be combined in the folder name as well, separated by hyphens. For example, to specify drawables for a high-density screen in landscape orientation, the folder name would be Drawable-land-hdpi.

One important thing to make sure of is that you always provide a default set of resources, just so Android has something to fall back on in the event that none of the alternative versions provided by the app apply. Android devices come in all sorts of shapes and sizes, so manually covering all of them by convention is pretty unrealistic.

Referencing Resources
Resources are generally always accessed using the identifier generated for them when the app was compiled. The syntax for using this identifier varies slightly depending on whether you're trying to access it from C# or XML.

Earlier I showed you an example of referencing string values from XML, using it to set the text of a button. The same pattern applies for other resource types as well, so it would be @drawable, @layout and so on. To access the resource from C#, you can use the classes generated inside of Resource.designer.cs directly:

var button = FindViewById<Button>(Resource.Id.MyButton);
button.Text = Resources.GetText(Resource.String.Hello);

Configuration Changes
One thing that commonly trips up new Android developers is that configuration changes cause Android to destroy and recreate the current activity. There are a number of things that count as a configuration change, such as switching between landscape and portrait orientation, and changing the locale of the device. This can be extremely jarring when you're not expecting it, because it means your UI might lose all of its state if you aren't careful.

That might seem pretty heavy-handed, but it actually ties in directly to the resource system and the qualifiers discussed earlier. This allows Android to pick up the appropriate resources for any new configuration that comes along on the fly, rather than having to restart the application in order to pick it up. Android does also give you hooks into when it's tearing down and recreating your UI, which provides a chance to save and restore any state that might be needed by the application at a given time.

Countless Configurations
This only scratches the surface of the types of resources that can be used in Android apps. Android is a system designed with the intention of being run in countless combinations of configurations, between different screen sizes, resolutions, versions and more. Its resource system directly reflects that, and gives you the power to support as many of those combinations as you need to without having to make drastic changes to your app.

About the Author

Greg Shackles, Microsoft MVP, Xamarin MVP, is a Principal Engineer at Olo. He hosts the Gone Mobile podcast, organizes the NYC Mobile .NET Developers Group, and wrote Mobile Development with C# (O'Reilly). Greg is obsessed with heavy metal, baseball, and craft beer (he’s an aspiring home brewer). Contact him at Twitter @gshackles.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube