72

Assume I have an activity with three different layouts in different resource folders. For example:

layout-land/my_act.xml
layout-xlarge/my_act.xml
layout-xlarge-land/my_act.xml

In different devices and different positions one of them is selected by Android.
How can I find out which one is selected programmatically?

Does Android have any API that returns these layouts to the program?


Edit: Graham Borland's solution has a problem in some situations that I mentioned in the comments.

Community
  • 1
  • 1
Bob
  • 22,810
  • 38
  • 143
  • 225

8 Answers8

74

You can set a different android:tag attribute on the views in each different resource file, and read the tag back at runtime with View.getTag().

Example:

layout-xlarge-land/my_act.xml

<View
    android:id="@+id/mainview"
    android:tag="xlarge-landscape"
/>

layout-xlarge/my_act.xml

<View
    android:id="@+id/mainview"
    android:tag="xlarge-portrait"
/>

MyActivity.java

String tag = view.getTag();
if (tag.equals("xlarge-landscape") {
    ...
}
Bob
  • 22,810
  • 38
  • 143
  • 225
Graham Borland
  • 60,055
  • 21
  • 138
  • 179
  • Now I have 2 layouts. **1)layout** and **2)layout-xlarge-land**. for the first one, tag is "layout" and for the second "layout-xlarge-land". When I get tag in `onCreate()` method it gives me the true value of tag like "layout-xlarge-land". **But** when I click on a button in that page and in that button use `findViewById()` and get its tag, its value is default layout tag "layout". Why????? – Bob Jun 27 '12 at 06:37
  • I have a tabHost in that Activity. I noticed that in line `tabHost.addTab(myTabSpec);` the tag is changed from "layout-xlarge-land" to "layout"? why??? – Bob Jun 27 '12 at 07:10
  • To answer your first question, post your activity's onCreate() and your button click handling code, as well as the relevant sections of the layout files. – Graham Borland Jun 29 '12 at 09:29
  • @breceivemail I would like to help resolve the problems you have experienced with this approach, but you need to provide more information. Please post your activity's onCreate() and your button click handling code, as well as the relevant sections of the layout files. – Graham Borland Jul 24 '12 at 16:40
  • @breceivemail I believe the `TabHost` also uses the tag, so your usage might be colliding with it. See http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.1_r1/android/widget/TabHost.java/ – Edward Dale Jul 26 '12 at 13:28
46

You could create a values-<config> directory for each of your supported configurations. Inside of each of those directories, create a strings.xml with a single selected_configuration string which describes the current configuration. At runtime, fetch the string using the standard getString method, which will do the configuration resolution for you and return the correct string for the configuration. This is untested.

Edward Dale
  • 29,597
  • 13
  • 90
  • 129
  • 1
    Very good solution!!! I tested it and it works. It is free of problems found in @Graham Borlans's solution. – Bob Jul 27 '12 at 07:08
  • 1
    To avoid copying all of `strings.xml` contents for each `values-` resource folder, you can create a custom file like `layout_type.xml` and put only `selected_configuration` in it. – Bob Jul 27 '12 at 07:12
  • 1
    Please could you advise as to whether you experienced [this issue](http://stackoverflow.com/questions/12965002/android-resource-selection-layout-and-values-inconsistencies?lq=1) when you implemented this @breceivemail? – BrantApps Oct 21 '12 at 17:15
  • 1
    Amazing! Great solution. I created a strings.xml on my values-large-land directory, only added a single string resource called screen on it and solved the problem. All other strings are fetched from main strings.xml when needed. Perfect – Herrera Apr 05 '14 at 16:53
  • 1
    Simplicity is excellence! Thank you! – Tash Pemhiwa Jun 17 '16 at 05:44
  • @breceivemail can you explain your approach here? – Henrique de Sousa Aug 24 '16 at 15:52
8

You can try to repeat this algorithm "How Android Finds the Best-matching Resource" - it's quite simple, especially if you have different layouts only for different screens.

Jin35
  • 8,602
  • 3
  • 32
  • 52
  • I have tried to find it in android sources, but it is native code. – Jin35 Jul 04 '12 at 04:46
  • I think it is more reliable. Can you give us sample code to be a reference in stackoverflow? :) – Bob Jul 04 '12 at 04:49
  • Order for choosing is here http://developer.android.com/guide/topics/resources/providing-resources.html#table2 I suppose all this modificators can be find in `Context.getResources().getConfiguration()` – Jin35 Jul 04 '12 at 04:49
  • I prefer this approach as answer. But it needs a complete and applicable algorithm for test. Because @Graham's answer seems good at first look but it encountered problem in some of my complicated Activities. – Bob Jul 05 '12 at 06:18
  • Sorry, but I have not time now for tests. If i do it i'll write results here – Jin35 Jul 05 '12 at 07:05
5

My answer is implemented from @Graham Borland

 @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        DisplayMetrics metrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metrics);
        switch(metrics.densityDpi){
             case DisplayMetrics.DENSITY_LOW:

             if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
             {
               Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
               String tag = view.getTag();
               if (tag.equals("small-landscape") {
                .....
              }
             } 
            else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) 
            {
            Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
             String tag = view.getTag();
               if (tag.equals("small-potrait") {
                .....
              }
            }
            break;

             case DisplayMetrics.DENSITY_MEDIUM:

             if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
             {
               Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
               String tag = view.getTag();
               if (tag.equals("medium-landscape") {
                .....
              }
             } 
            else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) 
            {
            Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
             String tag = view.getTag();
               if (tag.equals("medium-potrait") {
                .....
              }
            }
             break;

             case DisplayMetrics.DENSITY_HIGH:

               if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
             {
               Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
               String tag = view.getTag();
               if (tag.equals("large-landscape") {
                .....
              }
             } 
            else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) 
            {
            Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
             String tag = view.getTag();
               if (tag.equals("large-potrait") {
                .....
              }
            }
             break;
        }

This will work in API lavel 4 or higher.

Dinesh
  • 6,500
  • 10
  • 42
  • 77
  • Now I have 2 layouts. **1)layout** and **2)layout-xlarge-land**. for the first one, tag is "layout" and for the second "layout-xlarge-land". When I get tag in `onCreate()` method it gives me the true value of tag like "layout-xlarge-land". **But** when I click on a button in that page and in that button use `findViewById()` and get its tag, its value is default layout tag "layout". Why????? – Bob Jun 27 '12 at 06:48
  • I have a tabHost in that Activity. I noticed that in line `tabHost.addTab(myTabSpec);` the tag is changed from "layout-xlarge-land" to "layout"? why??? – Bob Jun 27 '12 at 07:10
3

You can get info about screen orientation and size from Resources object. From there you can understand which layout is used.

getResources().getConfiguration().orientation; - returns either Configuration.ORIENTATION_PORTRAIT or Configuration.ORIENTATION_LANDSCAPE.

int size = getResources().getConfiguration().screenLayout; - returns mask of screen size. You can test against Small, Normal, Large, xLarge sizes. For example:

if ((size & Configuration.SCREENLAYOUT_SIZE_XLARGE)==Configuration.SCREENLAYOUT_SIZE_XLARGE)
sshturma
  • 1,111
  • 8
  • 7
2

I am assuming you are using setContentView(int resID) to set the content of your activities.


METHOD 1 (This is my answer)

Now in all your layouts make sure that the root view always has the right tag:

example:

layout-xlarge/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:tag="xlarge-landscape"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</LinearLayout>

layout-small/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:tag="small"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</LinearLayout>

Now let your activities extend this activity:

package shush.android.screendetection;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class SkeletonActivity extends Activity {

    protected String resourceType;

    @Override
    public void setContentView(int layoutResID) {
        LayoutInflater inflater = getLayoutInflater();
        View view = inflater.inflate(layoutResID, null);
        resourceType = (String)view.getTag();
        super.setContentView(view);
    }
}

In this case, you can use the resourceType to know what is the resource identifier used.


METHOD 2 (This was my answer but before posting I thought of the better one)

Now in all your layouts make sure that the root view always has the right tag:

example:

layout-xlarge/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:tag="xlarge-landscape"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</LinearLayout>

layout-small/main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:tag="small"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

</LinearLayout>

Now let your activities extend this activity:

package shush.android.screendetection;

import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class SkeletonActivity extends Activity {

    @Override
    public void setContentView(int layoutResID) {
        LayoutInflater inflater = getLayoutInflater();
        View view = inflater.inflate(layoutResID, null);
        fix(view, view.getTag());
        super.setContentView(view);
    }

    private void fix(View child, Object tag) {
        if (child == null)
            return;

        if (child instanceof ViewGroup) {
            fix((ViewGroup) child, tag);
        }
        else if (child != null) {
            child.setTag(tag);
        }
    }

    private void fix(ViewGroup parent, Object tag) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            View child = parent.getChildAt(i);
            if (child instanceof ViewGroup) {
                fix((ViewGroup) child, tag);
            } else {
                fix(child, tag);
            }
        }
    }
}

In this case all your views in your hierarchy will have the same tag.

Sherif elKhatib
  • 45,786
  • 16
  • 89
  • 106
  • Your solution has a problem. What do you do when your screen is rotated and your layout is changed to portrait or landscape? – Bob Jun 30 '12 at 05:17
  • for example if your current tag is "xlarge-land" after the rotation tag must be changed to "xlarge-port" – Bob Jun 30 '12 at 06:50
  • yeah so? it will change (onCreate will be called again and the tag will be set)\ – Sherif elKhatib Jun 30 '12 at 09:53
  • Have you read my comment on @Graham's answer? I think these solutions might have problems that I mentioned in comments. – Bob Jun 30 '12 at 16:01
  • I am giving you 2 solutions that work. And the situation your comment states implies that the layout is normal and there is nothing wrong in the approach. Try them and if they didn't work I will be glad to help (: – Sherif elKhatib Jun 30 '12 at 17:31
1

I dont know the exact way to find it. But we can find it in different way.

Add one textview in all the layouts.(visibility hidden). Assign values like xlarge, land, xlarge-land accordingly.

In program, get the value from textview. Somehow we can get to know like this.

Raghu Nagaraju
  • 3,278
  • 1
  • 18
  • 25
  • 1
    Look at @Graham Borland's answer. It is your solution. And look at comments. His solution has problems in some cases. – Bob Jul 25 '12 at 05:09
0

Your question is as same as this How to get layout xml file path?
You can add a Hidden Text View with corresponding Folder names in the xml Get the String in the text view by

TextView path = (TextView)findViewbyid(R.id.hiddentextview); 
 String s =  path.gettext().tostring();

Make sure that all the id's of the text view are same.

Example

if your xml is in `normal-mdpi` in hidden textview hard code `normal-mdpi`
if your xml is in `large-mdpi` in hidden textview hard code `large-mdpi`
Community
  • 1
  • 1
VenomVendor
  • 15,064
  • 13
  • 65
  • 96
  • Look at @Graham Borland's answer. It is your solution. And look at comments. His solution has problems in some cases. – Bob Jul 25 '12 at 05:10
  • There is no margin for error in this method, you will get the exact value or String, if your are getting the other String, then that layout is activated. Are you using the above app in 10" Tablet?? To activate "layout-xlarge-land" you should be using a 10" tablet in landscape mode, Try it & post your result. – VenomVendor Jul 25 '12 at 06:54
  • When your Activity has tabs, the value of string changes. – Bob Jul 25 '12 at 07:19
  • will find a solution & post it soon – VenomVendor Jul 25 '12 at 07:29