android fragment


转自:http://www.aiuxian.com/article/p-359900.html

请转原文学习:Using Fragmenys in Android - Tutorial

where I found this resource:干干货分享——Android开发中学习资源大集合(译)

很素的翻译开始:

2. Fragments

2.1. 什么是fragments?

fragments是一个能够应用于Activity中的独立组件,他封装了功能,所以在activitys和layouts中更容易被复用。

fragment在activity的上下文环境中运行,但是他有自己的生命周期和自己的用户界面,定义一个fragmengts,我们也可以不需要用户界面,也就是无头fragments(headless fragments).

fragments可以被动态或者静态地添加到一个Activity中。

2.2. 使用fragments的好处

Fragments使得在不同的布局文件中复用组件变得更容易,你可以给手机构建单窗格布局,给平板构建多窗格布局。这并不仅限于用在平板上;例如,你也可以在智能手机上使用fragments去支持横屏和竖屏的布局。

一个典型的例子就是在Activity中项目(items)列表。在平板电脑上,如果你点某个列表项,就能立即在同屏幕右侧看到对应的详细内容(detail),而在智能手机上,你要跳转到一个新的详情页面(detail),下面是图形描述:

 

下面的讨论将假定你有两个fragments(main和detail),但是你也可以有更多个。我们也会有一个,main activity和一个detailed activity。在平板电脑上main activity包含了这两个Fragments,而在手机上只包含了main fragments。

下面的屏幕截图展示了这种用法:

2.3. 如何使用Fragments

用Fragments创建不同的布局,我们可以:

  •  用一个activity,在平板上显示两个Fragments,在手机上显示一个fragemtns。在这种情况下,必要的时候要转换Fragments。这要求the fragment不能在布局文件layout file中被声明,同样地Fragments不能在运行时被移除。
  • 在手机上使用不同的activities来 host每个fragment。例如,当平板上的UI实现是:在一个activity中使用了两个Fragments,在手机上使用同一个activity,但是提供另一种只包含一个fragment的布局。当你需要切换Fragments时, 需要调用另一个带有frgment的activity。(个人理解就是,在手机上一个activity用一个fragmen

第二种方法是最灵活,也是使用Fragments一般来说更可取的方法。在这种情况下,main activity检测如果detail fragment在布局文件可用。如果存在detailed fragment,the main activity通知fragment该更新自己的fragment。如果没有detail fragment, the main activity 启动 detailed activity。

3. Fragments 的生命周期

fragment的生命周期与持有他的activity的生命周期相关联

 

表一. Fragment生命周期

MethodDescription
onAttach()fragemnt实例关联到Activity实例,此时这个activity还未完全初始化完
onCreate()Fragment被创建
onCreateView()fragment实例创建自己的view层级(fragment第一次话他自己的用户界面时调用)the inflated view成为activity view层级的一部分
onActivityCreated()Activity和fragment的view层级创建完的同时他们的实例也已经创建完成。此时,view可以通过findviewById方法被找到
onResume()Fragment可见并且是激活状态
onPause()Fragment可见但非激活状态,例如,另一个activity覆盖在带有fragment的activity上面
onStop()Fragment不可见

4. 定义和使用Fragments

4.1. 定义fragments

定义一个新的fragment,你可以继承android.app.Fragment 类或他的一个子类,例如 ListFragment, DialogFragment, PreferenceFragment或者WebViewFragment。下面的代码是一个实现的例子:

01 package com.example.android.rssfeed;
02  
03 import android.app.Fragment;
04 import android.os.Bundle;
05 import android.view.LayoutInflater;
06 import android.view.View;
07 import android.view.ViewGroup;
08 import android.widget.TextView;
09  
10 public class DetailFragment extends Fragment {
11  
12   @Override
13   public View onCreateView(LayoutInflater inflater, ViewGroup container,
14       Bundle savedInstanceState) {
15     View view = inflater.inflate(R.layout.fragment_rssitem_detail,
16         container, false);
17     return view;
18   }
19  
20   public void setText(String item) {
21     TextView view = (TextView) getView().findViewById(R.id.detailsText);
22     view.setText(item);
23   }
24 }

4.2. 静态添加fragments

要使用一个新的fragment,可以静态地将他添加到一个XML布局文件中

你可以使用FragmentManager类来检查布局文件是否包含了这个fragment

1 DetailFragment fragment = (DetailFragment) getFragmentManager().
2    findFragmentById(R.id.detail_frag);
3 if (fragment==null || ! fragment.isInLayout()) {
4   // start new Activity
5   }
6 else {
7   fragment.update(...);
8 }
如果一个fragment在XML布局文件里已经定义了, 他的android:name属性会指向相对应的类。

4.3 Fragment生命周期

fragment有他自己的生命周期。但总是和持有他的activity的生命周期相关联。

fragment的onCreate()方法在activity的onCreate()方法之后,该fragment的onCreateView()方法之前被调用。

当fragment开始创建他的用户界面时,系统就调用onCreateView(),在该方法里你可以通过Inflator类的对象调用inflate()方法来inflate一个布局文件,该布局文件作为inflate方法的一个参数。对于headless fragments没有必要实现该方法。

当持有该fragment的activity被创建后,onActivityCreated()方法在onCreateView()方法之后被调用。在这你可以初始化一个需要Context对象的对象。

Fragment并不是Context的子类,所以你必须通过getActivity()方法来获得父activity

一旦fragment可见,onStart()方法就会被调用

如果一个activity停止了,它的fragment也会停止;如果一个activity被销毁,它的fragment也会被销毁。

4.4 fragments之间的通讯

为了增加fragments的复用,fragment和fragment不应该直接和彼此沟通,fragemnts之间的每次沟通都应该通过持有他们的activity来完成。

为了达到该目的,fragment应该定义一个内部接口,然后要求activity必须实现该接口。该方法可以避免fragment对该activity的有任何了解(不懂怎么翻译,原文:This way you avoid that the fragment has any knowledge about the activity which uses it)。在fragment的onAttach()方法中,可以检查activity是否正确实现了该接口。

例如,假定你有一个fragment,该fragemnt要将一个值传递给他的父activity。可以通过下面的方法来实现。

01 package com.example.android.rssfeed;
02  
03 import android.app.Activity;
04 import android.app.Fragment;
05 import android.os.Bundle;
06 import android.view.LayoutInflater;
07 import android.view.View;
08 import android.view.ViewGroup;
09 import android.widget.Button;
10  
11 public class MyListFragment extends Fragment {
12  
13   private OnItemSelectedListener listener;
14  
15   @Override
16   public View onCreateView(LayoutInflater inflater, ViewGroup container,
17       Bundle savedInstanceState) {
18     View view = inflater.inflate(R.layout.fragment_rsslist_overview,
19         container, false);
20     Button button = (Button) view.findViewById(R.id.button1);
21     button.setOnClickListener(new View.OnClickListener() {
22       @Override
23       public void onClick(View v) {
24         updateDetail();
25       }
26     });
27     return view;
28   }
29  
30   public interface OnItemSelectedListener {
31     public void onRssItemSelected(String link);
32   }
33  
34   @Override
35   public void onAttach(Activity activity) {
36     super.onAttach(activity);
37     if (activity instanceof OnItemSelectedListener) {
38       listener = (OnItemSelectedListener) activity;
39     else {
40       throw new ClassCastException(activity.toString()
41           " must implemenet MyListFragment.OnItemSelectedListener");
42     }
43   }
44  
45   @Override
46   public void onDetach() {
47     super.onDetach();
48     listener = null;
49   }
50  
51   // may also be triggered from the Activity
52   public void updateDetail() {
53     // create a string just for testing
54     String newTime = String.valueOf(System.currentTimeMillis());
55  
56     // inform the Activity about the change based
57     // interface defintion
58     listener.onRssItemSelected(newTime);
59   }
60 }

5. 在fragments中存储(持久化)数据

5.1 应用程序重启时保存数据

在fragments中你也需要存储你的应用数据。你可以把数据存储到一个中央地区。例如:

  • SQLite database
  • File
  • 应用对象,在应用需要去处理存储的情况下

5.2配置变化时持久化数据

如果想要在配置变化时保持数据,你可以使用应用对象(application object)。

除此之外,还可以在fragments里调用setRetainState(true)方法,这种方法在配置变化时会保持fragment的实例,但是只有在fragment未加入到回退栈时才起作用。这种方式并不被Google提倡用在有用户界面的fragment上。在这种情况下,数据必须存为成员变量。

如果要被保存的数据由Bundle类支持,则可以使用onSaveInstanceState()方法将数据存放到Bundle,再在onActivityCreated()方法中找回数据。

6. 运行时修改Fragments

FragmentManager类和FragmentTransaction类允许你在activity的布局中添加,删除和替换fragments。

Fragments可以通过transaction来动态修改。要动态将fragments添加到现有的布局中,通常在要添加Fragment的XML布局文件中定义一个container(这个container就相当于在xml文件中的LinearLayout(or other layout)用来装载fragment),你可以使用一个FrameLayout 元素。

1 FragmentTransaction ft = getFragmentManager().beginTransaction();
2 ft.replace(R.id.your_placehodler, new YourFragment());
3 ft.commit();
一个新的Fragment将会替换现有的Fragment,这个现有的Fragment就是先前被添加到container里的fragment。 
如果你想要把transaction添加到Android的回退栈中,你可以使用addToBackStack()方法。这会使该操作添加到activity的历史栈中,通过返回按钮就可以恢复Fragment的变化。

7. Fragment transition的动画

在fragment的事物期间,可以定义动画,该动画基于Property Animation API来使用,通过调用setCustomAnimations()方法。

也可以通过调用setTransition()方法使用多种Android提供的基础动画。这些通过以FragmentTransaction为首的内容来定义。TRANSIT_FRAGMENT_*.

两种方式都允许你定义一个实体动画和一个现有动画。

8. 将Fragment transition添加到回退栈中

你可以添加一个FragmentTransition到回退栈中以便用户可以使用回退按钮倒退到这个转变。

使用FragmentTransition对象中的addToBackStack来实现。

9. Fragments的后台处理

9.1 无界面的Fragments

使用Fragments,可以不需要用户界面。

实现一个无界面的fragment,只需要在fragment里的onCreateView()方法中返回null即可。

Tip:建议在使用无界面的fragment来处理后台操作时结合setRetainInstance()方法来使用,通过这种方式,在异步操作的时候就无需处理配置变化了(configuration changes)

9.2 无界面fragments处理配置变化

无界面的fragment通常用于封装一些配置变化的状态或者用于一个后台处理任务。因此,你应该设置无界面的fragment处于被保持(retained)的状态。一个被保持的fragment在配置变化期间不会被销毁。

Retained headless fragment

设置fragment为被保持,调用setRetainInsatance()方法即可。

你可以使用FragmentManager类的add()方法来添加这样的fragment到activity中。如果之后你还会查找到该Fragment,你需要给这个fragment添加一个标签tag,这样就可以使用FragmentManager中的findFragmentByTag()方法来查找到它。

Warging:onRetainNonConfigurationInstance()已废弃,should be replaced by retained headless fragments。

10. Fragments 教程

10.1 概览

接下来的教程说明了怎样使用fragment。应用将会根据横屏模式和竖屏模式分别使用带有fragment的不同布局。

在竖屏模式下,RssfeedActivity会显示一个Fragment。在这个fragment中用户可以跳转到另外一个带有fragment的activity。

在横屏模式下,RssfeedActivity会并列的显示两个fragments

1. 创建项目

按照下列内容创建一个新的Android Project。


Table 2. Android project

Property Value
Application NameRSS Reader
Project Namecom.example.android.rssfeed
Package namecom.example.android.rssfeed
TemplateBlankActivity
ActivityRssfeedActivity
Layoutactivity_rssfeed


10.2 创建一个标准的layouts

在res/layout文件夹下创建或者改变以下布局文件。

创建一个新的布局文件:fragment_rssitem_detail.xml.该布局文件被DetailFragment使用。

01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="match_parent"
04     android:layout_height="match_parent"
05     android:orientation="vertical" >
06  
07     <TextView
08         android:id="@+id/detailsText"
09         android:layout_width="wrap_content"
10         android:layout_height="match_parent"
11         android:layout_gravity="center_horizontal|center_vertical"
12         android:layout_marginTop="20dip"
13         android:text="Default Text"
14         android:textAppearance="?android:attr/textAppearanceLarge"
15         android:textSize="30dip" />
16  
17 </LinearLayout>

创建一个新的布局文件:fragment_rsslist_overview.xml,该布局文件用于MyListFragment类。

01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="match_parent"
04     android:layout_height="match_parent"
05     android:orientation="vertical" >
06  
07     <Button
08         android:id="@+id/button1"
09         android:layout_width="wrap_content"
10         android:layout_height="wrap_content"
11         android:text="Press to update"
12          />
13  
14 </LinearLayout>
修改现有的activity_rssfeed.xml文件。该布局是RssfeedActivity默认的布局文件,她展示两个fragments。

01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="fill_parent"
04     android:layout_height="fill_parent"
05     android:orientation="horizontal" >
06  
07     <fragment
08         android:id="@+id/listFragment"
09         android:layout_width="0dp"
10         android:layout_weight="1"
11         android:layout_height="match_parent"
12         android:layout_marginTop="?android:attr/actionBarSize"
13         class="com.example.android.rssfeed.MyListFragment" ></fragment>
14  
15     <fragment
16         android:id="@+id/detailFragment"
17         android:layout_width="0dp"
18         android:layout_weight="2"
19         android:layout_height="match_parent"
20         class="com.example.android.rssfeed.DetailFragment" >
21         <!-- Preview: layout=@layout/details -->
22     </fragment>
23  
24 </LinearLayout>

10.3 创建Fragment类

接下来要创建Fragment类,由DetailFragment类开始

01 package com.example.android.rssfeed;
02  
03 import android.app.Fragment;
04 import android.os.Bundle;
05 import android.view.LayoutInflater;
06 import android.view.View;
07 import android.view.ViewGroup;
08 import android.widget.TextView;
09  
10 public class DetailFragment extends Fragment {
11  
12   @Override
13   public View onCreateView(LayoutInflater inflater, ViewGroup container,
14       Bundle savedInstanceState) {
15     View view = inflater.inflate(R.layout.fragment_rssitem_detail,
16         container, false);
17     return view;
18   }
19  
20   public void setText(String item) {
21     TextView view = (TextView) getView().findViewById(R.id.detailsText);
22     view.setText(item);
23   }
24 }

创建MyListFragment类,先不管这个类的名字,他不会像他的命名一样展示一个列表,先用一个Button来代替,点击该按钮会将当前时间传递给details fragemnt。

01 package com.example.android.rssfeed;
02  
03 import android.app.Activity;
04 import android.app.Fragment;
05 import android.os.Bundle;
06 import android.view.LayoutInflater;
07 import android.view.View;
08 import android.view.ViewGroup;
09 import android.widget.Button;
10  
11 public class MyListFragment extends Fragment {
12    
13   private OnItemSelectedListener listener;
14    
15   @Override
16   public View onCreateView(LayoutInflater inflater, ViewGroup container,
17       Bundle savedInstanceState) {
18     View view = inflater.inflate(R.layout.fragment_rsslist_overview,
19         container, false);
20     Button button = (Button) view.findViewById(R.id.button1);
21     button.setOnClickListener(new View.OnClickListener() {
22       @Override
23       public void onClick(View v) {
24         updateDetail();
25       }
26     });
27     return view;
28   }
29  
30   public interface OnItemSelectedListener {
31       public void onRssItemSelected(String link);
32     }
33    
34   @Override
35     public void onAttach(Activity activity) {
36       super.onAttach(activity);
37       if (activity instanceof OnItemSelectedListener) {
38         listener = (OnItemSelectedListener) activity;
39       else {
40         throw new ClassCastException(activity.toString()
41             " must implemenet MyListFragment.OnItemSelectedListener");
42       }
43     }
44    
45    
46   // May also be triggered from the Activity
47   public void updateDetail() {
48     // create fake data
49     String newTime = String.valueOf(System.currentTimeMillis());
50     // Send data to Activity
51     listener.onRssItemSelected(newTime);
52   }
53 }

10.4 RssfeedActivity

按照下面的代码来修改RssfeedActivity。

01 package com.example.android.rssfeed;
02  
03 import android.os.Bundle;
04 import android.app.Activity;
05 import android.view.Menu;
06  
07 public class RssfeedActivity extends Activity implementsMyListFragment.OnItemSelectedListener{
08  
09     @Override
10     protected void onCreate(Bundle savedInstanceState) {
11         super.onCreate(savedInstanceState);
12         setContentView(R.layout.activity_rssfeed);
13     }
14  
15     // if the wizard generated an onCreateOptionsMenu you can delete
16     // it, not needed for this tutorial
17  
18   @Override
19   public void onRssItemSelected(String link) {
20     DetailFragment fragment = (DetailFragment) getFragmentManager()
21             .findFragmentById(R.id.detailFragment);
22         if (fragment != null && fragment.isInLayout()) {
23           fragment.setText(link);
24         }
25   }
26      
27 }

10.5 运行

运行该例子。在横屏模式或者竖屏模式都会显示两个fragments。如果点击了ListFragment中的按钮,DetailFragment上的当前时间会被更新。

11. Fragments 教程-竖屏模式的布局

11.1 为竖屏模式创建布局文件

RssfeedActivity在竖屏模式下应该使用特定的布局文件。在竖屏模式Android会检查有没有layout-port文件夹,该文件夹下是否有合适的布局文件。如果Android没有找到符合的布局文件,他就会去使用layout文件夹。

因此我们要创建res/layout-port文件夹。之后在res/layout-port文件夹下创建activity_rssfeed.xml布局文件。

01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="match_parent"
04     android:layout_height="match_parent"
05     android:orientation="horizontal" >
06  
07     <fragment
08         android:id="@+id/listFragment"
09         android:layout_width="match_parent"
10         android:layout_height="match_parent"
11         android:layout_marginTop="?android:attr/actionBarSize"
12         class="com.example.android.rssfeed.MyListFragment" />
13 </LinearLayout>

继续创建activity_detail.xml。该布局文件用户DetailActivity。注意我们本可以在res/layout文件夹下创建该文件,但是它只用于竖屏模式下,因此我们把它放到这个文件夹下(res/layout-port)。

01 <?xml version="1.0" encoding="utf-8"?>
02 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03     android:layout_width="match_parent"
04     android:layout_height="match_parent"
05     android:orientation="vertical" >
06  
07     <fragment
08         android:id="@+id/detailFragment"
09         android:layout_width="match_parent"
10         android:layout_height="match_parent"
11         class="com.example.android.rssfeed.DetailFragment" />
12  
13 </LinearLayout>

11.2 DetailActivity

按照下面的类,重新创建DetailActivity

01 package com.example.android.rssfeed;
02  
03 import android.app.Activity;
04 import android.content.res.Configuration;
05 import android.os.Bundle;
06 import android.widget.TextView;
07  
08 public class DetailActivity extends Activity {
09    
10   public static final String EXTRA_URL = "url";
11    
12   @Override
13   protected void onCreate(Bundle savedInstanceState) {
14     super.onCreate(savedInstanceState);
15  
16     // Need to check if Activity has been switched to landscape mode
17     // If yes, finished and go back to the start Activity
18     if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
19       finish();
20       return;
21     }
22     setContentView(R.layout.activity_detail);
23     Bundle extras = getIntent().getExtras();
24     if (extras != null) {
25       String s = extras.getString(EXTRA_URL);
26       TextView view = (TextView) findViewById(R.id.detailsText);
27       view.setText(s);
28     }
29   }
30 }

11.3 调整RssfeedActivity

调整RssfeedActivity类来显示DetailActivity以防另一个fragment不在该布局内

01 package com.example.android.rssfeed;
02  
03 import android.app.Activity;
04 import android.content.Intent;
05 import android.os.Bundle;
06 import android.view.Menu;
07  
08 public class RssfeedActivity extends Activity implements
09     MyListFragment.OnItemSelectedListener {
10  
11   @Override
12   protected void onCreate(Bundle savedInstanceState) {
13     super.onCreate(savedInstanceState);
14     setContentView(R.layout.activity_rssfeed);
15   }
16  
17   @Override
18   public void onRssItemSelected(String link) {
19     DetailFragment fragment = (DetailFragment) getFragmentManager()
20         .findFragmentById(R.id.detailFragment);
21     if (fragment != null && fragment.isInLayout()) {
22       fragment.setText(link);
23     else {
24       Intent intent = new Intent(getApplicationContext(),
25           DetailActivity.class);
26       intent.putExtra(DetailActivity.EXTRA_URL, link);
27       startActivity(intent);
28  
29     }
30   }
31  
32 }

11.4 运行

运行你的例子。如果在竖屏模式下运行应用,你应该只看到一个Fragment,使用Ctrl+F11快捷键来改变方向。在横向模式下你会看到两个fragments。如果你点击竖屏模式的按钮,新的DetailActivity会被重启,并且显示当前的时间。在横屏模式下两个fragment都会被看到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值