首次发表于依鹏csdn博客,转载请注明出处:http://blog.csdn.net/m0_37240709/article/details/77783767
Building a Dynamic UI with Fragments使用fragment构建动态用户界面
要在Android上创建一个动态和多窗格的用户界面,您需要将UI组件和活动行为封装到模块中,在活动中这些模块能够在使用和去除的状态间转换。您可以使用Fragment类创建这些模块,它的行为有点像被嵌套的活动,可以定义自己的布局和管理自己的生命周期。
When a fragment specifies its own layout, it can be configured in different combinations with other fragments inside an activity to modify your layout configuration for different screen sizes (a small screen might show one fragment at a time, but a large screen can show two or more).
当一个片段(fragment)指定它自己的布局时,他能够与活动中其他片段配置成不同的组合,为了不同的屏幕大小来修改您的布局配置 (一个小屏幕可能一次显示一个片段,但是一个大屏幕可以显示两个或更多)。
This class shows you how to create a dynamic user experience with fragments and optimize your app's user experience for devices with different screen sizes, all while continuing to support devices running versions as old as Android 1.6.
本节课程将向您展示如何使用片段创建动态用户体验,并为不同屏幕大小的设备优化应用程序的用户体验,同时继续支持最低运行版本Android 1.6的设备。
1、Creating a Fragment创建Fragment
You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running (sort of like a "sub activity" that you can reuse in different activities). This lesson shows how to extend the Fragment class using the Support Library so your app remains compatible with devices running system versions as low as Android 1.6.您可以将一个片段看作活动的模块化部分,它有自己的生命周期,接收它自己的输入事件,并且您可以在活动运行时添加或删除它(类似于您可以在不同活动中重用的“子活动”)。这个课程展示了如何使用支持库扩展片段类,这样你的应用程序就能与运行的系统版本为Android 1.6的设备保持兼容。
Before you begin this lesson, you must set up your Android project to use the Support Library. If you have not used the Support Library before, set up your project to use the v4 library by following the Support Library Setup document. However, you can also include the app bar in your activities by instead using the v7 appcompat library, which is compatible with Android 2.1 (API level 7) and also includes the Fragment APIs.
在开始这节课之前,你必须建立你的Android项目来使用支持库。如果您以前没有使用过支持库,那么可以通过支持库设置文档来设置您的项目来使用v4库。不过,您也可以在活动中包含应用程序栏,而不是使用v7 应用应用程序兼容库,该库与Android 2.1(API级别7)兼容,还包括Fragment API。
1.1、Create a Fragment Class创建一个片段类
To create a fragment, extend the Fragment class, then override key lifecycle methods to insert your app logic, similar to the way you would with an Activity class.要创建一个片段,继承Fragment类,然后覆盖关键的生命周期方法来插入您的应用程序逻辑,类似于您使用Activity类的方式。
One difference when creating a Fragment is that you must use the onCreateView() callback to define the layout. In fact, this is the only callback you need in order to get a fragment running. For example, here's a simple fragment that specifies its own layout:
创建片段时的一个区别是必须使用onCreateView()回调来定义布局。实际上,这是您需要的唯一回调,以便让片段运行。例如,这里有一个简单的片段,它指定了自己的布局:
import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.ViewGroup; public class ArticleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.article_view, container, false); } }
Just like an activity, a fragment should implement other lifecycle callbacks that allow you to manage its state as it is added or removed from the activity and as the activity transitions between its lifecycle states. For instance, when the activity's onPause() method is called, any fragments in the activity also receive a call to onPause().
就像一个活动一样,一个片断应该实现其他的生命周期回调,当它被添加或从活动中移除,以及它的生命周期状态之间的活动转换时,它允许您管理它的状态。例如,当调用活动的onPause()方法时,活动中的任何片段也会收到对onPause()的调用。
More information about the fragment lifecycle and callback methods is available in the Fragments developer guide.
有关片段生命周期和回调方法的更多信息可以在Fragments开发人员指南中找到。
1.2、Add a Fragment to an Activity using XML使用XML向活动添加一个片段
While fragments are reusable, modular UI components, each instance of a Fragment class must be associated with a parent FragmentActivity. You can achieve this association by defining each fragment within your activity layout XML file.虽然片段是可重用的、模块化的UI组件,但Fragment类的每个实例都必须与父类为FragmentActivity的活动相关联。您可以通过在活动布局XML文件中定义每个片段来实现这个关联。
Note: FragmentActivity is a special activity provided in the Support Library to handle fragments on system versions older than API level 11. If the lowest system version you support is API level 11 or higher, then you can use a regular Activity.
注意: FragmentActivity是支持库中提供的一种特殊活动,用于处理比API级别11更老的系统版本的片段。如果您支持的最低系统版本是API级别11或更高,那么您可以使用一个常规活动。
Here is an example layout file that adds two fragments to an activity when the device screen is considered "large" (specified by the large qualifier in the directory name).
下面是一个示例布局文件,当设备屏幕被认为是“大屏”(由目录名中的大型限定符指定)时,它将两个片段添加到一个活动中。
res/layout-large/news_articles.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <fragment android:name="com.example.android.fragments.HeadlinesFragment" android:id="@+id/headlines_fragment" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.android.fragments.ArticleFragment" android:id="@+id/article_fragment" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
Tip: For more about creating layouts for different screen sizes, read Supporting Different Screen Sizes. 提示:对于为不同的屏幕大小创建布局,请阅读支持不同的屏幕大小。
Then apply the layout to your activity:
然后把布局应用到你的活动中:
import android.os.Bundle; import android.support.v4.app.FragmentActivity; public class MainActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.news_articles); } }
If you're using the v7 appcompat library, your activity should instead extend AppCompatActivity, which is a subclass of FragmentActivity. For more information, read Adding the App Bar.
如果您使用的是v7 appcompat库,那么您的活动应该继承AppCompatActivity,它是FragmentActivity的一个子类。要了解更多信息,请阅读添加应用程序栏。
Note: When you add a fragment to an activity layout by defining the fragment in the layout XML file, you cannot remove the fragment at runtime. If you plan to swap your fragments in and out during user interaction, you must add the fragment to the activity when the activity first starts, as shown in the next lesson.
注意:当在布局XML文件中定义片段时,在活动布局中添加一个片段时,您不能在运行时删除片段。如果您计划在用户交互期间更换您的片段,那么您必须在活动开始时将片段添加到活动中,如下一节所示。
2、Building a Flexible UI建立一个灵活的用户界面
When designing your application to support a wide range of screen sizes, you can reuse your fragments in different layout configurations to optimize the user experience based on the available screen space.在设计应用程序以支持大屏幕时,您可以在不同的布局配置中重用您的片段,以优化基于可用屏幕空间的用户体验。
For example, on a handset device it might be appropriate to display just one fragment at a time for a single-pane user interface. Conversely, you may want to set fragments side-by-side on a tablet which has a wider screen size to display more information to the user.
例如,在一个手持设备上,一个单独的窗格用户界面一次只显示一个片段是合适的。但相反,您可能希望将一些片段并排放在一个具有更宽屏幕大小的平板上,以便向用户显示更多信息。
Figure 1. Two fragments, displayed in different configurations for the same activity on different screen sizes. On a large screen, both fragments fit side by side, but on a handset device, only one fragment fits at a time so the fragments must replace each other as the user navigates.
图1所示。两个片段显示在不同的配置中,在不同的屏幕大小上显示相同的活动。在大屏幕上,两个片段都是并排的,但在手机设备上,同一时间只适合于显示一个片段,所以当用户导航时,片段必须相互替换。
The FragmentManager class provides methods that allow you to add, remove, and replace fragments to an activity at runtime in order to create a dynamic experience.
FragmentManager类提供了一些方法,允许您在活动运行时将片段添加、删除和替换,以创建动态体验。
2.1、Add a Fragment to an Activity at Runtime在运行时向活动添加一个片段
Rather than defining the fragments for an activity in the layout file—as shown in the previous lesson with the <fragment> element—you can add a fragment to the activity during the activity runtime. This is necessary if you plan to change fragments during the life of the activity.您可以在活动运行时向活动中添加一个片段,而不是在布局文件中定义一个活动的片段,就像上一节中所展示的那样,您可以在活动运行时添加一个片段。如果您计划在活动的生命周期中更改片段,那么这是必要的。
To perform a transaction such as add or remove a fragment, you must use the FragmentManager to create a FragmentTransaction, which provides APIs to add, remove, replace, and perform other fragment transactions.
要执行诸如添加或删除片段之类的事务,您必须使用FragmentManager来创建一个FragmentTransaction,它提供了添加、删除、替换和执行其他片段事务的api。
If your activity allows the fragments to be removed and replaced, you should add the initial fragment(s) to the activity during the activity's onCreate() method.
如果您的活动允许删除并替换片段,那么您应该在活动的onCreate()方法中添加初始片段(多个)到活动中。
An important rule when dealing with fragments—especially when adding fragments at runtime—is that your activity layout must include a container View in which you can insert the fragment.
处理片段时的一个重要规则——特别是在运行时添加片段时——您的活动布局必须包含一个容器视图,其中可以插入片段。
The following layout is an alternative to the layout shown in the previous lesson that shows only one fragment at a time. In order to replace one fragment with another, the activity's layout includes an empty FrameLayout that acts as the fragment container.
下面的布局是上一节中显示的布局的一种替代方法,它每次只显示一个片段。为了将一个片段替换为另一个片段,该活动的布局包括一个充当片段容器的空的FrameLayout。
Notice that the filename is the same as the layout file in the previous lesson, but the layout directory does not have the large qualifier, so this layout is used when the device screen is smaller than large because the screen does not fit both fragments at the same time.
请注意,文件名与上一节中的布局文件相同,但是布局目录没有大型限定符,所以当设备屏幕小于大屏幕时,这个布局就会被使用,因为屏幕不同时匹配两个片段。
res/layout/news_articles.xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="match_parent" />
Inside your activity, call getSupportFragmentManager() to get a FragmentManager using the Support Library APIs. Then call beginTransaction() to create a FragmentTransaction and call add() to add a fragment.
在你活动内,调用getSupportFragmentManager()获得FragmentManager使用支持库api。然后调用beginTransaction ()来创建一个FragmentTransaction,并调用add()来添加一个片段。
You can perform multiple fragment transaction for the activity using the same FragmentTransaction. When you're ready to make the changes, you must call commit().
您可以使用相同的FragmentTransaction来为活动执行多个分段事务。当您准备好进行更改时,您必须调用commit()。
For example, here's how to add a fragment to the previous layout:
例如,下面是如何在之前的布局中添加一个片段:
import android.os.Bundle; import android.support.v4.app.FragmentActivity; public class MainActivity extends FragmentActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.news_articles); // Check that the activity is using the layout version with // the fragment_container FrameLayout if (findViewById(R.id.fragment_container) != null) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else // we could end up with overlapping fragments. if (savedInstanceState != null) { return; } // Create a new Fragment to be placed in the activity layout HeadlinesFragment firstFragment = new HeadlinesFragment(); // In case this activity was started with special instructions from an // Intent, pass the Intent's extras to the fragment as arguments firstFragment.setArguments(getIntent().getExtras()); // Add the fragment to the 'fragment_container' FrameLayout getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, firstFragment).commit(); } } }
Because the fragment has been added to the FrameLayout container at runtime—instead of defining it in the activity's layout with a <fragment> element—the activity can remove the fragment and replace it with a different one.
因为片段已经在运行时被添加到FrameLayout的容器中,而不是在活动的布局中定义它的<fragment>元素——活动可以删除这个片段,并使用另一个元素来替换它。
2.2、Replace One Fragment with Another将一个片段替换为另一个片段
The procedure to replace a fragment is similar to adding one, but requires the replace() method instead of add().替换一个片段的过程类似于添加一个片段,但是需要replace ()方法而不是add()。
Keep in mind that when you perform fragment transactions, such as replace or remove one, it's often appropriate to allow the user to navigate backward and "undo" the change. To allow the user to navigate backward through the fragment transactions, you must call addToBackStack() before you commit the FragmentTransaction.
请记住,当您执行片段事务时,例如替换或删除一个片段,通常是允许用户向后导航并“撤消”更改。为了让用户能够在片段事务中向后导航,您必须在提交FragmentTransaction之前调用addToBackStack()。
Note: When you remove or replace a fragment and add the transaction to the back stack, the fragment that is removed is stopped (not destroyed). If the user navigates back to restore the fragment, it restarts. If you do not add the transaction to the back stack, then the fragment is destroyed when removed or replaced.
注意:当您删除或替换一个片段并将事务添加到后退堆栈时,被删除的片段将被停止(未被销毁)。如果用户导航回来恢复片段,它将重新启动。如果您不将事务添加到后退堆栈中,那么当删除或替换时,碎片将被销毁。
Example of replacing one fragment with another:
用另一个片段替换一个片段的例子:
// Create fragment and give it an argument specifying the article it should show ArticleFragment newFragment = new ArticleFragment(); Bundle args = new Bundle(); args.putInt(ArticleFragment.ARG_POSITION, position); newFragment.setArguments(args); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack so the user can navigate back transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); // Commit the transaction transaction.commit();
The addToBackStack() method takes an optional string parameter that specifies a unique name for the transaction. The name isn't needed unless you plan to perform advanced fragment operations using the FragmentManager.BackStackEntry APIs.
addToBackStack()方法接受一个可选的字符串参数,该参数指定事务的唯一名称。除非您计划使用FragmentManager.BackStackEntry APIs执行高级片段操作,否则这个名称是不需要的。
3、Communicating with Other Fragments与其他片段交互
In order to reuse the Fragment UI components, you should build each as a completely self-contained, modular component that defines its own layout and behavior. Once you have defined these reusable Fragments, you can associate them with an Activity and connect them with the application logic to realize the overall composite UI.为了重用片段UI组件,您应该将每个组件构建为一个完全自包含的、模块化的组件,它定义了它自己的布局和行为。一旦定义了这些可重用的片段,就可以将它们与活动关联起来,并将它们与应用程序逻辑连接起来,以实现总体的复合UI。
Often you will want one Fragment to communicate with another, for example to change the content based on a user event. All Fragment-to-Fragment communication is done through the associated Activity. Two Fragments should never communicate directly.
通常,您需要一个片段与另一个片段进行通信,例如根据用户事件更改内容。所有的片段与片段之间的通信都是通过相关的活动来完成的。两个片断不应该直接交流。
3.1、Define an Interface定义一个接口
To allow a Fragment to communicate up to its Activity, you can define an interface in the Fragment class and implement it within the Activity. The Fragment captures the interface implementation during its onAttach() lifecycle method and can then call the Interface methods in order to communicate with the Activity.为了允许一个片段与它的活动进行通信,您可以在片段类中定义一个接口,并在活动中实现它。片段在onAttach()生命周期方法中捕获接口实现,然后可以调用接口方法,以便与活动进行通信。
Here is an example of Fragment to Activity communication:
下面是一个关于活动通信的片段示例:
public class HeadlinesFragment extends ListFragment { OnHeadlineSelectedListener mCallback; // Container Activity must implement this interface public interface OnHeadlineSelectedListener { public void onArticleSelected(int position); } @Override public void onAttach(Activity activity) { super.onAttach(activity); // This makes sure that the container activity has implemented // the callback interface. If not, it throws an exception try { mCallback = (OnHeadlineSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnHeadlineSelectedListener"); } } ... }
Now the fragment can deliver messages to the activity by calling the onArticleSelected() method (or other methods in the interface) using the mCallback instance of the OnHeadlineSelectedListener interface.
现在片段可以传递消息给活动,通过调用onArticleSelected()方法(或其他接口中的方法)使用OnHeadlineSelectedListener接口的实例mCallback
For example, the following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
例如,当用户单击列表项时,将调用片段中的以下方法。片段使用回调接口将事件传递给父活动。
@Override public void onListItemClick(ListView l, View v, int position, long id) { // Send the event to the host activity mCallback.onArticleSelected(position); }
3.2、Implement the Interface实现该接口
In order to receive event callbacks from the fragment, the activity that hosts it must implement the interface defined in the fragment class.为了从片段中接收事件回调,托管它的活动必须实现片段类中定义的接口。
For example, the following activity implements the interface from the above example.
例如,下面的活动实现了上述示例的接口。
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... public void onArticleSelected(int position) { // The user selected the headline of an article from the HeadlinesFragment // Do something here to display that article } }
3.3、Deliver a Message to a Fragment将消息传递给一个片段
The host activity can deliver messages to a fragment by capturing the Fragment instance with findFragmentById(), then directly call the fragment's public methods.托管活动可以通过使用findFragmentById()捕获片段实例,然后直接调用片段的公共方法,从而将消息传递给一个片段。
For instance, imagine that the activity shown above may contain another fragment that's used to display the item specified by the data returned in the above callback method. In this case, the activity can pass the information received in the callback method to the other fragment that will display the item:
例如,假设上面显示的活动可能包含另一个片段,这个片段用于显示在上述回调方法中返回的数据所指定的项。在这种情况下,活动可以将回调方法中接收到的信息传递给另一个显示该条目的片段:
public static class MainActivity extends Activity implements HeadlinesFragment.OnHeadlineSelectedListener{ ... public void onArticleSelected(int position) { // The user selected the headline of an article from the HeadlinesFragment // Do something here to display that article ArticleFragment articleFrag = (ArticleFragment) getSupportFragmentManager().findFragmentById(R.id.article_fragment); if (articleFrag != null) { // If article frag is available, we're in two-pane layout... // Call a method in the ArticleFragment to update its content articleFrag.updateArticleView(position); } else { // Otherwise, we're in the one-pane layout and must swap frags... // Create fragment and give it an argument for the selected article ArticleFragment newFragment = new ArticleFragment(); Bundle args = new Bundle(); args.putInt(ArticleFragment.ARG_POSITION, position); newFragment.setArguments(args); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack so the user can navigate back transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); // Commit the transaction transaction.commit(); } } }