Skip to content

Most visited

Recently visited

navigation

实现自适应 UI 流

UI 流可能视您的应用当前显示的布局而有所不同。例如,如果您的应用处于双窗格模式,点击左侧窗格中的某个项目会直接在右侧窗格中显示内容;如果是处于单窗格模式,内容应该会独立显示(在不同的 Activity 中)。

确定当前布局

由于您对每个布局的实现都略有差异,您需要优先完成的一项工作可能是确定用户目前查看的布局。 例如,您可能想了解用户是处于“单窗格”模式还是“双窗格”模式。 您可以通过查询给定视图是否存在并且是否可见来实现此目的:

public class NewsReaderActivity extends FragmentActivity {
    boolean mIsDualPane;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);

        View articleView = findViewById(R.id.article);
        mIsDualPane = articleView != null &&
                        articleView.getVisibility() == View.VISIBLE;
    }
}

请注意,此代码查询“article”窗格是否可用,这种方法要比针对特定布局对查询进行硬编码灵活得多。

还有一个示例可以说明如何适应不同组件的存在,其内容是检查组件是否存在,然后再对它们执行操作。 例如,在 News Reader 示例应用中,有一个用于打开菜单的按钮,但该按钮只在运行的版本低于 Android 3.0 时才存在(因为从 API 级别 11 开始,该功能已被 ActionBar 取代)。因此,如需为此按钮添加事件侦听器,您可以这样做:

Button catButton = (Button) findViewById(R.id.categorybutton);
OnClickListener listener = /* create your listener here */;
if (catButton != null) {
    catButton.setOnClickListener(listener);
}

根据当前布局作出反应

某些操作可能视当前布局而有不同的结果。例如,在 News Reader 示例应用中,如果 UI 处于双窗格模式,则点击标题列表中的某个标题会在右侧窗格中打开该文章,但如果 UI 处于单窗格模式,则会启动不同的 Activity:

@Override
public void onHeadlineSelected(int index) {
    mArtIndex = index;
    if (mIsDualPane) {
        /* display article on the right pane */
        mArticleFragment.displayArticle(mCurrentCat.getArticle(index));
    } else {
        /* start a separate activity */
        Intent intent = new Intent(this, ArticleActivity.class);
        intent.putExtra("catIndex", mCatIndex);
        intent.putExtra("artIndex", index);
        startActivity(intent);
    }
}

同样,如果应用处于双窗格模式,其设置的操作栏应包含用于导航的标签;而如果应用处于单窗格模式,则应设置具有微调框小部件的导航。 因此您的代码还应检查哪一种情况适当:

final String CATEGORIES[] = { "Top Stories", "Politics", "Economy", "Technology" };

public void onCreate(Bundle savedInstanceState) {
    ....
    if (mIsDualPane) {
        /* use tabs for navigation */
        actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_TABS);
        int i;
        for (i = 0; i < CATEGORIES.length; i++) {
            actionBar.addTab(actionBar.newTab().setText(
                CATEGORIES[i]).setTabListener(handler));
        }
        actionBar.setSelectedNavigationItem(selTab);
    }
    else {
        /* use list navigation (spinner) */
        actionBar.setNavigationMode(android.app.ActionBar.NAVIGATION_MODE_LIST);
        SpinnerAdapter adap = new ArrayAdapter(this,
                R.layout.headline_item, CATEGORIES);
        actionBar.setListNavigationCallbacks(adap, handler);
    }
}

在其他 Activity 中重复使用 Fragment

在面向多种屏幕的设计中采用的一种固定模式是,让界面的某一部分在一些屏幕配置下以窗格形式实现,在其他配置下则以一个单独 Activity 的形式实现。 例如,在 News Reader 示例应用中,新闻文章文字在较大屏幕上显示在右侧窗格中,但在较小屏幕上则显示在一个单独的 Activity 内。

在这类情况下,您通常可以通过在几个 Activity 中重复使用同一 Fragment 子类来避免代码重复。例如,双窗格布局中使用了 ArticleFragment

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <fragment android:id="@+id/headlines"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.HeadlinesFragment"
              android:layout_width="400dp"
              android:layout_marginRight="10dp"/>
    <fragment android:id="@+id/article"
              android:layout_height="fill_parent"
              android:name="com.example.android.newsreader.ArticleFragment"
              android:layout_width="fill_parent" />
</LinearLayout>

并在适用于较小屏幕的 Activity 布局 (ArticleActivity) 中重复使用(无布局):

ArticleFragment frag = new ArticleFragment();
getSupportFragmentManager().beginTransaction().add(android.R.id.content, frag).commit();

当然,这与在 XML 布局中声明 Fragment 的效果相同,但在此情况下,XML 布局是无用功,因为文章 Fragment 是该 Activity 的唯一组件。

设计 Fragment 时需要牢记的一个要点是不要创建与特定 Activity 的强耦合。 为此,您通常可以定义一个界面,将 Fragment 与其宿主 Activity 进行交互时需要使用的所有方式抽象化,然后宿主 Activity 会实现该界面:

例如,News Reader 应用的 HeadlinesFragment 发挥的就是这个作用:

public class HeadlinesFragment extends ListFragment {
    ...
    OnHeadlineSelectedListener mHeadlineSelectedListener = null;

    /* Must be implemented by host activity */
    public interface OnHeadlineSelectedListener {
        public void onHeadlineSelected(int index);
    }
    ...

    public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener listener) {
        mHeadlineSelectedListener = listener;
    }
}

然后,当用户选择某个标题时,Fragment 便会通知宿主 Activity 指定的侦听器(而不是通知特定硬编码 Activity):

public class HeadlinesFragment extends ListFragment {
    ...
    @Override
    public void onItemClick(AdapterView<?> parent,
                            View view, int position, long id) {
        if (null != mHeadlineSelectedListener) {
            mHeadlineSelectedListener.onHeadlineSelected(position);
        }
    }
    ...
}

支持平板电脑和手机指南中将进一步阐述此技巧。

处理屏幕配置变更

如果您要使用不同的 Activity 来实现界面的不同部分,您需要牢记的是,可能需要对某些配置变更(如旋转变化)作出反应,以便保持界面的一致性。

例如,在一台运行 Android 3.0 或更高版本的典型 7 英寸平板电脑上,当平板电脑在纵向模式下运行时,News Reader 示例应用使用单独的 Activity 来显示新闻文章,但在横向模式下则使用双窗格布局。

这意味着当用户处于纵向模式,并且用于查看文章的 Activity 位于屏幕上时,您需要检测屏幕方向已变为横向模式的情况并作出相应的反应:结束该 Activity 并返回主 Activity,以便内容可以显示在双窗格布局中:

public class ArticleActivity extends FragmentActivity {
    int mCatIndex, mArtIndex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mCatIndex = getIntent().getExtras().getInt("catIndex", 0);
        mArtIndex = getIntent().getExtras().getInt("artIndex", 0);

        // If should be in two-pane mode, finish to return to main activity
        if (getResources().getBoolean(R.bool.has_two_panes)) {
            finish();
            return;
        }
        ...
}
This site uses cookies to store your preferences for site-specific language and display options.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Follow Google Developers on WeChat

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a short survey?
Help us improve the Android developer experience.
(Sep 2017 survey)