如何在我的应用程序中检测Android选择了哪个布局?

72
假设我有一个活动,它在不同的资源文件夹中有三个不同的布局。例如:

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

在不同的设备和位置上,Android 会选择其中一个布局。如何通过编程找出选中了哪个布局?
Android 是否有任何 API 可以将这些布局返回给程序?

编辑: Graham Borland的解决方案在我评论中提到的某些情况下存在问题。


在您的设备上?还是XML布局? - Praveenkumar
我想在我的应用程序中以编程方式知道选择了哪个布局。 - Bobs
不介意我问一下,您为什么想以编程方式了解这个呢? - MrJre
2
好问题.. 这可以用于研究和理解Android布局选择.. - Ron
1
我很失望,你多次忽略了要求提供更多信息的请求,这些信息本可以帮助我诊断你在我的答案中发现的问题。 - Graham Borland
8个回答

74
你可以在每个不同的资源文件中设置不同的 android:tag 属性,并使用 View.getTag() 在运行时读取标记。

例如:

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") {
    ...
}

现在我有两个布局。1)layout2)layout-xlarge-land。对于第一个,标签是“layout”,对于第二个是“layout-xlarge-land”。当我在onCreate()方法中获取标签时,它会给我标签的真实值,如“layout-xlarge-land”。但是,当我在页面上单击按钮并在该按钮中使用findViewById()获取其标签时,其值是默认布局标签“layout”。为什么??? - Bobs
我在那个Activity中有一个tabHost。我注意到在行tabHost.addTab(myTabSpec);中,标签从“layout-xlarge-land”更改为“layout”了?为什么??? - Bobs
回答你的第一个问题,请发布您活动的onCreate()和按钮点击处理代码,以及布局文件的相关部分。 - Graham Borland
@breceivemail 我很愿意帮助解决你在这个方法中遇到的问题,但是你需要提供更多信息。请发布你的activity的onCreate()和按钮点击处理代码,以及布局文件的相关部分。 - Graham Borland
@breceivemail 我相信 TabHost 也使用了该标签,因此您的使用可能会与其冲突。请参见 http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.1_r1/android/widget/TabHost.java/ - Edward Dale

46
你可以为每个支持的配置创建一个名为values-<config>的目录。在这些目录中,创建一个包含单个selected_configuration字符串的strings.xml文件,该字符串描述当前的配置。在运行时,使用标准的getString方法获取该字符串,它会为你执行配置解析并返回正确的字符串。这尚未经过测试。

1
非常好的解决方案!!!我测试过了,它可以正常工作。它没有@Graham Borlans解决方案中发现的问题。 - Bobs
1
为了避免在每个values-<config>资源文件夹中复制所有strings.xml内容,您可以创建一个自定义文件,例如layout_type.xml,并仅将selected_configuration放入其中。 - Bobs
1
请问您实施了@breceivemail的时候是否遇到了这个问题:https://dev59.com/i2cs5IYBdhLWcg3wSiJo?lq=1。 - BrantApps
1
太棒了!好的解决方案。我在values-large-land目录下创建了一个strings.xml文件,只添加了一个名为“screen”的字符串资源,问题得到了解决。所有其他字符串在需要时都从主strings.xml中获取。完美。 - Herrera
1
简洁就是卓越!谢谢! - Tash Pemhiwa
@breceivemail,你能解释一下你的方法吗? - Henrique de Sousa

8

我已经尝试在Android源代码中找到它,但它是本地代码。 - Jin35
我认为这更可靠。你能给我们提供一些示例代码作为stackoverflow的参考吗? :) - Bobs
选择顺序在这里 http://developer.android.com/guide/topics/resources/providing-resources.html#table2 我认为所有这些修饰符都可以在 Context.getResources().getConfiguration() 中找到。 - Jin35
我更喜欢这种回答方式。但是它需要一个完整和适用的算法进行测试。因为@Graham的答案乍一看似乎很好,但在我的一些复杂活动中遇到了问题。 - Bobs
抱歉,我现在没有时间进行测试。如果我做了,我会在这里写下结果。 - Jin35

5

我的答案来自@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;
        }

这将在API 4级或更高级别中起作用。


现在我有两个布局。1)layout2)layout-xlarge-land。对于第一个,标签是“layout”,对于第二个是“layout-xlarge-land”。当我在onCreate()方法中获取标签时,它会给我标签的真实值,如“layout-xlarge-land”。但是,当我在页面上单击按钮并在该按钮中使用findViewById()获取其标签时,其值是默认布局标签“layout”。为什么??? - Bobs
我在那个Activity中有一个tabHost。我注意到在行tabHost.addTab(myTabSpec);中,标签从“layout-xlarge-land”更改为“layout”了?为什么??? - Bobs

3
您可以从Resources对象中获取有关屏幕方向和大小的信息。从那里,您可以了解使用哪种布局。 getResources().getConfiguration().orientation; - 返回Configuration.ORIENTATION_PORTRAITConfiguration.ORIENTATION_LANDSCAPEint size = getResources().getConfiguration().screenLayout; - 返回屏幕大小的掩码。您可以针对Small、Normal、Large、xLarge大小进行测试。例如:
if ((size & Configuration.SCREENLAYOUT_SIZE_XLARGE)==Configuration.SCREENLAYOUT_SIZE_XLARGE)

2
我假设您正在使用setContentView(int resID)来设置活动的内容。

方法1 (这是我的答案)

现在,在所有布局中,请确保根视图始终具有正确的标记:

例如:

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>

现在让您的活动扩展此活动:
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);
    }
}

在这种情况下,您可以使用resourceType来知道所使用的资源标识符是什么。

METHOD 2 (这是我的答案,但在发布之前,我想到了更好的方法)

现在,在所有布局中,请确保根视图始终具有正确的标记:

例如:

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>

现在让您的活动扩展此活动:
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);
            }
        }
    }
}

在这种情况下,你的层次结构中的所有视图将具有相同的标记。

你的解决方案存在问题。当屏幕旋转并且布局更改为纵向或横向时,你该怎么办? - Bobs
如果您当前的标签是“xlarge-land”,则旋转后的标签必须更改为“xlarge-port”。 - Bobs
是啊,那又怎样?这会改变(onCreate 方法将再次被调用并且标签将被设置)。 - Sherif elKhatib
你看了我在 @Graham 回答下的评论吗?我认为这些解决方案可能存在我在评论中提到的问题。 - Bobs
我会给你两个解决方案,它们有效。根据你的评论所述情况,布局正常,方法没有问题。尝试一下,如果不行的话,我很乐意帮助你 (: - Sherif elKhatib

1
我不知道确切的查找方法,但我们可以用不同的方式找到它。
在所有布局中添加一个TextView(可见性为隐藏)。根据需要指定xlarge、land、xlarge-land等值。
在程序中从TextView获取值。我们可以通过这种方式了解到。

1
看看 @Graham Borland 的答案。那是你的解决方案。还要看评论。他的方案在某些情况下存在问题。 - Bobs

0

你的问题与这个如何获取布局xml文件路径?相同。
你可以在xml中添加一个带有相应文件夹名称的隐藏文本视图,通过以下方式获取文本视图中的字符串:

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

确保所有文本视图的ID相同。

示例

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`

看@Graham Borland的答案,那是你的解决方案。并且看看评论。他的解决方案在某些情况下存在问题。 - Bobs
在这种方法中没有任何错误的余地,你将得到确切的值或字符串,如果你得到了其他字符串,那么就是启用了另一个布局。你在使用上述应用程序的10英寸平板电脑吗?要激活“layout-xlarge-land”,你应该在横屏模式下使用10英寸平板电脑,试一试并发布你的结果。 - VenomVendor
当您的Activity具有选项卡时,字符串的值会发生变化。 - Bobs
会尽快找到解决方案并发布。 - VenomVendor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接