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; } ... }