正文
为将这周阐述的所有内容融会贯通,以下提供了一个示例,其中的 Activity 使用两个Fragment来创建一个双窗格布局。下面的 Activity 包括两个Fragment:一个用于显示莎士比亚戏剧标题列表,另一个用于在从列表中选定戏剧时显示其摘要。此外,它还展示了如何根据屏幕配置提供不同的Fragment配置。
注意:有关此 Activity 的完整源代码,可留言邮箱,该应用展示了示例 FragmentLayout
类的用法。
主 Activity 在 onCreate()
期间以常规方式应用布局:
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_layout);}
应用的布局为 fragment_layout.xml
:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment" android:id="@+id/titles" android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" /> <FrameLayout android:id="@+id/details" android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" android:background="?android:attr/detailsElementBackground" />LinearLayout>
通过使用此布局,系统会在 Activity 加载布局时立即实例化 TitlesFragment
(其列出了戏剧标题),
而 FrameLayout
(用于显示戏剧摘要的Fragment所在位置)则会占用屏幕右侧的空间,但最初处于空白状态。如下文所示,只有当用户从列表中选择某个项目后,系统才会将Fragment放入 FrameLayout
。
不过,并非所有屏幕配置都具有足够的宽度,可以一同显示戏剧列表和摘要。因此,以上布局仅用于横向屏幕配置(布局保存在 res/layout-land/fragment_layout.xml
中)。
因此,当屏幕纵向显示时,系统会应用以下布局(保存在 res/layout/fragment_layout.xml
中):
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment" android:id="@+id/titles" android:layout_width="match_parent" android:layout_height="match_parent" />FrameLayout>
此布局仅包括 TitlesFragment
。这意味着,当设备纵向显示时,用户只能看到戏剧标题列表。因此,当用户在此配置中点击某个列表项时,应用会启动一个新 Activity 来显示摘要,而非加载另一个Fragment。
接下来,您将了解如何在Fragment类中实现此目的。第一个Fragment是 TitlesFragment
,用于显示莎士比亚戏剧标题列表。该Fragment扩展了 ListFragment
,并依靠它来处理大多数列表视图工作。
当用户点击列表项时可能会出现两种行为:系统可能会创建并显示一个新Fragment,从而在同一 Activity 中显示详细信息(将Fragment添加到 FrameLayout
),也可能会启动一个新 Activity(在该 Activity 中可显示Fragment),具体取决于这两个布局中哪一个处于活动状态。
public static class TitlesFragment extends ListFragment { boolean dualPane; int curCheckPosition = 0; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Populate list with our static array of titles. setListAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES)); // Check to see if we have a frame in which to embed the details // fragment directly in the containing UI. View detailsFrame = getActivity().findViewById(R.id.details); dualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; if (savedInstanceState != null) { // Restore last state for checked position. curCheckPosition = savedInstanceState.getInt("curChoice", 0); } if (dualPane) { // In dual-pane mode, the list view highlights the selected item. getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); // Make sure our UI is in the correct state. showDetails(curCheckPosition); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", curCheckPosition); } @Override public void onListItemClick(ListView l, View v, int position, long id) { showDetails(position); } /** * Helper function to show the details of a selected item, either by * displaying a fragment in-place in the current UI, or starting a * whole new activity in which it is displayed. */ void showDetails(int index) { curCheckPosition = index; if (dualPane) { // We can display everything in-place with fragments, so update // the list to highlight the selected item and show the data. getListView().setItemChecked(index, true); // Check what fragment is currently shown, replace if needed. DetailsFragment details = (DetailsFragment) getSupportFragmentManager().findFragmentById(R.id.details); if (details == null || details.getShownIndex() != index) { // Make new fragment to show this selection. details = DetailsFragment.newInstance(index); // Execute a transaction, replacing any existing fragment // with this one inside the frame. FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if (index == 0) { ft.replace(R.id.details, details); } else { ft.replace(R.id.a_item, details); } ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.commit(); } } else { // Otherwise we need to launch a new activity to display // the dialog fragment with selected text. Intent intent = new Intent(); intent.setClass(getActivity(), DetailsActivity.class); intent.putExtra("index", index); startActivity(intent); } }}
第二个Fragment DetailsFragment
显示从 TitlesFragment
的列表中选择的项目的戏剧摘要:
public static class DetailsFragment extends Fragment { /** * Create a new instance of DetailsFragment, initialized to * show the text at 'index'. */ public static DetailsFragment newInstance(int index) { DetailsFragment f = new DetailsFragment(); // Supply index input as an argument. Bundle args = new Bundle(); args.putInt("index", index); f.setArguments(args); return f; } public int getShownIndex() { return getArguments().getInt("index", 0); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) { // We have different layouts, and in one of them this // fragment's containing frame doesn't exist. The fragment // may still be created from its saved state, but there is // no reason to try to create its view hierarchy because it // isn't displayed. Note this isn't needed -- we could just // run the code below, where we would create and return the // view hierarchy; it would just never be used. return null; } ScrollView scroller = new ScrollView(getActivity()); TextView text = new TextView(getActivity()); int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getActivity().getResources().getDisplayMetrics()); text.setPadding(padding, padding, padding, padding); scroller.addView(text); text.setText(Shakespeare.DIALOGUE[getShownIndex()]); return scroller; }}
从 TitlesFragment
类中重新调用,如果用户点击某个列表项,且当前布局不包括 R.id.details
视图(即 DetailsFragment
所属视图),则应用会启动 DetailsActivity
Activity 以显示该项目的内容。
以下是 DetailsActivity
,它通过简单嵌入 DetailsFragment
,在屏幕为纵向时显示所选的戏剧摘要:
public static class DetailsActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { // If the screen is now in landscape mode, we can show the // dialog in-line with the list so we don't need this activity. finish(); return; } if (savedInstanceState == null) { // During initial setup, plug in the details fragment. DetailsFragment details = new DetailsFragment(); details.setArguments(getIntent().getExtras()); getSupportFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); } }}
请注意,如果配置为横向,则此 Activity 会自行完成,这样主 Activity 便可接受并显示 TitlesFragment
和 DetailsFragment
。如果用户在纵向模式下启动 DetailsActivity
,但随后将设备旋转为横向(这会重启当前 Activity),则可能会出现这种情况。
好了,到今天为止,我们就把Fragment快速的深入了解一个一下。欢迎更多同学关注“计算机自学平台”来跟我共同探讨。