[Android] 使用Fragment创建动态UI

来源:互联网 发布:爱思助手mac版 编辑:程序博客网 时间:2024/05/13 03:00

使用Fragment创建动态UI

要在Android上面创建一个动态的、多面板的UI,你需要将这些UI组件和activity的行为封装成模块(modules),这样就可以在你的activity中进行交互。你可以使用Fragment类来创建这些模块,这个类的行为有点像是嵌套的activity,可以定义自己的布局(layout)以及管理它自己的生命周期。

当一个fragment指定了它的布局,它就可以和其他的fragment组成不同的组合放在activity里,当运行在屏幕尺寸不同的device上时可以修改布局配置(一个小屏幕可能同时只能显示一个fragment,而一个大屏幕可能可以显示两个或更多)。

这节课来介绍的是使用fragment创建动态的UI并提升你的app在不同尺寸的设备上的用户体验,所有内容都继续支持那些还运行在Android 1.6上点设备。

创建一个Fragment

你可以把Fragment想成是activity的一个模块化的部分,有它自己的生命周期,可以接收输入事件,并且在activity运行的时候也可以进行添加或移除的动作(有点像是“子activity”的感觉,可以在不同的activity中重复使用)。这节课介绍的是怎样使用Support Library继承(extend)fragment,这样你的app就算是在运行着Android 1.6的设备上也可以保持兼容。

注:如果你决定自己app的最小API level是11或者是更高的话,那么就不用再使用Support Library,取而代之地可以使用framework内建的Fragment类以及相关的API。请注意,这节课重点关注的是Support Library里的API,使用的是特定的包签名(package signature),可能和导入在platform中的版本在API名字上略有差异。

在开始这节课之前,你需要使用Support Library创建你的Android Project。如果你之前没有使用过Support Library,那就跟着Support Library Setup这篇文档(需要翻墙)使用v4 library建立你的project。你也可以使用v7 appcompat library在你的activity中引入app bar,这个库与Android 2.1(API level 7)兼容,并且也包含了Fragment API。

创建一个Fragment类

要创建一个fragment,先继承Fragment类,然后重写(override)关键的生命周期方法来插入你自己的代码逻辑,这和使用Activity类的方法很相似。

一个重要的区别是,在创建fragment时,你必须使用onCreateView() callback来定义布局。事实上,要让你的fragment运行起来,这个是你唯一需要的callback。举个例子,下面是一个简单的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);    }}

就像是activity一样,fragment也需要实现其他的生命周期callback,来允许你当它在activity中添加或移除时、或者是activity在它的生命周期转换时来管理自己的状态。例如当activity的onPause()方法被调用了之后,所有在这个activity中的fragment也会收到onPause()的调用。

更多关于fragment的生命周期及callback方法可以参考developer guide中的Fragments章节。

使用XML向Activity添加Fragment

由于fragment是可以重复使用的、模型化的UI组件,每一个Fragment类的实例(instance)都要与一个父FragmentActivity相关联。你可以通过在activity的布局XML文件中定义各个fragment来实现这种关联。

:FragmentActivity是一个特殊的activity,由Support Library提供,用来处理版本低于API level 11的系统中的fragment,如果你的app最低系统版本为API level 11或者更高,那么你就可以使用正常的Activity类。

如下的示例布局文件是在当device的屏幕被认为是“large”的时候向activity中添加两个fragment(在路径名中使用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>

提示:想要了解更多关于创造不同屏幕尺寸的布局,请阅读Support Different Screen Sizes。

然后在你的activity中使用这个布局:
译者注:文件位置为 java/com/example/android/fragments/MainActivity.java
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);    }}

如果你使用的是v7 appcompat library,你的MainActivity应该继承的是AppCompatActivity,这是FragmentActivity的一个子类。要获取更多信息,请阅读Adding the APP Bar。

:如果你在布局XML文件中定义了fragment来将它们添加到activity的布局,那么在运行时你将没办法将这些fragment移除。如果你想要在与用户交互时可以替换你的fragment,那么你必须让activity先开始,然后再添加fragment,即后续章节所讲。

创建一个灵活的UI

为了将你的应用设计成可以支持广泛的屏幕尺寸范围,你可以在不同的布局配置中重新使用你的fragment,基于现有的屏幕空间来提高用户体验。

例如,在单视图的手机上同时只显示一个fragment可能是更恰当的;相反的,你可能想要在平板上并排显示这些fragment,因为它有更大的屏幕尺寸,一个给用户显示更多的信息。



图1: 两个fragment,对于同一个activity在不同的屏幕尺寸上显示不同的配置。在大屏幕上,两个fragment可以并排摆放;然而在手机上,同一时间只有一个fragment是比较合适的,所以在用户操作时fragment之间会互相替换。

FragmentManager类提供了在运行时可以向activity添加、移除或替换fragment的方法,以实现动态的体验。

在运行时向Activity添加Fragment

与其在布局文件中为activity定义fragment(之前所提到的使用<fragment>元素),你可以在activity运行时向activity添加fragment。如果你打算在activity的生命周期中改变fragment,那么这种方法是必须的。

要执行一项事务(transcation),例如添加或移除fragment,你需要使用FragmentManager来创建一个FragmentTranscation,它提供了添加、移除、替换以及执行其他fragment事务的API。

如果你的activity允许fragment被移除或替换,那么你应该在activity 的onCreate()方法中向activity添加初始fragment(s)。

处理fragment时的一个非常重要的原则(特别时在运行时添加fragment)就是你的activity布局必须包含了一个View容器,可以将fragment插在里面。

下面的这种布局可以作为一种替代选择,替代之前所提到的在同一时间只能显示一个fragment的布局。为了使fragment能够被互相替换,activity的布局包含了一个空的Framelayout,这个framelayout扮演者fragment容器的角色。

要注意的是这个布局文件要与之前的layout文件同名,但是布局的路径并不含有large限定符,所以实际上这个布局是使用在设备的屏幕比large要小的时候,因为这时屏幕并不适合同时显示两个fragment。

res/layout/new_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" />

在你的activity类中,调用getSupportFragmentManager()使用Support Library APIs得到FragmentManager。然后调用beginTranscation()来创建一个FragmentTranscation,然后调用add()来添加一个fragment。

你可以使用同一个FragmentTranscation对activiy执行多个fragment事务。当你准备好要让这些更改生效时,你必须调用commit()。

下面这个例子介绍了如何向上面的这个布局添加fragment:
译者注:文件位置为 java/com/example/android/fragments/MainActivity.java
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();        }    }}

因为这个fragment是在运行时被添加到了Framelayout这个容器中(而不是在activity的布局中使用<fragment>元素),所以activity也可以将这个fragment移除,亦或者是使用其他的fragment来代替之前的这个。

替换Fragment

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

请记住,当你执行fragment事务,例如替换或移除fragment的时候,允许用户会退到之前并“取消”之前的改变往往是必要的。为了允许用户在fragment事务中操作回退,你需要在提交FragmentTranscation之前调用addToBackStack()。

注:当你移除或替换了一个fragment并且把事务添加到回退栈(back stack)时,这个被移除/替换的fragment处在停止(stopped)的状态,而不是销毁了(destoryed)的状态。如果用户操作回来并恢复这个fragment,它会重新开始(restart)。如果你没有把这个事务添加到回退栈的话,这个fragment在替换或移除时就会被销毁。

替换fragment的示例:
译者注:文件位置为 java/com/example/android/fragments/ArticleFragment.java
// code snippet// Create fragment and give it an argument specifying the article it should showArticleFragment 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 backtransaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);// Commit the transactiontransaction.commit();// code snippet

addToBackStack()方法有一个可选的字符串输入变量,可以指明唯一的事务的名字。除非你想要使用FragmentManager.BackStackEntry API执行高级的fragment操作,否则这个名字是不需要的(译者注:平常带null就好)。

与其他Fragment通信

为了能够重复使用fragment UI组件,你需要将每一个fragment构建成完全独立的、模块化的组件,并且定义有自己的布局及行为。一旦你定义好了这些可以重复使用的fragment,你就可以将它们关联到activity,使用你的应用逻辑连接它们,来实现一个整体复合的UI。

时常地你会想要让一个fragment可以与其他的fragment进行通信,例如基于用户事件来改变内容。所有的fragment到fragment的通信都是通过与之关联的activity来完成的。两个fragment之前永远都不能直接通信。

定义一个接口

为了允许fragment向它的activity通信,你可以在fragment类里定义一个接口(interface)并在activity里实现(implement)它。Fragment会在它生命周期的onAttach()方法中捕获到接口的实现,然后可以调用这个接口方法来和activity通信。

以下是fragment到activity通信的举例:
译者注:文件位置为 java/com/example/android/fragments/HeadlinesFragment.java
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");        }    }        // ...}

现在,通过调用onArticleSelected()方法(或者是接口里的其他方法),fragment可以通过使用OnHeadlineSelectedListener接口的实例mCallback向activity传递信息。

举个例子,当用户在列表项目上进行点击时,fragment里的如下方法就会被调用。Fragment使用callback接口向父activity传递事件。
译者注:文件位置为 java/com/example/android/fragments/HeadlinesFragment.java
// ...    @Override    public void onListItemClick(ListView l, View v, int position, long id) {        // Send the event to the host activity        mCallback.onArticleSelected(position);    }// ...

实现这个接口

为了从fragment接收事件回调,盛装fragment的activity比需要实现在fragment类中定义的借口。

例如,如下activity实现了上面例子中的借口。
译者注:文件位置为 java/com/example/android/fragments/MainActivity.java
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    }}

向Fragment传递信息

父activity可以通过findFragmentById()捕获Fragment实例来向fragment传递信息,然后可以直接调用fragment的公有方法。

举个例子:想象一下,上面的activity有可能包含了另外一个fragment,用来显示通过上面的callback方法返回的数据所指明的项目。在这种情况下,activity可以将从callback方法中得到的信息传递给另一个fragment,这个fragment将会显示那些项目:

译者注:文件位置为 java/com/example/android/fragments/MainActivity.java
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        // Capture the article fragment from the activity layout        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 {            // If the frag is not available, 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();        }    }}

原始地址

谷歌开发者training(需要翻墙):https://developer.android.com/training/basics/fragments/index.html


代码下载

http://download.csdn.net/detail/qiwenhe1990/9369199



0 0
原创粉丝点击