Android Fragment 详细翻译
来源:互联网 发布:淘宝图片设计软件 编辑:程序博客网 时间:2024/06/06 00:24
第一次发布翻译,自己翻译水平有限,若有错漏,一定多多指教,互相进步!
翻译原文网址:http://developer.android.com/guide/components/fragments.html
Fragment
一个Fragment代表着Activity的一种行为或其界面的一部分。你可以将多个Fragement组建在一个Activity来构建一个多窗口的UI,并且复用fragment在多个Activity中。你可以把fragment当作是Activity的一个模块,它拥有自己的生命周期,接收自己的输入事件。并且Activity可以在运行时添加或移除这个模块(类似于一个子类Activity,你可以复用在不同的Activity中)
public static class ExampleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.example_fragment, container, false); }}
ViewGroup
to be the parent of the inflated layout. Passing the container
is important in order for the system to apply layout parameters to the root view of the inflated layout, specified by the parent view in which it's going.<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /></LinearLayout>
在<fragment>中android:name属性指定Fragment类在布局中初始化。
当系统创建这个Activity布局,它初始化每个被指定的Fragment并调用它们的onCreateView()方法,以检索每个Fragment的布局。系统插入onCreateView()中返回的视图,这视图通过Fragment直接取代<fragment>元素。
注意:每个Fragment需要唯一标记,系统可以使用这个标记恢复指定的Fragmen,如果Activity重启(restart)(并且你可以使用这个标签捕获这个Fragment要执行的事务,比如移除它。)。这有三种方式给一个Fragment提供一个ID:
a.android:id属性指定一个唯一ID
b.android:tag属性指定一个唯一字符串
c.如果你没有提供以上两个,系统将使用容器视图的ID(the ID of the container view).
2.或者,在代码中添加Fragment到一个已存在的ViewGroup.
在你Activity运行的任意时刻,你都可以添加framents到你的Activity布局。你只需简单指定一个ViewGroup来显示Fragment。
在你的Activity提交一个Fragment事务(比如添加add,删除remove,或替代replace一个Fragment),你必须使用FragmentTransaction的APIs。你可以从你的Activity得到一个FragmentTransaction的实例。比如:
FragmentManager fragmentManager =getFragmentManager() ;
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
;
你可以添加一个Fragment使用add()方法,指定这个Fragment添加和插入的视图地方。例如:
ExampleFragment fragment = new ExampleFragment();fragmentTransaction.add(R.id.fragment_container, fragment);fragmentTransaction.commit();
add()第一个参数是ViewGroup,ViewGroup是Fragment显示的地方,ViewGroup被资源ID指定。第二个参数是要添加的Fragment。
一旦你通过FragmentTransaction做了些更改,你必须调用commit()使更改有效。
五、添加一个没有界面的Fragment
上述例子展示了如何添加一个Fragment到你的Activity以提供一个界面。但是,你也可以使用没有界面的Fragment给Activity提供一种后台行为。
添加一个无界面的Fragment,使用add(Fragment,String)(支持唯一的字符串“tag”对于Fragment而不是视图ID)添加Fragment到Activity。这就添加了Fragment,但是,因为它与Activity布局视图无关,它不会调用onCreateView().所以你不需要实现这个方法。
Fragment支持一个字符串标记tag不仅仅局限在无界面的Fragment—有界面的Fragment也支持—但是如果Fragment没有界面,字符串tag是辨识它的唯一方式。如果你后面想要从Activity得到Fragment,你需要使用findFragmentByTag()。
Activity使用没有界面的Fragment作为后台的工作者,请查看FragmentRetainInstance.java。在SDK例子中(可以通过SDK Manager获得),在你系统的路径:<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/
六、管理Fragment
在你的Activity管理Fragment,你需要使用FragmentManager。从你的Activity调用getFragmentManager()可以得到它。
你可以使用FragmentManager做一些事情:
1.通过findFragmentById()(对于在Activity布局中的有界面的Fragment)或findFragmentByTag()(对于没有界面的Fragment)得到Activity中存在的Fragment。
2.从回退栈中弹出Fragment,使用popBackStack()(模拟用户使用回退命令)
3.注册一个回退栈变化的监听器,使用addOnBackStackChangedListener()
关于这些方法更多的信息,查看FragmentManager类文档。
上一节中的演示,你也可以使用FragmentManager打开一个FragmentTransaction, FragmentTransaction允许你执行事务,比如添加和删除Fragment。
七、执行Fragment事务
在你的Activity中使用Fragment一个很好地特点是可以添加,删除,替换,并使用它们执行其他的动作,在界面上相应。你在Activity提交的一系列更改叫做事务,你可以通过使用FragmentTransaction的APIs使用。你也可以保存事务到Activity管理的回退栈中,允许用户通过Fragment的更改导航回退(就像Activity导航回退一样)。
你可以从FragmentManager获得FragmentTransaction的一个实例,例如:
FragmentManager fragmentManager =getFragmentManager()
;FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction()
;
每个事务是在同一时刻你想要执行的一组更改。你可以通过一个给定的事务装配你想要执行的所有的更改,使用add(),remove(),replace().然后应用事务到Activity,你必须使用commit().
但是在你commit()之前,为了添加事务到Fragment事务的回退栈中,你可能要调用addToBackStack().这个回退栈由Activity管理,并且允许用户通过返回键返回先前Fragment的状态。
这有个例子,演示如何用一个Fragment取代另一个,并且在回退栈中保存先前的状态:
// Create new fragment and transactionFragment newFragment = new ExampleFragment();FragmentTransaction transaction = getFragmentManager().beginTransaction();// Replace whatever is in the fragment_container view with this fragment,// and add the transaction to the back stacktransaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);// Commit the transactiontransaction.commit();
在这个例子中,newFragment替代任意当前在布局容器中R.id.fragment_container的Fragment(如果存在。不存在则直接添加)。通过调用addToBackStack(),这个替代动作的事务被保存在回退栈,所以用户可以回退这个事务,并且返回先前的Fragment通过回退按钮。
如果你添加多个更改到一个事务中(比如add()和remove()),并调用addToBackStack(),然后在你调用commit()之前所有被应用的更改被作为单个事务添加到回退栈中。现在按回退键将会一起返回这些更改。
添加更改操作到FragmentTransaction与顺序无关,除了:
1.你必须最后调用commit()
2.如果你添加多个Fragment到同一个容器container中,你添加它们的顺序决定了它们在视图层次显示的顺序。
当你执行一个移除Fragment的事务,如果你不调用addToBackStack(),这个Fragment会销毁(destroy)当提交后。并且用户不能返回它。然而,当你移除Fragment时,如果你调用addToBackStack(),这个Fragment会停止(stop生命周期),并且恢复(resume)如果用户返回。
提示:每个Fragment事务,你可以应用一个事务动画,在提交之前通过调用setTransition()。
调用commit()不会立即执行事务。相反,它安排事务到activity的UI线程(“主”线程),线程才会执行。但是,如果需要立即执行,你应该从UI线程调用executePendingTransactions(),为了立即执行通过commit()提交的事务。这种做法通常是不需要的,除非该事务是其他线程的附属工作。
注意:你可以使用commit()提交一个事务,仅当Activity保存它的状态(当用户离开Activity)之前。如果你在这之后想要提交,将会抛出异常。这是因为如果Activity需要恢复时,提交之后的状态会丢失。如果丢是也没关系,使用commitAllowingStateLoss().
public abstract int commitAllowingStateLoss()
八、与Activity通信Like
commit()
but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from it state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user.类似于commit(),但是允许在Activity的状态被保存之后提交执行。这是危险的,因为Activity往后需要从它的状态恢复时,之前的提交可能已丢失,所以只有对于用户界面状态意外改变也无所谓的情况下,你才应该使用commitAllowingStateLoss()方法。
虽然Fragment是独立于Activity的对象,并且可以在多个Activity中使用,一个给定的Fragment实例直接与包含它的Fragment相关联。尤其是,Fragment可以通过getActivity()对象访问Activity实例,并且执行简单的任务,比如在Activity布局中查找一个视图。View listView =getActivity()
.findViewById
(R.id.list);同样,通过从FragmentManager获取Fragment的索引,使用例如:findFragmentById()
或者findFragmentByTag(),
你的Activity也可以调用Fragment的方法。ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);九、创建Activity的回调事件在默写情况下,你可能需要一个Fragment与Activity共享事件。最好的方式是在Fragment中定义一个回调接口,并在宿主Activity实现它。当activity通过interface接收到一个回调, 必要时它可以和在layout中的其他fragment分享信息。例如, 如果一个新的应用在activity中有2个fragment – 一个用来显示文章列表(framgent A), 另一个显示文章内容(fragment B) – 然后 framgent A必须告诉activity何时一个list item被选中,然后它可以告诉fragmentB去显示文章。在这个例子中, OnArticleSelectedListener 接口在fragment A中声明:
public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ...}然后fragment的宿主activity实现 OnArticleSelectedListener 接口,并覆写 onArticleSelected() 来通知fragment B,从fragment A传来的事件。为了确保宿主activity实现这个接口, fragment A的 onAttach() 回调方法(当添加fragment到activity时由系统调用) 通过将作为参数传入onAttach()的Activity做类型转换来实例化一个OnArticleSelectedListener实例。代码如下:
public static class FragmentA extends ListFragment
{
OnArticleSelectedListener mListener;
//...
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Append the clicked item's row ID with the content provider Uri
Uri noteUri =ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
// Send the event and Uri to the host activity
mListener.onArticleSelected(noteUri);
//...
}
传给 onListItemClick() 的 id 参数是被点击的项的行ID,activity(或其他fragment)用来从应用的 ContentProvider 获取文章。
十、添加项目到ActionBar
你的fragment可以通过实现 onCreateOptionMenu() 提供菜单项给activity的选项菜单(以此类推, Action Bar也一样)。然而为了使这个方法接收调用,你必须在 onCreate() 期间调用 setHasOptionsMenu() 来指出fragment将会添加item到选项菜单(否则, fragment将接收不到对 onCreateOptionsMenu()的调用)。
随后从fragment添加到Option菜单的任何项,都会被追加到现有菜单项的后面。当一个菜单项被选择,fragment也会接收到 对 onOptionsItemSelected() 的回调。也可以在你的fragment layout中通过调用registerForContextMenu() 注册一个view来提供一个上下文菜单。当用户打开环境菜单,fragment接收到一个对 onCreateContextMenu() 的调用.当用户选择一个选项item时, fragment接收到一个对onContextItemSelected() 的调用。
注意: 尽管你的fragment会接收到它所添加的每一个菜单项被选择后的回调,但实际上当用户选择一个菜单项时,activity会首先接收到对应的回调。如果activity的on-item-selected回调函数实现并没有处理被选中的项目,然后事件才会被传递到fragment的回调。
这个规则适用于选项菜单和上下文菜单。
关于菜单更多的信息,查看 Menus and Action Bar 指导手册。
图3. Fragment生命周期中宿主Activity生命周期的影响
管理Fragment的生命周期很像管理Activity的生命周期。就像Activity,Fragment可以存在三种状态:
1.Resumed:fragment在运行中的Activity是可视的。
2.Paused:另一个Activity处于前台并且取得焦点,但是这个Fragment的宿主Activity仍然可见(前台Activity是部分透明或者没有完全覆盖屏幕)。
3.Stopped:Fragment不可见。要么是宿主Activity已经停止stopped或者Fragment被移除但已添加到回退栈中。已停止stopped的Fragment仍然存活这(所有状态和成员信息被系统保持着)。然而,它对用户不再可见,并且如果宿主activity被干掉,其中的Fragment也会被干掉。
和activity一样, 你可以使用Bundle保持fragment的状态,万一activity的进程被干掉,并且当activity被重新创建的时候, 你需要恢复fragment的状态时就可以用到. 你可以在fragment的 onSaveInstanceState() 期间保存状态,并可以在 onCreate(),onCreateView() 或 onActivityCreated() 期间恢复它。
生命周期方面activity和fragment之间最重要的区别是各自如何在它的后台堆栈中储存。 在默认情况下,activity在停止后,它会被放到一个由系统管理的用于保存activity的后台堆栈。(因此用户可以使用BACK按键导航回退到它)。更多关于保存状态的信息请查看Activities文档。
Activity和Fragment最显著的不同是它们如何被存储在各自的回退栈中。当Activity停止时,Activity会默认被放置在由系统管理的Activities回退栈中(所以用户可以通过回退键导航回退到Activity,详情请看Tasks and Back Stack)。然而,仅当你在一个事务期间移除fragment时,显式调用addToBackStack()请求保存实例时,才被放到一个由宿主activity管理的后台堆栈。
然而,管理Fragment的生命周期非常类似于管理Activity的生命周期。所以, managing the activity lifecycle同样适用于Fragment。你需要理解的是,activity的生命如何影响fragment的生命。
注意:如果在你的Fragment中你需要一个Context对象,可以调用getActivity()。但是使用getActivity()要当心,只有当Fragment被绑定到Activity后(即onAttach())才能用。当Fragment没有绑定到Activity,或者Fragment生命周期末尾Fragment与Activity分离,getActivity()都会返回NULL。
Fragment有一些额外的生命周期回调方法,然而,为了处理像是创建和销毁fragment界面,它与activity进行独特的交互。这些额外的回调方法是:
- onAttach()
- 当fragment被绑定到activity时调用(Activity会被传入)。
- onCreateView()
- 创建与fragment相关的视图体系时被调用。
- onActivityCreated()
- 当activity的onCreate()函数返回时被调用。
- onDestroyView()
- 当与fragment关联的视图体系正被移除时被调用。
- onDetach()
- 当fragment正与activity解除关联时被调用。
fragment的生命周期流程实际上是受其宿主activity影响,如图3所示。在这 张图中,可以看到activity的每个连续状态是如何决定fragment可能接收到哪个回调函数的。例如,当activity接收到它的 onCreate()回调时,activity之中的fragment接收到的仅仅是onActivityCreated()回调。
一旦activity处于resumed状态,则可以在activity中自由的添加或者移除fragment。因此,只有当activity处于resumed状态时,fragment的生命周期才可以独立变化。
然而,当activity离开恢复状态时,fragment再一次被activity推入它的生命周期中。
范例
为了把这篇文档里的内容放到一起讨论,这里有一个使用两个fragment创建双区域布局的activity的范例。这个activity下面包含 的一个fragment用来显示莎士比亚戏剧列表,另一个fragment则用来在列表项被选中时显示该戏剧的概要。它也表明了如何基于不同屏幕配置提供 不同的fragment配置。
注意:这个activity的全部源代码可以在FragmentLayout.java获取。
在onCreate()期间,主activity用常用的方法应用了一个布局。
@Override protected 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类里是如何完成的。首先是显示莎士比亚戏剧标题的TitlesFragment。这个fragment继承于ListFragment,并且靠它处理大部分的列表视图工作。
就像你看到的这段代码,注意当用户点击一个列表项时会产生两个可能的行为:这依赖于两个布局哪个处于激活状态,它要么创建并显示一个新的 fragment以在同一个activity中显示概要内容(添加fragment到FrameLayout),要么启动一个新的activity(在 fragment可显示的地方)。
public static class TitlesFragment extends ListFragment { boolean mDualPane; int mCurCheckPosition = 0; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Populate list with our static array of titles. setListAdapter(new ArrayAdapter<String>(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); mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; if (savedInstanceState != null) { // Restore last state for checked position. mCurCheckPosition = savedInstanceState.getInt("curChoice", 0); } if (mDualPane) { // 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(mCurCheckPosition); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", mCurCheckPosition); } @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) { mCurCheckPosition = index; if (mDualPane) { // 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) getFragmentManager().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 = getFragmentManager().beginTransaction(); ft.replace(R.id.details, 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 // won't be displayed. Note this is not 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以显示列表项内容。
下面是DetailsActivity,它只是在屏幕纵向时,嵌入DetailFragment以显示被选中的戏剧概要。
public static class DetailsActivity extends Activity { @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()); getFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); } }}
请注意,如果配置是横向的,这个activity会结束掉自己,使主acitivity可以接管并在TitlesFragment旁显示 DetailsFragment。这种情况可能会发生在,用户在纵向时开启了DetailsActivity,但是之后旋转到横向(它重新启动了当前 activity)。
更多的关于使用fragment的例子(还有这个例子的完整源码),请参阅ApiDemos中的示例代码(从 Samples SDK component中下载可用的)。
- Android Fragment 详细翻译
- android fragment详细介绍
- android fragment详细介绍
- android fragment详细介绍
- Android Fragment 详细使用
- 【fragment】Android Fragments 详细使用
- android Fragment开发文档翻译 - 1
- android Fragment开发文档翻译 - 2
- Android中Fragment-SDK翻译版
- Android Api Component---翻译Fragment组件(一)
- Android Api Component---翻译Fragment组件(二)
- android Fragment的使用详细分析
- Android:fragment书上例子详细介绍
- Android Fragment和Activity详细解答
- 【Android官方文档】翻译Android官方文档-Fragment(四)
- android之Fragment(官网资料翻译)
- android之Fragment(官网资料翻译)
- Android之Fragment(官网资料翻译)
- tcp文件发送和接收-server端/windows
- 《Java实战开发经典》第五章5.3
- Android URI简介
- kvm虚拟机静态和动态迁移
- 解决MySQL不允许从远程访问的方法
- Android Fragment 详细翻译
- Android中RelativeLayout的属性详解
- 使用 GCD 自定义 UIImageView 增加异步下载图片的类别
- leetcode: (191) Number of 1 Bits
- 数组类型 多维参数
- hdu 1671
- Intent填坑
- A simple problem
- Android Studio调试功能使用总结【转】