一、为什么要使用Fragment

1、当我们需要动态的多界面切换的时候,就需要将UI元素和Activity融合成一个模块。在2.3中我们一般通过各种Activity中进行跳转来实现多界面的跳转和单个界面动态改变。在4.0或以上系统中就可以使用新的特性来方便的达到这个效果--Fragment类。Fragment类似一个嵌套Activity,可以定义自己的layout和自己的生命周期。

2、 多个Fragment可以放在一个Activity中(所以上面讲到类似一个嵌套Activity),而这个类可以对这些Fragment进行配置以适应不同的屏幕尺寸(比如平板和手机)。

二、使用Fragment

1、Fragment 是 activity 的界面中的一部分或一种行为。可以把多个 Fragment 组合到一个 activity 中来创建一 个多面界面并且可以在多个 activity 中重用一个 Fragment。可以把 Fragment 认为模块化的一段 activity,它具 有自己的生命周期,接收它自己的事件,并可以在 activity 运行时被添加或删除。

2、Fragment 不能独立存在,它必须嵌入到 activity 中,而且 Fragment 的生命周期直接受所在的 activity 的影 响。

3、当向 activity 中添加一个 Fragment 时,它须置于 ViewGroup 控件中,并且需定义 Fragment 自己的界面。可 以在 layoutxml 文件中声明 Fragment,元素为:;也可以在代码中创建 Fragment,然后把它加入到 ViewGroup 控件中。然而,Fragment 不一定非要放在 activity 的界面中,它可以隐藏在后台为 actvitiy 工作。

三、 生命周期

通常, 应当至少实现如下的生命周期方法:

onCreate()
当创建fragment时, 系统调用该方法.

在实现代码中,应当初始化想要在fragment中保持的必要组件, 当fragment被暂停或者停止后可以恢复.

onCreateView()
fragment第一次绘制它的用户界面的时候, 系统会调用此方法. 为了绘制fragment的UI,此方法必须返回一个View, 这个view是你的fragment布局的根view. 如果fragment不提供UI, 可以返回null.

onPause()

用户将要离开fragment时,系统调用这个方法作为第一个指示(然而它不总是意味着fragment将被销毁.) 在当前用户会话结束之前,通常应当在这里提交任何应该持久化的变化(因为用户有可能不会返回).

大多数程序应最少对 fragment 实现这三个方法。当然还有其它几个回调方法可应该按情况实现之。
下图为 fragment 的生命周期(它所在的 activity 处于运行状态)。

四、如何使用Fragment

1、添加一个用户界面
fragment通常用来作为一个activity的用户界面的一部分,并将它的layout提供给activity.为了给一个fragment提供一 个layout,你必须实现 onCreateView()回调方法, 当到了fragment绘制它自己的layout的时候,Android系统调用它.你的此方法的实现代码必须返回一个你的fragment的 layout的根view.

从onCreateView()返回的View, 也可以从一个layout的xml资源文件中读取并生成. 为了帮助你这么做, onCreateView() 提供了一个LayoutInflater 对象.

举个例子, 这里有一个Fragment的子类, 从文件 frament_main.xml 加载了一个layout:

 @Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View view = inflater.inflate(R.layout.frament_main, container, false);return view;}

PS
传入onCreateView()的container参数是你的fragmentlayout将被插入的父ViewGroup(来自activity的layout) savedInstanceState 参数是一个Bundle, 如果fragment是被恢复的,它提供关于fragment的之前的实例的数据,

inflate() 方法有3个参数:

a、想要加载的layout的resource ID.
b、加载的layout的父ViewGroup.传入container是很重要的, 目的是为了让系统接受所要加载的layout的根view的layout参数,由它将挂靠的父view指定.
c、布尔值指示在加载期间, 展开的layout是否应当附着到ViewGroup (第二个参数).

2、将fragment添加到activity
通常地, fragment为宿主activity提供UI的一部分, 被作为activity的整个viewhierarchy的一部分被嵌入. 有2种方法你可以添加一个fragment到activity layout:

2.1、使用XML将Fragment添加到一个Activity中
在这种情况下,你可以像为View一样, 为fragment指定layout属性。

PS

1、 中的 android:name属性指定了在layout中实例化的Fragment类.当系统创建这个activity layout时,它实例化每一个在layout中指定的fragment,并调用每一个上的onCreateView()方法,来获取每一个 fragment的layout.系统将从fragment返回的 View直接插入到元素所在的地方.2、通过在xml中定义fragment的方式,我们不能在运行时移除fragment。如果我们想要通过切换fragments来跟用户有更好的互动,那么就需要在activity启动的时候定义fragment了。

2.2、在运行时添加一个Fragment到Activity

上面一节的在activity的布局文件(layout xml)中添加Fragment的方法我们已经知道了。现在我们将学习另外一种方式,这种方式允许我们在运行时动态的显示和隐藏fragment。为了达到在activity中动态管理Fragment,我们需要用到FragmentManager,并且通过它创建FragmentTransaction。

activity允许移除或者替换fragment需要有如下条件:1、activity的onCreate()方法中添加初始化的fragment2、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" />

Activity中使用getSupportFragmentManager()获取FragmentManager,之后调用beginTransaction去创建一个FragmentTransaction对象, 再调用add()方法即可添加一个fragment。 在activity中可以使用同一个FragmentTransaction对象去执行多个fragment事务,当做这样操作时,必须调用commint()方法。 下面的代码演示怎样添加一个fragment到res/layout/news_articles.xml的layout:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;public class MainActivity extends FragmentActivity {@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.news_articles);if (findViewById(R.id.fragment_container) != null) {if (savedInstanceState != null) {return;}HeadlinesFragment firstFragment = new HeadlinesFragment();firstFragment.setArguments(getIntent().getExtras());// Add the fragment to the 'fragment_container' FrameLayoutgetSupportFragmentManager().beginTransaction().add(R.id.fragment_container, firstFragment).commit();}}
}

add()的第一个参数是fragment要放入的ViewGroup, 由resource ID指定,第二个参数是需要添加的fragment.一旦用FragmentTransaction做了改变,为了使改变生效,必须调用commit().

PS

现在再来说明另外一个实例,实例图如下,我要在四个标签页面切换(主页,手机,配件,购物车)

代码如下,具体就是通过影藏和显示fragment来实现切换

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.Window;public class FramentMainActivity extends FragmentActivity {private Fragment[] fragments;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_frament_main);fragments = new Fragment[4];fragments[0] = getSupportFragmentManager().findFragmentById(R.id.farment_main);fragments[1] = getSupportFragmentManager().findFragmentById(R.id.farment_phone);fragments[2] = getSupportFragmentManager().findFragmentById(R.id.farment_accessory);fragments[3] = getSupportFragmentManager().findFragmentById(R.id.farment_cart);getSupportFragmentManager().beginTransaction().hide(fragments[1]).hide(fragments[2]).hide(fragments[3]).show(fragments[0]).commit();}public void mainClick(View view){getSupportFragmentManager().beginTransaction().hide(fragments[1]).hide(fragments[2]).hide(fragments[3]).show(fragments[0]).commit();}public void phoneClick(View view){getSupportFragmentManager().beginTransaction().hide(fragments[0]).hide(fragments[2]).hide(fragments[3]).show(fragments[1]).commit();}public void accessoryClick(View view){getSupportFragmentManager().beginTransaction().hide(fragments[0]).hide(fragments[1]).hide(fragments[3]).show(fragments[2]).commit();}public void cartClick(View view){getSupportFragmentManager().beginTransaction().hide(fragments[0]).hide(fragments[1]).hide(fragments[2]).show(fragments[3]).commit();}
}

3、Frament 管理

要管理 fragment,需使用 FragmentManager,要获取它,需在 activity 中调用方法 getFragmentManager()。 可以用 FragmentManager 来做以上事情:

使用方法 findFragmentById()或 findFragmentByTag(),获取 activity 中已存在的 fragment

使用方法 popBackStack()从 activity 的后退栈中弹出 fragment(这可以模拟后退键引发的动作)

用方法 addOnBackStackChangedListerner()注册一个侦听器以监视后退栈的变化

还可以使用 FragmentManager 打开一个 FragmentTransaction 来执行 fragment 的事务,比如添加或删除 fragment。

在 activity 中使用 fragment 的一个伟大的好处是能跟据用户的输入对 fragment 进行添加、删除、替换以及执行 其它动作的能力。提交的一组 fragment 的变化叫做一个事务。事务通过 FragmentTransaction 来执行。还可以把每个 事务保存在 activity 的后退栈中,这样就可以让用户在 fragment 变化之间导航(跟在 activity 之间导航一样)。

可以通过 FragmentManager 来取得 FragmentTransaction 的实例,如下:

FragmentManagerfragmentManager = getFragmentManager();
FragmentTransactionfragmentTransaction =fragmentManager.beginTransaction(); 

一个事务是在同一时刻执行的一组动作(很像数据库中的事务)。可以用 add(),remove(),replace()等方法构成事务,最后使用 commit()方法提交事务。在调用 commint()之前,可以用addToBackStack()把事务添加到一个后退栈中, 这个后退栈属于所在的 activity。有了它,就可以在用户按下返回键时,返回到 fragment 执行事务之前的状态。如 下例:演示了如何用一个 fragment 代替另一个 fragment,同时在后退栈中保存被代替的 fragment 的状态。

4、回退栈管理

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

看这样一个效果图:

点击第一个按钮,切换到第二个界面,点击第二个按钮,切换到第三个界面,然后点击Back键依次回退。这像不像初学Android时的Activity跳转,当然了,这里肯定不是,不然我就跪了。这里是Fragment实现的,用户点击Back,实际是Fragment回退栈不断的弹栈。

如何添加一个Fragment事务到回退栈:

FragmentTransaction.addToBackStack(String)

下面讲解代码:很明显一共是3个Fragment和一个Activity.

先看Activity的布局文件:

[html] view plain copy print ?
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5. <FrameLayout
  6. android:id="@+id/id_content"
  7. android:layout_width="fill_parent"
  8. android:layout_height="fill_parent" >
  9. </FrameLayout>
  10. </RelativeLayout>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent" ><FrameLayoutandroid:id="@+id/id_content"android:layout_width="fill_parent"android:layout_height="fill_parent" ></FrameLayout></RelativeLayout>

不同的Fragment就在这个FrameLayout中显示。

MainActivity.java

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. public class MainActivity extends Activity
  8. {
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState)
  11. {
  12. super.onCreate(savedInstanceState);
  13. requestWindowFeature(Window.FEATURE_NO_TITLE);
  14. setContentView(R.layout.activity_main);
  15. FragmentManager fm = getFragmentManager();
  16. FragmentTransaction tx = fm.beginTransaction();
  17. tx.add(R.id.id_content, new FragmentOne(),"ONE");
  18. tx.commit();
  19. }
  20. }
package com.zhy.zhy_fragments;import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.Window;public class MainActivity extends Activity
{@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);FragmentManager fm = getFragmentManager();FragmentTransaction tx = fm.beginTransaction();tx.add(R.id.id_content, new FragmentOne(),"ONE");tx.commit();}}

很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.

下面是FragmentOne

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.View.OnClickListener;
  9. import android.view.ViewGroup;
  10. import android.widget.Button;
  11. public class FragmentOne extends Fragment implements OnClickListener
  12. {
  13. private Button mBtn;
  14. @Override
  15. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  16. Bundle savedInstanceState)
  17. {
  18. View view = inflater.inflate(R.layout.fragment_one, container, false);
  19. mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
  20. mBtn.setOnClickListener(this);
  21. return view;
  22. }
  23. @Override
  24. public void onClick(View v)
  25. {
  26. FragmentTwo fTwo = new FragmentTwo();
  27. FragmentManager fm = getFragmentManager();
  28. FragmentTransaction tx = fm.beginTransaction();
  29. tx.replace(R.id.id_content, fTwo, "TWO");
  30. tx.addToBackStack(null);
  31. tx.commit();
  32. }
  33. }
package com.zhy.zhy_fragments;import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;public class FragmentOne extends Fragment implements OnClickListener
{private Button mBtn;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){View view = inflater.inflate(R.layout.fragment_one, container, false);mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);mBtn.setOnClickListener(this);return view;}@Overridepublic void onClick(View v){FragmentTwo fTwo = new FragmentTwo();FragmentManager fm = getFragmentManager();FragmentTransaction tx = fm.beginTransaction();tx.replace(R.id.id_content, fTwo, "TWO");tx.addToBackStack(null);tx.commit();}}

我们在点击FragmentOne中的按钮时,使用了replace方法,如果你看了前一篇博客,一定记得replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用tx.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,证据就是:仔细看上面的效果图,我们在跳转前在文本框输入的内容,在用户Back得到第一个界面的时候不见了。

接下来FragmentTwo

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.View.OnClickListener;
  9. import android.view.ViewGroup;
  10. import android.widget.Button;
  11. public class FragmentTwo extends Fragment implements OnClickListener
  12. {
  13. private Button mBtn ;
  14. @Override
  15. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  16. Bundle savedInstanceState)
  17. {
  18. View view = inflater.inflate(R.layout.fragment_two, container, false);
  19. mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
  20. mBtn.setOnClickListener(this);
  21. return view ;
  22. }
  23. @Override
  24. public void onClick(View v)
  25. {
  26. FragmentThree fThree = new FragmentThree();
  27. FragmentManager fm = getFragmentManager();
  28. FragmentTransaction tx = fm.beginTransaction();
  29. tx.hide(this);
  30. tx.add(R.id.id_content , fThree, "THREE");
  31. //      tx.replace(R.id.id_content, fThree, "THREE");
  32. tx.addToBackStack(null);
  33. tx.commit();
  34. }
  35. }
package com.zhy.zhy_fragments;import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;public class FragmentTwo extends Fragment implements OnClickListener
{private Button mBtn ;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){View view = inflater.inflate(R.layout.fragment_two, container, false);mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);mBtn.setOnClickListener(this);return view ; }@Overridepublic void onClick(View v){FragmentThree fThree = new FragmentThree();FragmentManager fm = getFragmentManager();FragmentTransaction tx = fm.beginTransaction();tx.hide(this);tx.add(R.id.id_content , fThree, "THREE");
//      tx.replace(R.id.id_content, fThree, "THREE");tx.addToBackStack(null);tx.commit();}}

这里点击时,我们没有使用replace,而是先隐藏了当前的Fragment,然后添加了FragmentThree的实例,最后将事务添加到回退栈。这样做的目的是为了给大家提供一种方案:如果不希望视图重绘该怎么做,请再次仔细看效果图,我们在FragmentTwo的EditText填写的内容,用户Back回来时,数据还在~~~

最后FragmentThree就是简单的Toast了:

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. import android.widget.Toast;
  10. public class FragmentThree extends Fragment implements OnClickListener
  11. {
  12. private Button mBtn;
  13. @Override
  14. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  15. Bundle savedInstanceState)
  16. {
  17. View view = inflater.inflate(R.layout.fragment_three, container, false);
  18. mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);
  19. mBtn.setOnClickListener(this);
  20. return view;
  21. }
  22. @Override
  23. public void onClick(View v)
  24. {
  25. Toast.makeText(getActivity(), " i am a btn in Fragment three",
  26. Toast.LENGTH_SHORT).show();
  27. }
  28. }
package com.zhy.zhy_fragments;import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;public class FragmentThree extends Fragment implements OnClickListener
{private Button mBtn;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){View view = inflater.inflate(R.layout.fragment_three, container, false);mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);mBtn.setOnClickListener(this);return view;}@Overridepublic void onClick(View v){Toast.makeText(getActivity(), " i am a btn in Fragment three",Toast.LENGTH_SHORT).show();}}

好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。

这里极其注意一点:上面的整体代码不具有任何参考价值,纯粹为了显示回退栈,在后面讲解了Fragment与Activity通信以后,会重构上面的代码!

5、Fragment与Activity通信

因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。

6、Fragment与Activity通信的最佳实践

因为要考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。

下面我通过两种方式的代码,分别重构,FragmentOne和FragmentTwo的点击事件,以及Activity对点击事件的响应:

首先看FragmentOne

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. public class FragmentOne extends Fragment implements OnClickListener
  10. {
  11. private Button mBtn;
  12. /**
  13. * 设置按钮点击的回调
  14. * @author zhy
  15. *
  16. */
  17. public interface FOneBtnClickListener
  18. {
  19. void onFOneBtnClick();
  20. }
  21. @Override
  22. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  23. Bundle savedInstanceState)
  24. {
  25. View view = inflater.inflate(R.layout.fragment_one, container, false);
  26. mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
  27. mBtn.setOnClickListener(this);
  28. return view;
  29. }
  30. /**
  31. * 交给宿主Activity处理,如果它希望处理
  32. */
  33. @Override
  34. public void onClick(View v)
  35. {
  36. if (getActivity() instanceof FOneBtnClickListener)
  37. {
  38. ((FOneBtnClickListener) getActivity()).onFOneBtnClick();
  39. }
  40. }
  41. }
package com.zhy.zhy_fragments;import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;public class FragmentOne extends Fragment implements OnClickListener
{private Button mBtn;/*** 设置按钮点击的回调* @author zhy**/public interface FOneBtnClickListener{void onFOneBtnClick();}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){View view = inflater.inflate(R.layout.fragment_one, container, false);mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);mBtn.setOnClickListener(this);return view;}/*** 交给宿主Activity处理,如果它希望处理*/@Overridepublic void onClick(View v){if (getActivity() instanceof FOneBtnClickListener){((FOneBtnClickListener) getActivity()).onFOneBtnClick();}}}

可以看到现在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;并且我们声明了一个接口,来回调其点击事件,想要管理其点击事件的Activity实现此接口就即可。可以看到我们在onClick中首先判断了当前绑定的Activity是否实现了该接口,如果实现了则调用。

再看FragmentTwo

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. public class FragmentTwo extends Fragment implements OnClickListener
  10. {
  11. private Button mBtn ;
  12. private FTwoBtnClickListener fTwoBtnClickListener ;
  13. public interface FTwoBtnClickListener
  14. {
  15. void onFTwoBtnClick();
  16. }
  17. //设置回调接口
  18. public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
  19. {
  20. this.fTwoBtnClickListener = fTwoBtnClickListener;
  21. }
  22. @Override
  23. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  24. Bundle savedInstanceState)
  25. {
  26. View view = inflater.inflate(R.layout.fragment_two, container, false);
  27. mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
  28. mBtn.setOnClickListener(this);
  29. return view ;
  30. }
  31. @Override
  32. public void onClick(View v)
  33. {
  34. if(fTwoBtnClickListener != null)
  35. {
  36. fTwoBtnClickListener.onFTwoBtnClick();
  37. }
  38. }
  39. }
package com.zhy.zhy_fragments;import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;public class FragmentTwo extends Fragment implements OnClickListener
{private Button mBtn ;private FTwoBtnClickListener fTwoBtnClickListener ;public interface FTwoBtnClickListener{void onFTwoBtnClick();}//设置回调接口public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener){this.fTwoBtnClickListener = fTwoBtnClickListener;}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){View view = inflater.inflate(R.layout.fragment_two, container, false);mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);mBtn.setOnClickListener(this);return view ; }@Overridepublic void onClick(View v){if(fTwoBtnClickListener != null){fTwoBtnClickListener.onFTwoBtnClick();}}}

与FragmentOne极其类似,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。

最后看Activity :

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;
  8. import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;
  9. public class MainActivity extends Activity implements FOneBtnClickListener,
  10. FTwoBtnClickListener
  11. {
  12. private FragmentOne mFOne;
  13. private FragmentTwo mFTwo;
  14. private FragmentThree mFThree;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState)
  17. {
  18. super.onCreate(savedInstanceState);
  19. requestWindowFeature(Window.FEATURE_NO_TITLE);
  20. setContentView(R.layout.activity_main);
  21. mFOne = new FragmentOne();
  22. FragmentManager fm = getFragmentManager();
  23. FragmentTransaction tx = fm.beginTransaction();
  24. tx.add(R.id.id_content, mFOne, "ONE");
  25. tx.commit();
  26. }
  27. /**
  28. * FragmentOne 按钮点击时的回调
  29. */
  30. @Override
  31. public void onFOneBtnClick()
  32. {
  33. if (mFTwo == null)
  34. {
  35. mFTwo = new FragmentTwo();
  36. mFTwo.setfTwoBtnClickListener(this);
  37. }
  38. FragmentManager fm = getFragmentManager();
  39. FragmentTransaction tx = fm.beginTransaction();
  40. tx.replace(R.id.id_content, mFTwo, "TWO");
  41. tx.addToBackStack(null);
  42. tx.commit();
  43. }
  44. /**
  45. * FragmentTwo 按钮点击时的回调
  46. */
  47. @Override
  48. public void onFTwoBtnClick()
  49. {
  50. if (mFThree == null)
  51. {
  52. mFThree = new FragmentThree();
  53. }
  54. FragmentManager fm = getFragmentManager();
  55. FragmentTransaction tx = fm.beginTransaction();
  56. tx.hide(mFTwo);
  57. tx.add(R.id.id_content, mFThree, "THREE");
  58. // tx.replace(R.id.id_content, fThree, "THREE");
  59. tx.addToBackStack(null);
  60. tx.commit();
  61. }
  62. }
package com.zhy.zhy_fragments;import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.Window;import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;
import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;public class MainActivity extends Activity implements FOneBtnClickListener,FTwoBtnClickListener
{private FragmentOne mFOne;private FragmentTwo mFTwo;private FragmentThree mFThree;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);mFOne = new FragmentOne();FragmentManager fm = getFragmentManager();FragmentTransaction tx = fm.beginTransaction();tx.add(R.id.id_content, mFOne, "ONE");tx.commit();}/*** FragmentOne 按钮点击时的回调*/@Overridepublic void onFOneBtnClick(){if (mFTwo == null){mFTwo = new FragmentTwo();mFTwo.setfTwoBtnClickListener(this);}FragmentManager fm = getFragmentManager();FragmentTransaction tx = fm.beginTransaction();tx.replace(R.id.id_content, mFTwo, "TWO");tx.addToBackStack(null);tx.commit();}/*** FragmentTwo 按钮点击时的回调*/@Overridepublic void onFTwoBtnClick(){if (mFThree == null){mFThree = new FragmentThree();}FragmentManager fm = getFragmentManager();FragmentTransaction tx = fm.beginTransaction();tx.hide(mFTwo);tx.add(R.id.id_content, mFThree, "THREE");// tx.replace(R.id.id_content, fThree, "THREE");tx.addToBackStack(null);tx.commit();}}

代码重构结束,与开始的效果一模一样。上面两种通信方式都是值得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然Fragment和Activity可以通过getActivity与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。

7、如何处理运行时配置发生变化

运行时配置发生变化,最常见的就是屏幕发生旋转,如果你不知道如何处理屏幕变化可以参考:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

这里提一下:很多人觉得强制设置屏幕的方向就可以了,但是有一点,当你的应用被至于后台(例如用户点击了home),长时间没有返回的时候,你的应用也会被重新启动。比如上例:如果你把上面的例子你至于FragmentThree界面,然后处于后台状态,长时间后你会发现当你再次通过home打开时,上面FragmentThree与FragmentOne叠加在一起,这就是因为你的Activity重新启动,在原来的FragmentThree上又绘制了一个FragmentOne。

好了,下面看一段代码:

Activity:

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. public class MainActivity extends Activity
  8. {
  9. private FragmentOne mFOne;
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState)
  12. {
  13. super.onCreate(savedInstanceState);
  14. requestWindowFeature(Window.FEATURE_NO_TITLE);
  15. setContentView(R.layout.activity_main);
  16. mFOne = new FragmentOne();
  17. FragmentManager fm = getFragmentManager();
  18. FragmentTransaction tx = fm.beginTransaction();
  19. tx.add(R.id.id_content, mFOne, "ONE");
  20. tx.commit();
  21. }
  22. }
package com.zhy.zhy_fragments;import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.Window;public class MainActivity extends Activity{private FragmentOne mFOne;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);mFOne = new FragmentOne();FragmentManager fm = getFragmentManager();FragmentTransaction tx = fm.beginTransaction();tx.add(R.id.id_content, mFOne, "ONE");tx.commit();}}

Fragment

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. public class FragmentOne extends Fragment
  9. {
  10. private static final String TAG = "FragmentOne";
  11. @Override
  12. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  13. Bundle savedInstanceState)
  14. {
  15. Log.e(TAG, "onCreateView");
  16. View view = inflater.inflate(R.layout.fragment_one, container, false);
  17. return view;
  18. }
  19. @Override
  20. public void onCreate(Bundle savedInstanceState)
  21. {
  22. // TODO Auto-generated method stub
  23. super.onCreate(savedInstanceState);
  24. Log.e(TAG, "onCreate");
  25. }
  26. @Override
  27. public void onDestroyView()
  28. {
  29. // TODO Auto-generated method stub
  30. super.onDestroyView();
  31. Log.e(TAG, "onDestroyView");
  32. }
  33. @Override
  34. public void onDestroy()
  35. {
  36. // TODO Auto-generated method stub
  37. super.onDestroy();
  38. Log.e(TAG, "onDestroy");
  39. }
  40. }
package com.zhy.zhy_fragments;import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;public class FragmentOne extends Fragment
{private static final String TAG = "FragmentOne";@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState){Log.e(TAG, "onCreateView");View view = inflater.inflate(R.layout.fragment_one, container, false);return view;}@Overridepublic void onCreate(Bundle savedInstanceState){// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);Log.e(TAG, "onCreate");}@Overridepublic void onDestroyView(){// TODO Auto-generated method stubsuper.onDestroyView();Log.e(TAG, "onDestroyView");}@Overridepublic void onDestroy(){// TODO Auto-generated method stubsuper.onDestroy();Log.e(TAG, "onDestroy");}}

很简单的代码,当你运行之后,不断的旋转屏幕,你会发现每旋转一次屏幕,屏幕上就多了一个FragmentOne的实例,并且后台log会打印出许多套生命周期的回调。

类似:

[html] view plain copy print ?
  1. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  2. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  3. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  4. 07-20 08:18:46.681: E/FragmentOne(1633): onCreateView
  5. 07-20 08:18:46.831: E/FragmentOne(1633): onCreateView
  6. 07-20 08:18:46.891: E/FragmentOne(1633): onCreateView
07-20 08:18:46.651: E/FragmentOne(1633): onCreate
07-20 08:18:46.651: E/FragmentOne(1633): onCreate
07-20 08:18:46.651: E/FragmentOne(1633): onCreate
07-20 08:18:46.681: E/FragmentOne(1633): onCreateView
07-20 08:18:46.831: E/FragmentOne(1633): onCreateView
07-20 08:18:46.891: E/FragmentOne(1633): onCreateView

这是为什么呢,因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建;这样造成当旋转的时候,本身存在的Fragment会重新启动,然后当执行Activity的onCreate时,又会再次实例化一个新的Fragment,这就是出现的原因。

那么如何解决呢:

其实通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建:

默认的savedInstanceState会存储一些数据,包括Fragment的实例:通过打印可以看出:

[java] view plain copy print ?
  1. 07-20 08:23:12.952: E/FragmentOne(1782): Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8, android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721, android:views=android.util.SparseArray@40d0af68}]}]
07-20 08:23:12.952: E/FragmentOne(1782): Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8, android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721, android:views=android.util.SparseArray@40d0af68}]}]

所以,我们简单改一下代码,只有在savedInstanceState==null时,才进行创建Fragment实例:

[java] view plain copy print ?
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.util.Log;
  7. import android.view.Window;
  8. public class MainActivity extends Activity
  9. {
  10. private static final String TAG = "FragmentOne";
  11. private FragmentOne mFOne;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState)
  14. {
  15. super.onCreate(savedInstanceState);
  16. requestWindowFeature(Window.FEATURE_NO_TITLE);
  17. setContentView(R.layout.activity_main);
  18. Log.e(TAG, savedInstanceState+"");
  19. if(savedInstanceState == null)
  20. {
  21. mFOne = new FragmentOne();
  22. FragmentManager fm = getFragmentManager();
  23. FragmentTransaction tx = fm.beginTransaction();
  24. tx.add(R.id.id_content, mFOne, "ONE");
  25. tx.commit();
  26. }
  27. }
  28. }
package com.zhy.zhy_fragments;import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;public class MainActivity extends Activity{private static final String TAG = "FragmentOne";private FragmentOne mFOne;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);Log.e(TAG, savedInstanceState+"");if(savedInstanceState == null){mFOne = new FragmentOne();FragmentManager fm = getFragmentManager();FragmentTransaction tx = fm.beginTransaction();tx.add(R.id.id_content, mFOne, "ONE");tx.commit();}}}

现在无论进行多次旋转都只会有一个Fragment实例在Activity中。

现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持?

其实和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。

由于篇幅原因,就不贴测试代码了。

史上最全Fragment介绍,包括fragment的定义,生命周期,用法相关推荐

  1. 函数指针史上最全的介绍

    函数指针 一. 函数指针的概念 如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址.而且函数名表示的就是这个地址.既然是地址我们就可 ...

  2. WMS系统是什么——史上最全WMS介绍

    一.什么是WMS? WMS是仓库管理系统(Warehouse Management System) 的缩写.仓储管理系统利用物联网.大数据等技术为多货多仓的企业实现智能化库房管理.系统化仓储管理.透明 ...

  3. 史上最全 XMind 8 快捷键大全

    对于那3名小学生在我背后鬼鬼祟祟小声议论的这件事,其实我是知晓的.但我还是将注意力集中在眼前的屏幕上,力求表现得尽可能好一些,毕竟这局的形势尚未明朗,胜负依旧难分. 又是一阵剧烈的连续按键,对方英雄终 ...

  4. 史上最全的数学建模竞赛介绍,大家不要错过哦!!!

    史上最全的数学建模竞赛介绍 什么是数学建模竞赛? 数学建模,就是根据实际问题来建立数学模型,并对数学模型来进行求解,然后根据结果去解决实际问题. 数学建模竞赛的基本要求: 不超过三人的队伍(大佬一个人 ...

  5. mc服务器常用指令_我的世界服务器指令大全 史上最全的服务器指令介绍

    我的世界服务器指令大全 史上最全的服务器指令介绍.那大家也知道在我的世界中有许许多多的指令,有的是单机的有的是手机的也有的是服务器的,那今天给大家介绍的是服务器的指令,那下面一起来看看在服务器里面都有 ...

  6. 史上最全第三代半导体产业发展介绍(附世界各国研究概况解析)

    转载自:http://www.sohu.com/a/119626002_464013 导读:第3代半导体是指以氮化镓(GaN).碳化硅(SiC).金刚石.氧化锌(ZnO)为代表的宽禁带半导体材料,各类 ...

  7. (13)UVM 史上最全TLM单向/双向/多向通信介绍

    UVM 史上最全TLM单向/双向/多向通信介绍 文章目录 UVM 史上最全TLM单向/双向/多向通信介绍 一.TLM单向通信 1.概述 2.类型 3.方法 4.单向通信例子 二.TLM双向通信 1.概 ...

  8. Java Math类方法介绍之史上最全

    Java Math类方法介绍之史上最全 开发中用到了Java Math类的方法,故温习记录如下. /*** 常量*/System.out.println("------>" ...

  9. 史上最全 BAT 大厂面试题整理

    转载自 史上最全 BAT 大厂面试题整理!(速度收藏) 主要分为以下几部分: (1)java面试题 (2)Android面试题 (3)高端技术面试题 (4)非技术性问题&HR问题汇总 1 ja ...

  10. 2016年GitHub上史上最全的Android开源项目分类汇总

    以下内容为转载 版主原网址 http://itindex.net/detail/51896-github-android-开源 GitHub上史上最全的Android开源项目分类汇总 今天在看博客的时 ...

最新文章

  1. 4种最常问的编码算法面试问题,你会吗?
  2. Coinbase内部调查未发现比特币现金内幕交易证据
  3. <马哲>商品二因素及其辩证关系2017-12-27
  4. 谈谈密码学的数学原理
  5. Vue + Element UI——滚动条el-scrollerbar和无限滚动指令v-infinite-scroll整合解决方案
  6. tensorflow 启动Session(tf.Session(),tf.InteractivesSession(),tf.train.Supervisor().managed_session() )
  7. 201771010137 赵栋 《第十二周学习总结》
  8. mysql 定期备份策略,MySQL--3--mysqldump备份策略
  9. 互联网日报 | 7月15日 星期四 | B站赠送所有用户1天大会员;饿了么投入3亿用于今夏骑手保障;小米智能工厂二期开工...
  10. 苹果照片库的照片,直接拖到桌面上
  11. 【315天】每日项目总结系列053(2017.12.17)
  12. ASP.NET页面间的传值方法(2)
  13. 精心整理了40个Python办公自动化真实案例,一口一个,高效办公!
  14. 集团类企业信息化原则与思路
  15. 用PPO玩2048游戏--可以达到合成2048的目的
  16. python pyecharts生成图表
  17. Windows系统安全检查脚本
  18. 百度飞桨的乌镇时刻:拿下的至高荣誉和背后的绝对实力
  19. python操作xslx/xsl出现‘\xa0‘和读取时间变成float类型的处理办法
  20. (复习次数:1)D - Permutation Transformation——Codeforces Round #702 (Div. 3)

热门文章

  1. VUEX 报错:Do not mutate vuex store state outside mutation handlers.
  2. 从零开始搭建vue2.0+elementUi 后台管理系统 一项目搭建
  3. 卡券、直充接口文档源码分享
  4. vuex实现查询和删除
  5. android 背诵方歌项目
  6. 软件工程:模拟鸭子游戏
  7. 业绩增速放缓的敷尔佳将上市:规模低于巨子生物,张立国为实控人
  8. 解决WebView加载的网页被放大的问题
  9. 星巴克中国已开4家手语门店,分别位于广州、北京、杭州与上海
  10. cadnece设置等长