如何在运行时检测横向版本的布局?

时间:2013-03-06 20:51:44

标签: android android-layout android-resources

我到处搜索,根本无法找到任何方法:

在我的项目中,我有两个用于布局的资源文件夹:一个叫做“layout”,另一个叫做“layout-land”。这是根据当前方向为每个Activity使用两个单独布局的常见方法。问题是我没有为每个Activity设置单独的“布局 - 土地”布局,并且并非所有活动甚至都需要不同的横向模式布局。

我所做的是覆盖默认onConfigurationChanged以防止方向更改发生(使用清单中的相应configChanges="orientation")。每次用户倾斜屏幕时,这给我带来了很多令人头疼的问题。但是,在某些情况下,我实际上希望方向更改发生。每当“layout-land”文件夹中有相应的布局时,我希望方向可以像往常一样改变。如果没有,我希望onConfigurationChanged抑制方向更改事件。

我创建了一个扩展BaseActivity的父Activity类,它是我所有活动的基类,并且覆盖了onConfigurationChanged

@Override
public void onConfigurationChanged(android.content.res.Configuration newConfig)
{
    super.onConfigurationChanged(newConfig);

    if(...there is a layout-land version of the current activity's layout...)
    {
        setRequestedOrientation(newConfig.orientation);
    }
    else
    {
        // Do nothing, since raising the orientation change event
        // to change from a portrait layout to the same portrait layout is silly.
    }
}

唯一的障碍是我找不到任何方法来确定我的布局资源是否有布局版本。我已经知道当前布局的资源ID(通过覆盖BaseActivity在我的setContentView类中的其他地方获得),但布局的横向版本和肖像版本共享相同的ID,我看到了Resources对象没有简单的方法告诉我当我通过id询问特定资源时它给我的布局版本。必须有办法做到这一点。我错过了什么?

2 个答案:

答案 0 :(得分:1)

好吧,所以我找到了一个解决方案,尽管是一个可怜的,黑客攻击的解决方案。我不推荐这个黑客,因为它是由撒旦制成的:

您可以从活动的Resources方法获取的getResources()对象本身不会告诉您是否存在资源的“布局 - 土地”版本。但是,您可以通过调用AssetManager方法获取Resources对象在内部使用的getAssets()对象。

AssetManager对象有一个名为openNonAssetFd的方法,它将尝试获取res文件夹中特定文件的文件描述符。不幸的是,即使文件存在,大多数(如果不是全部)您尝试使用此方法打开的内容都会抛出FileNotFoundException

因此,例如,如果您有一个名为res/layout-land/settings_menu.xml的文件,那么它将抛出FileNotFoundException并带有以下消息:“此文件无法作为文件描述符打开;它可能已被压缩”。这是Android人员的糟糕设计,因为从技术上讲,文件就在那里,抛出的异常至少应该是不同的类型。

然而,更重要的是,为了检测仅存在layout-land布局,我们可以依赖于抛出异常的返回消息的差异。这就是黑客:

如果文件存在,则异常消息将是“此文件无法作为文件描述符打开;它可能已被压缩”。

如果文件不存在,则异常消息将与您传递给openNonAssetFd方法的参数相同(即,它将是不存在的资源的文件名)。

现在,通过这个,我们可以制作一个类似的方法:

protected boolean landscapeLayoutExists(int layoutId)
{
// Since we're always passing in an id of a layout, this will always correspond
// to a layout file in any of the layout folders (either layout, or both layout and layout-land, in my case).
String rawName = this.getResources().getResourceEntryName(layoutId);
String testPath = "res/layout-land/" + rawName + ".xml";

try
{
    this.getResources().getAssets().openNonAssetFd(testPath);
}
catch(Exception ex)
{
    if(ex.getMessage().equals(testPath))
        return false;
    else
        return true;
}   

// Somehow, we managed to grab the file descriptor successfully.  
// In my experience, this cannot happen with layouts, but if it ever does,
// then we definitely know the file exists.
return true;
}

通过使用此方法,我现在可以确定“layout-land”文件夹中是否存在当前活动布局的版本,并采取相应措施。

然而,我对这种方法不满意,虽然它确实有效(至少现在,至少在我测试过的Galaxy S3上)。如果有人有更好的,请告诉我。

答案 1 :(得分:1)

在layout_land文件夹的布局中,为最外面的FrameLayout添加一个id = layout_name_of_this_file。 例如

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_name_of_this_file"
    .....

并在您的代码中

getResources().getIdentifier("layout_name_of_this_file", "id", "com.example....")) = resource id of the button (a pretty long number)

getResources().getIdentifier("layout_name_of_the_layout_file_exist_in_layout_but_not_in_layout_land", "id", "com.example....")) = 0  

因为没有带此ID的FrameLayout。