您的位置:首页 > 产品设计 > UI/UE

Android doc |Getting Started|部分 --Building a Flexible UI(部分翻译)

2016-09-10 16:36 447 查看

Building a Dynamic UI with Fragments

To create a dynamic and multi-pane user interface on Android, you need to encapsulate UI components and activity behaviors into modules that you can swap into and out of your activities. You can create these modules with the Fragment class, which behaves somewhat like a nested activity that can define its own layout and manage its own lifecycle.

要在Android创建一个动态的多屏用户界面,你需要将UI组件和Activity的行为压缩到模型中以便你可以在你的activity中切换。你可以使用Fragment类创建这些模型,Fragment就像是嵌套的activity,他可以定义自己的布局管理自己的生命周期。

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指定自己的布局,在一个activity中,他可以和其他Fragment配置成不同的组合,以修改你的布局配置来适配不同尺寸的屏幕。(一个小屏设备也许一次只能显示一个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.

这节课展示如何用Fragment创建一个动态用户界面并提高不同尺寸屏幕上你的app的用户体验,所有支持运行的设备版本至少是Android1.6.(Fragment 最早在Android 3.0.x API11中新增)。

Creating a 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.

你可以把Fragment想象成一个模块化的activity,他有自己的生命周期,接收自己的键盘输入事件,在你的activity还在运行时你可以把Fragment移除(有几分像一个你可以在不同的activity重用的“子activity”),本节课向你展示如何使用支持类库继承Fragment,而且你的app在Android1.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类库。另外,在你的activity你也可以导入v7应用程序兼容类库,v7类库在Android2.1(API 等级 7)且也包含

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,既存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:

与创建Activity的一个不同点是你必须使用onCreateView()的回调来定义你的布局。实际上,这是你需要的惟一的回调来让Fragment运行。例如,这里有一个简单的Fragment,并且定义了他的布局

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().

就像一个Activity,一个Fragment应该继承其他生命周期回调,这会允许你在Fragment被添加到Activity或者从Activity移除的时候已经Activity之间切换的时候管理Fragment的生命周期。举例:当一个Activity的onPause()方法被调用,任何在该Activity的Fragment都会受到onPause的回调。

More information about the fragment lifecycle and callback methods is available in the Fragments developer guide.

更多关于Fragment的生命周期和回调方法参见Fragments开发指南

Add a Fragment to an Activity using 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.

Fragment是可重用的,模块化UI组件,每个Fragment类的实例必须关联一个父类FragmentActivity。你可以实现这种关联,通过在你的Activity XML布局文件定义每个Fragment。

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是一个特殊的Activity,他被提供,来提供支持类库处理系统版本低于API等级低于11的设备上的Fragment。

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).

例子:添加两个Fragment到一个Activity的布局文件,当屏幕是大屏的时候(定义在layout-large目录中)。

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.

贴士:更多关于创建不同屏幕布局的信息,参见Supporting Different Screen Sizes。

Then apply the layout to your activity:

将布局文件应用到你的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应用程序兼容性库,你的Activity应继承AppCompatActivity,AppCompatActivity是FragmentActivity的子类。更多信息参见Adding the App Bar

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文件添加一个Fragment到Activity布局,你无法在Activity运行中将Fragment移除。如果你打算在用户操作中切换Fragment,你必须在Activity初次创建的时候将Fragment添加到Activity,也就是下节课要讲的。

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.

在设计支持各种屏幕尺寸的应用时,你可以在不同的布局配置中重复使用 Fragment ,以便根据相应的屏幕空间提供更出色的用户体验。

例如,一次只显示一个 Fragment 可能就很适合手机这种单窗格界面,但在平板电脑上,你可能需要设置并列的 Fragment,因为平板电脑的屏幕尺寸较宽阔,可向用户显示更多信息。



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: 两个 Fragment,显示在不同尺寸屏幕上同一 Activity 的不同配置中。在较宽阔的屏幕上,两个 Fragment 可并列显示;在手机上,一次只能显示一个 Fragment,因此必须在用户导航时更换 Fragment。

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 类提供的方法,你可以在运行时添加、移除和替换 Activity 中的 Fragment,以便为用户提供一种动态体验。

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.

你可以在 Activity 运行时向其添加 Fragment,而不用像上一课中介绍的那样,使用 元素在布局文件中为 Activity 定义 Fragment。如果你打算在 Activity 运行周期内更改 Fragment,就必须这样做。

要执行添加或移除 Fragment 等事务,你必须使用 FragmentManager 创建一个 FragmentTransaction,后者可提供用于执行添加、移除、替换以及其他 Fragment 事务的 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.

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.

如果 Activity 中的 Fragment 可以移除和替换,你应在调用 Activity 的 onCreate() 方法期间为 Activity 添加初始 Fragment(s)。

在处理 Fragment(特别是在运行时添加的 Fragment )时,请谨记以下重要规则:必须在布局中为 Fragment 提供 View 容器,以便保存 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.

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.

下面是上一课所示布局的替代布局,这种布局一次只会显示一个 Fragment。要用一个 Fragment 替换另一个 Fragment, Activity 的布局中需要包含一个作为 Fragment 容器的空 FrameLayout。

请注意,该文件名与上一课中布局文件的名称相同,但布局目录没有 large 这一限定符。因此,此布局会在设备屏幕小于“large”的情况下使用,原因是尺寸较小的屏幕不适合同时显示两个 Fragment。

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.

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().

For example, here’s how to add a fragment to the previous layout:

在 Activity 内部,使用 Support Library API 调用 getSupportFragmentManager() 以获取 FragmentManager,然后调用 beginTransaction() 创建 FragmentTransaction,同时调用 add() 添加 Fragment。

你可以使用同一个 FragmentTransaction 对 Activity 执行多 Fragment 事务。当你准备好进行更改时,必须调用 commit()。

例如,下面介绍了如何为上述布局添加 Fragment :

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.

由于该 Fragment 已在运行时添加到 FrameLayout 容器中,而不是在 Activity 布局中通过
<fragment>
元素进行定义,因此该 Activity 可以移除和替换这个 Fragment 。

Replace One Fragment with Another

The procedure to replace a fragment is similar to adding one, but requires the replace() method instead of 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.

替换 Fragment 的步骤与添加 Fragment 的步骤相似,但需要调用 replace() 方法,而非 add()。

请注意,当你执行替换或移除 Fragment 等 Fragment 事务时,最好能让用户向后导航和“撤消”所做更改。要通过 Fragment 事务允许用户向后导航,你必须调用 addToBackStack(),然后再执行 FragmentTransaction。

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.

注意:当你移除或替换 Fragment 并向返回堆栈添加事务时,已移除的 Fragment 会停止(而不是销毁)。如果用户向后导航,还原该 Fragment,它会重新启动。如果你没有向返回堆栈添加事务,那么该 Fragment 在移除或替换时就会被销毁。

Example of replacing one fragment with another:

替换 Fragment 的示例:

// 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 API 执行高级 Fragment 操作,否则无需使用此名称。

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.

为了重用FragmentUI组件,你应该创建的时候就应该让他们保持完全独立和模块化,定义他们自己的布局和行为。一旦你定义了这些可重用的Fragments,你可以使用Activity关联到他们,用应用的逻辑与他们通信。

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.

通常你会希望一个Fragment与另一个Fragment通信,比如说根据用户事件改变内容。所有Fragment间的通信都是通过Activity关系起来的。两个Fragments永远不应该直接通信。

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.

为了允许Fragment和Activity通信,你可以在Fragment类定义一个接口并在Activity实现它。当Fragment执行到他的onAttach生命周期时,Fragment将会捕获这个接口的实现,然后就可以通过接口方法与Activity通信。

Here is an example of Fragment to Activity communication:

下面是一个Activity与Fragment通信的例子:

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.

现在Fragment可以通过调用OnHeadlineSelectedListener接口的实例:mCallback的onArticleSelected方法(或者接口中的其他方法)去向Activity发送messages

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.

举个例子,当用户点击list选项时,下面的Fragment的方法将会被调用。Fragment利用回调接口传递这个用户操作事件给父类Activity。

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}


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.

为了接受来自Fragment的回调事,宿主Activity必须实现定义在Fragment类里的方法。

For example, the following activity implements the interface from the above example.

举个例子,下面的Activity时序了上面例子中Fragment的接口

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


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.

宿主Activity通过findFragmentById拿到Fragment实例,可以传递messages给Fragment,然后调用Fragment的公共方法

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:

比如,想象一下 显示着的Activity也许包含另一个Fragment,该Fragment显示着item,item数据从上面的回调方法中取得。这种情况下,Activity可以传递从回调方法中接收的信息到其他Fragment,以显示具体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();
}
}
}


Next class
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android