Fragment为何要用
Fragment是Android 3.0 (Honeycomb)被引入的。主要目的是为了给大屏幕(如平板电脑)上更加动态和灵活的UI设计提供支持。由于平板电脑的屏幕比手机的屏幕大很多,因此可用于组合和交换的UI组件的空间更大,利用Fragment实现此类设计的时,就无需管理对视图层次结构的复杂更改。
通过将 Activity 布局分成片段,您可以在运行时修改 Activity 的外观,并在由 Activity 管理的返回栈中保留这些更改。如果仅仅只有Activity布局,那是不够的,不仅在手机上有一套布局,同时在平板上还需要设计一套布局,那样维护起来也麻烦,代码上也有一定的冗余,对于APP包的大小也有一定的压力。Fragment的优势是布局在不同设备上的适配。
除此之外,使用Fragment还有这么几个方面优势:
- 代码复用。特别适用于模块化的开发,因为一个Fragment可以被多个Activity嵌套,有个共同的业务模块就可以复用了,是模块化UI的良好组件。
- Activity用来管理Fragment。Fragment的生命周期是寄托到Activity中,Fragment可以被Attach添加和Detach释放。
- 可控性。Fragment可以像普通对象那样自由的创建和控制,传递参数更加容易和方便,也不用处理系统相关的事情,显示方式、替换、不管是整体还是部分,都可以做到相应的更改。
- Fragments是view controllers,它们包含可测试的,解耦的业务逻辑块,由于Fragments是构建在views之上的,而views很容易实现动画效果,因此Fragments在屏幕切换时具有更好的控制。
Fragment是什么
说了半天的Fragment,也看到这么多次Fragment这个名词出现,那么Fragment到底是什么东东呢?定义又是如何?
Fragment也可以叫为“片段”,但我觉得“片段”中文叫法有点生硬,还是保持叫Fragment比较好,它可以表示Activity中的行为或用户界面部分。我们可以在一个Activity中用多个Fragment组合来构建多窗格的UI,以及在多个Activity中重复使用某个Fragment。它有自己的生命周期,能接受自己的输入,并且可以在 Activity 运行时添加或删除Fragment(有点像在不同 Activity 中重复使用的“子 Activity”)。
简单来说,Fragment其实可以理解为一个具有自己生命周期的控件,只不过这个控件又有点特殊,它有自己的处理输入事件的能力,有自己的生命周期,又必须依赖于Activity,能互相通信和托管。
Fragment生命周期
Fragment生命周期和Activity生命周期对比图,可以看到两者还是有很多相似的地方,比如都有onCreate(),onStart(),onPause(),onDestroy()等等,因为Fragment是被托管到Activity中的,所以多了两个onAttach()和onDetach()。这里讲讲与Activity生命周期不一样的方法。
onAttach()
Fragment和Activity建立关联的时候调用,被附加到Activity中去。
onCreate()
系统会在创建Fragment时调用此方法。可以初始化一段资源文件等等。
onCreateView()
系统会在Fragment首次绘制其用户界面时调用此方法。 要想为Fragment绘制 UI,从该方法中返回的 View 必须是Fragment布局的根视图。如果Fragment未提供 UI,您可以返回 null。
onViewCreated()
在Fragment被绘制后,调用此方法,可以初始化控件资源。
onActivityCreated()
当onCreate(),onCreateView(),onViewCreated()方法执行完后调用,也就是Activity被渲染绘制出来后。
onPause()
系统将此方法作为用户离开Fragment的第一个信号(但并不总是意味着此Fragment会被销毁)进行调用。 通常可以在此方法内确认在当前用户会话结束后仍然有效的任何更改(因为用户可能不会返回)。
onDestroyView()
Fragment中的布局被移除时调用。
onDetach()
Fragment和Activity解除关联的时候调用。
但需要注一点是:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现。
Fragment怎么用
前面介绍了半天,不耐烦的人会说,这么多废话,也不见的到底是如何使用,毕竟我们是开发者,需要的使用方式,那么现在就来说说用法如何吧。两种方式:静态用法和动态用法。
静态用法
1、继承Fragment,重写onCreateView决定Fragemnt的布局
2、在Activity中声明此Fragment,就当和普通的View一样
首先是布局文件:fragment1.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#00ff00" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is fragment 1" android:textColor="#000000" android:textSize="25sp" /> </LinearLayout>
可以看到,这个布局文件非常简单,只有一个LinearLayout,里面加入了一个TextView。我们再新建一个fragment2.xml,代码跟上面一样,只是改不了背景颜色。
然后新建一个类Fragment1,这个类是继承自Fragment的:
public class Fragment1 extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment1, container, false); } }
可以看到,在onCreateView()方法中加载了fragment1.xml的布局。同样fragment2.xml也是一样的做法。
然后打开或新建activity_main.xml作为主Activity的布局文件,在里面加入两个Fragment的引用,使用android:name前缀来引用具体的Fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" > <fragment android:id="@+id/fragment1" android:name="com.example.fragmentdemo.Fragment1" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" /> <fragment android:id="@+id/fragment2" android:name="com.example.fragmentdemo.Fragment2" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" /> </LinearLayout>
最后新建MainActivity作为程序的主Activity,里面的代码非常简单,都是自动生成的。
现在我们来运行一次程序,就会看到,一个Activity很融洽地包含了两个Fragment,这两个Fragment平分了整个屏幕。
动态用法
上面仅仅是Fragment简单用法,它真正强大部分是在动态地添加到Activity中,那么动态用法又是如何呢?
还是在静态用法代码的基础上修改,打开activity_main.xml,将其中对Fragment的引用都删除,只保留最外层的LinearLayout,并给它添加一个id,因为我们要动态添加Fragment,不用在XML里添加了,删除后代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" > </LinearLayout>
然后打开MainActivity,修改其中的代码如下所示:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Display display = getWindowManager().getDefaultDisplay(); if (display.getWidth() > display.getHeight()) { Fragment1 fragment1 = new Fragment1(); getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit(); } else { Fragment2 fragment2 = new Fragment2(); getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit(); } } }
看到了没,首先,我们要获取屏幕的宽度和高度,然后进行判断,如果屏幕宽度大于高度就添加fragment1,如果高度大于宽度就添加fragment2。
动态添加Fragment主要分为4步:
- 获取到FragmentManager,在Activity中可以直接通过getFragmentManager得到。
- 开启一个事务,通过调用beginTransaction方法开启。
- 向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例。
- 提交事务,调用commit方法提交。
要想管理 Activity 中的片段,需要使用 FragmentManager。要想获取它,需要 Activity 调用 getFragmentManager()。
使用 FragmentManager 执行的操作包括:
通过 findFragmentById()(对于在 Activity 布局中提供 UI 的片段)或 findFragmentByTag()(对于提供或不提供 UI 的片段)获取 Activity 中存在的片段 通过 popBackStack()将片段从返回栈中弹出 通过 addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器 也可以使用 FragmentManager 打开一个 FragmentTransaction,通过它来执行某些事务,如添加和删除片段。
Fragment通信
尽管 Fragment 是作为独立于 Activity的对象实现,并且可在多个 Activity 内使用,但Fragment 的给定实例会直接绑定到包含它的 Activity。具体地说,Fragment 可以通过 getActivity() 访问 Activity实例,并轻松地执行在 Activity 布局中查找视图等任务。如:
View listView = getActivity().findViewById(R.id.list);
同样地,Activity 也可以使用 findFragmentById() 或 findFragmentByTag(),通过从 FragmentManager 获取对 Fragment 的引用来调用Fragment中的方法。例如:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
创建对 Activity 的事件回调
在某些情况下,可能需要通过与 Activity 共享事件。执行此操作的一个好方法是,在Fragment 内定义一个回调接口,并要求宿主 Activity 实现它。 当 Activity 通过该接口收到回调时,可以根据需要与布局中的其他Fragment共享这些信息。
摘抄
抱歉,忘记部分摘抄于哪了。