Android resource selection layout- and values- inconsistencies

BrantApps picture BrantApps · Oct 19, 2012 · Viewed 20k times · Source

The issue I am experiencing indicates that the resource bucket being selected for a given activity's layout XML is inconsistent with the resources being selected from the values folder despite the exact same resource qualifiers being used in each set of folders.

Example

After placing some logging code within my application's abstract parent activity I can see that when starting my application over a Nexus 7 type emulator (Android 4.1) the smallest width is indeed 600dp, the layout-sw600dp-* folder is being used to fetch the UI for the activity but the folder being used for the values is values-large-*. I was expecting this to be values-sw600dp-* thus providing me with the vital information as to what resource bucket the activity is running under.

Code doing the logging within my app's parent activity for all android.app.Activitys

  protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final Configuration config = getResources().getConfiguration();
    Log.i(this.getClass().getSimpleName(), String.format("Smallest width is [%s]", config.smallestScreenWidthDp));
    configurationContext = SupportedDeviceConfiguration.fromResourceQualifer(getString(string.resourceQualifier));
    Log.i(this.getClass().getSimpleName(), String.format("Running under the [%s] configuration context.", configurationContext.getResourceQualifier()));
...

Logging output from when I run this code on a Nexus 7 type device;

[Logging fluff] Smallest width is [600]
[Logging fluff] Running under the [layout-large-land] configuration context.

I know what you are thinking - where did that layout-large-land derivation come from? Read on...

Background

I am trialling an approach outlined here which would allow me to inspect the resources bucket under use at runtime. Essentially the approach I have implemented has the following structure of resource qualifiers;

- res
  + layout                   // Default portrait layout.
  + layout-land              // Default landscape layout
  + layout-large-land        // pre 3.2 phablet landscape layout (Galaxy Note at v2.3.3)
  + layout-xlarge-land       // pre 3.2 tablet landscape layout
  + layout-xlarge-port       // pre 3.2 tablet portrait layout
  + layout-sw520dp-port      // post 3.1 phablet portrait layout (Galaxy Note at v4.0.3)
  + layout-sw520dp-land      // post 3.1 phablet landscape layout
  + layout-sw600dp-port      // post 3.1 mini-tablet portrait layout (Nexus 7)
  + layout-sw600dp-land      // post 3.1 mini-tablet-landscape layout 
  + layout-sw700dp-port      // post 3.1 tablet portrait layout
  + layout-sw700dp-land      // post 3.1 tablet landscape layout
  - values                   // Contains the root strings.xml
     strings.xml
  - values-land
     default-config.xml            
  - values-large-land
     default-config.xml        
  - values-xlarge-land
     default-config.xml     
  - values-xlarge-port
     default-config.xml     
  - values-sw520dp-port
     default-config.xml     
  - values-sw520dp-land
     default-config.xml     
  - values-sw600dp-port
     default-config.xml     
  - values-sw600dp-land
     default-config.xml     
  - values-sw700dp-port
     default-config.xml     
  - values-sw700dp-land
     default-config.xml

So essentially the values qualifiers reflect that of the layout qualifiers. Under each of values-* folders I have defined a single XML file called device-config.xml with content;

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="resourceQualifier">layout-{qualifier of values folder}</string>
</resources>

So, for example the values-sw600dp-land folder's device-config.xml contains a single string with value layout-sw600dp-land. The objective here is for my code to remain in-sync with the resource layouts being displayed on screen. This is needed so that my code doesn't go off "finding by id" some item which doesn't exist on the displayed layout owing to the real-estate involved.

(Optional) Deeper reasoning for why I am doing this

The deeper reasoning for wanting to know the bucket being used at runtime was born out of the realisation that my single-fragment-for-all-configurations code was becoming difficult to manage with various switch based logic which was not transparent and often duplicated features from other layouts...it was as if I needed some sort of Fragment Inheritance ...which if you follow the link is exactly what I did. The downside of this is that I need to know what screen I am working with before instructing the framework to instantiate the x, y, or z fragment, safe in the knowledge that the Fragment being created will never be out of sync with the layout it is meant to inflate. This inheritance works and allows for a far more manageable fragment stack (Sonar is happier too which is nice).

Summary

However, I have been thwarted by this apparent discrepancy between which layout folder and values folder the framework selects. Each have the same qualifiers therefore why doesn't an Activity leveraging the layout-sw600dp-land UI XML use the values-sw600dp-land resource? I am hoping I've got something wrong because it was the neatest of the potential solutions posted on the SO discussion I linked to above.

Answer

Pointer Null picture Pointer Null · Oct 22, 2012

I'm sure you're dealing with resource precedence used for selection.

If you provide folders:

layout-sw600dp-*
values-large-*
values-sw600dp-*

Android is not obliged to match values selection folder to those of layout, rather it uses same precedence logic separately for layout and separately for values folder.

You can learn about this selection algorithm here: http://developer.android.com/guide/topics/resources/providing-resources.html#BestMatch