8

In android xml:ish

Is there any way to change a visibility attribute based on the layout size/orientation in the xml directly?

I have a button in a fragment that should be visible for small screens sizes only. On larger sizes, let's say layout-large, I want it to be hidden.

Sure, I can write code for this without any problem but for academic reasons I would like to know it it's possible to do something like this.

<Button
     android:id="@+id/btn_check_availability"
     style="@style/product_info_footer_button"
     android:layout_width="fill_parent"
     android:layout_height="35dp"
     android:text="@string/check_availability"
     android:visibility="<magic expression here>" />

Thanks

// Johan

Johan Karlsson
  • 898
  • 1
  • 10
  • 24

4 Answers4

6

This answer is based off the explanation provided here by Flávio Faria.

The visible, gone, etc can be values mapped to their corresponding enum values in a string resource - which means you can create a visibilty.xml with string resources for each layout you want, and Android will automatically resolve the one you want for each screen type.

I'd recommend the following:

/res/values/visibilty.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- Enum values pulled from Android source -->
    <string name="visibility_visible">0</string>
    <string name="visibility_invisible">1</string>
    <string name="visibility_gone">2</string>

    <string name="product_info_footer_button_visibility">@string/visibility_visible</string>
</resources>

/res/values-large/visibilty.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="product_info_footer_button_visibility">@string/visibility_invisible</string>
</resources>

And then you can reference the visibility as follows for your button:

<Button
     android:id="@+id/btn_check_availability"
     style="@style/product_info_footer_button"
     android:layout_width="fill_parent"
     android:layout_height="35dp"
     android:text="@string/check_availability"
     android:visibility="@string/product_info_footer_button_visibility" />

Warning: This depends on the device having the same enum values for visibility (0/1/2) as defined in the AOSP source. Device manufacturers and custom ROM creators can change these values, in which case this code will likely not work as desired.

Community
  • 1
  • 1
Adam S
  • 16,144
  • 6
  • 54
  • 81
  • After some experimentation, @ben75's answer also works (using `@integer` references rather than `@string`) for me in Android Studio 0.5.8. Unfortunately the layout editor fails to correctly parse either solution and complains that `@string/some_string` is not a valid value for the visibility attribute. – Adam S Jun 12 '14 at 15:58
  • Maybe specific to AS-0.5.8 ? I'm using AS-0.5.2 and both solutions works fine in preview. (preview is fine, no warnings in the editor) – ben75 Jun 12 '14 at 16:04
  • It's almost like we're using beta software ;) – Adam S Jun 12 '14 at 16:06
  • 1
    While this works technically, it is a rather hacky solution. It's interesting but I wouldn't want to release that to a wide range of devices. You better hope some version of Android or random custom ROM doesn't change the values of the visibility enum. Android has well-documented ways to better handle differences in layouts that are orientation or resolution dependent. – Robert Nekic Jun 12 '14 at 16:09
  • I agree about the problem with Android ROMs changing the values of the visibility enum, I'll add a disclaimer to the answer. As far as I'm aware, the ways of doing this _without_ taking the enum values would be to either use an `` and reproduce the entire button code in each layout file, or to use a ` – Adam S Jun 12 '14 at 16:16
  • AFAIK the values of the enum are part of the public API so I don't think they can be changed without breaking the contract. – ben75 Jun 13 '14 at 08:46
2

The android:visibility attribute is an int (like many attributes) so you can do the following :

Define a resource file named visibility.xml in values-port and values-land resource directories. The content of this file is like this :

values-port/visibility.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="buttonvisibility">0</integer> <!-- 0 is the value for android:visible -->
</resources>

values-land/visibility.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="buttonvisibility">1</integer> <!-- 1 is the value for android:invisible -->
</resources>

and in your layout.xml :

<Button
 android:id="@+id/btn_check_availability"
 style="@style/product_info_footer_button"
 android:layout_width="fill_parent"
 android:layout_height="35dp"
 android:text="@string/check_availability"
 android:visibility="@integer/buttonvisibility" />

It works : btn_check_availability is visible in portrait and invisible in landscape.

Note : this example use layout orientation as discriminator, but you can of course do it with any resource qualifier (like dimension, density, ...)

ben75
  • 29,217
  • 10
  • 88
  • 134
  • I don't think this will work. Attributes which can be references to resources are explicitly marked so in the documentation (_This may also be a reference to a resource in the form..._). The visibility attribute says _Must be one of the following constant values..._ – matiash Jun 12 '14 at 15:39
  • I experimented with this for my answer, and it seems that when you reference an _integer_ resource for the visiblity, it resolves the id of that resource, rather than the value. So for my example where I have 0/1/2 in String resources, the 0/1/2 gets swapped in, but when I have 0/1/2 in integer resources, I get: `Caused by: java.lang.NumberFormatException: Invalid int: "@2131361851"` – Adam S Jun 12 '14 at 15:41
  • @AdamS there must be something wrong in your code. I tried this solution exactly with the code posted here and it works fine. – ben75 Jun 12 '14 at 15:45
  • @ben75 You're right, I must've done something wrong - it worked fine this time. I'd recommend putting the visibility values in separate constants though, so you can do `@integer/visibility_invisible` instead so you don't have to remember what 0/1/2 means. – Adam S Jun 12 '14 at 15:54
1

There is no magic expressions available in XML. If only.

There are two approaches to this problem:

a/ use the drawable folder system. Drawable folders can be copied and named to be DPI aware following the conventions dictated here: Supporting Multiple Screens.

b/ Do it programmatically. On runtime check for screen DPI and show/hide view accordingly.

MLProgrammer-CiM
  • 17,231
  • 5
  • 42
  • 75
0

Have you looked at using includes and multiple layouts organized into the appropriate size/orientation layout folders? Some layouts could either simply not have the button or have it hidden by default.

Re-using Layouts with include

Providing Alternative Resources

Robert Nekic
  • 3,087
  • 3
  • 24
  • 36
  • the question is about one attribute in one layout file – ben75 Jun 12 '14 at 15:32
  • The question is about how to display a control for some resolutions and orientations but not others. By the docs, that should be handled with resolution and/or orientation-specific layouts and possibly the include to streamline it a bit. – Robert Nekic Jun 12 '14 at 15:59