1、基本介绍
Android 运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视。针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布局以适应平板等超大屏的。Fragment 的出现就是为了做到一个 App 可以同时适应手机和平板。
Fragment 的诞生是为了支持更动态、更灵活的 UI 设计,在我们平时开发的时候都是用 Activity 加载一个布局文件,所有的控件声明都写到这个 xml 文档中来。我们现在要制作动态的 UI 设计,用这种方式当然不行。
Fragment 的英文解释是碎片、片段,我们可以在 Activity 中包含很多个 Fragment,每个 Fragment 可以独立,彼此之间也可以做交互,也可以跟 Activity 做交互。
Fragment 就是以 Activity 的一部分存在,它可以支持自己的布局文件,拥有自己的生命周期,甚至Activity的界面可以完全由不同的Fragment组成。
Fragment 拥有自己的生命周期和接收、处理用户的事件,这样就不必在 Activity 写一堆控件的事件处理的代码了。更为重要的是,我们可以动态的添加、替换和移除某个 Fragment。
上图显示的同一个 App 分别在平板和手机上的状态,在平板上,因为是超大屏幕的,容纳的内容更多,所以可以在一个 Activity 中放两个 Fragment,我们可以将左边的 Fragment 看作一个新闻的列表,那么右边的就是指定新闻的详情页。因为手机的屏幕大小有限,不能像平板包含那么多的组件,所以将这两个 Fragment 分别由两个 Activity 来显示,当选中了一条新闻,就切换到 ActivityB,显示 FragmentB 的页面。
因为 Fragment 有自己独立的生命周期和响应事件,是一个模块化和可重用的组件,所以完全可以把它看作一个独立的个体,这样我们就不需要因为手机到平板的变换就对布局文件大改特改。
Fragment 的特点:
- Fragment 可以作为 Activity 界面的一部分组成出现。
- 可以在一个 Activity 中同时出现多个 Fragment,并且一个 Fragment 也可以在多个 Activity 中使用。
- 在 Activity 运行过程中,可以添加,移除或替换 Fragment。
- Fragment 可以响应自己的输出事件,并且有自己的生命周期,它们的生命周期会受宿主 Activity 的生命周期影响。
2、生命周期
Fragment 必须是依存于 Activity 而存在的,因此 Activity 的生命周期会直接影响到 Fragment 的生命周期。
通过上图我们可以很容易的比较 Activity 和 Fragment 两者生命周期的关联,Fragment 的创建到销毁的整个过程一目了然。
相信大家都是对 Activity 生命周期很熟悉的,我们可以看到对比 Activity,Fragment 的生命周期是十分相似的,相同方法代表的含义功能也大致相同,但 Fragment 也有了一些新的回调方法,我们首先当然要了解一下这些不认识的方法:
- onAttach (Activity):当 Fragment 与 Activity 发生关联时调用。
- onCreateView (LayoutInflater, ViewGroup, Bundle):用于首次绘制用户界面的回调方法,必须返回要创建的 Fragment 视图 UI。假如不希望提供 Fragment 界面则可以返回 NULL。
- onActivityCreated (Bundle):当 Activity 的 onCreate() 的方法初始化完成时被调用。
- onDestoryView ():与 onCreateView() 相对应,当该 Fragment 的视图被移除时调用。
- onDetach ():与 onAttach() 相对应,当 Fragment 与 Activity 关联被取消时调用。
注意:除了onCreateView(),其他的所有方法如果你重写了,必须调用父类对于该方法的实现。
3、静态使用Fragment
静态使用就是把 Fragment 当做一个普通的控件,放到 Activity 的布局文件中。
在 Activity 的 layout 文件中声明 Fragment,需要特别注意的是 Fragment 的 name 属性指定了在 layout 中实例化的 Fragment 类。要想标识一个 Fragment,可以为它的 id 属性提供一个唯一的 ID,也有 tag 属性可以提供一个唯一字符串。
name 属性是将我们写好的 Fragment 的包名作为 name 的值,所以我们先要写好一个 Fragment 的类,并为它设置好布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/id_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Fragment页面"
android:textSize="20sp"
android:textStyle="bold" />
<Button
android:id="@+id/btn"
android:text="点击"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
public class CustomFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.layout_fragment, container, false);
final TextView textView = (TextView) view.findViewById(R.id.id_txt);
Button button = (Button) view.findViewById(R.id.btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setText("Fragment的文字改变了");
}
});
return view;
}
}
关于 inflate() 方法的应用不了解的朋友可以看我的博客LayoutInflater.inflate()方法解析。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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" >
<fragment
android:id="@+id/fragment"
android:name="com.ht.fragmenttest.CustomFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/layout_fragment" />
</LinearLayout>
fragment 的 name 属性的值就是 Fragment 的包名,这里要告诉 xml 文件我们要添加的布局文件是什么,否则会给我们个警告。
Activity 中只要调用个 setContentView() 即可,把 Fragment 当成普通的 View 一样声明在 Activity 的布局文件中,然后所有控件的事件处理等代码都由各自的 Fragment 去处理,Activity 变得特别干净,代码的可读性、复用性以及可维护性瞬间提升了。
4、Fragment相关类
静态使用是把 Fragment 当做控件添加进 xml 布局文件里,那动态的使用就是撰写代码将 Fragment 添加到 Activity Layout 中去,功能有添加、更新、替换以及移除。
想要完成动态的 Fragment 的加载,因为是代码撰写,所以我们首先当然是要了解 Fragment 相关的类。
Fragment 常用的类有三个:
1、android.app.Fragment 主要用于定义 Fragment。
2、android.app.FragmentManager 主要用于在 Activity 中操作 Fragment。
3、android.app.FragmentTransaction 保证一些 Fragment 操作的原子性,在学习 SQLite 的时候相信大家没少用 SQLite 事务,应该能够理解。
1、Fragment
Fragment 类的方法大都是显示 Fragment 对象当前的状态的。
1、public final boolean isAdded()
//如果该 Fragment 对象被添加到了它的 Activity 中,那么它返回 true,否则返回 false。
2、public final boolean isDetached()
//如果该 Fragment 已经明确的从 UI 中分离,那么它返回 true。也就是说,在该 Fragment 对象上使用 FragmentTransaction.detach(Fragment) 方法。
3、public final boolean isHidden()
//如果该 Fragment 对象已经被隐藏,那么它返回 true。默认情况下, Fragment 是被显示的。能够用 onHiddenChanged(boolean) 回调方法使得该 Fragment 对象状态的改变,要注意的是隐藏状态与其他状态是正交的---也就是说,要把该 Fragment 对象显示给用户,Fragment 对象必须是被启动并不被隐藏,否则一切功能都无用。
4、public final boolean isInLayout()
//当 Fragment 是通过<fragment>标签来创建的时候,这个方法始终会返回 true。从之前的状态恢复旧的 Fragment 对象,并且该对象没有显示在当前状态的布局中的情况除外。
5、Public final boolean isRemoving()
//如果当前的 Fragment 对象正在从它的 Activity 中被删除,那么就返回 true。这删除过程不是该 Fragment 对象的 Activity 的结束过程,而是把 Fragment 对象从它所在的 Activity 中删除的过程。
6、public final boolean isResumed()
//如果 Fragment 对象是在恢复状态中,该方法会返回 true。在 onResume() 和 onPause() 回调期间,这个方法都返回 true。
7、public final boolean isVisible()
//如果该 Fragment 对象对用户可见,那么就返回 true。这就意味着它:1.已经被添加到Activity中;2.它的View对象已经被绑定到窗口中;3.没有被隐藏。
2、FragmentManager
1、FragmentManager.findFragmentById();
//根据 id 来找到对应的 Fragment 实例,主要用在静态添加 fragment 的布局中,因为静态添加的 fragment 才会有 id
2、FragmentManager.findFragmentByTag();
//根据 tag 找到对应的 Fragment 实例,主要用于在动态添加的 fragment 中,根据 tag 来找到 fragment 实例
3、FragmentManager.getFragments();
//获取所有被 add 进 Activity 中的 Fragment
3、FragmentTransaction
Fragment 的主要操作都是 FragmentTransaction 的方法,要想开启事务,首先我们要获得 FragmentMananger 的对象实例。
FragmentManager fm = getFragmentManager();
//v4中,getSupportFragmentManager()
我们开启事务就是调用 FragmentManager 的 benginTransatcion() 方法:
FragmentTransaction transaction = fm.beginTransaction();
1、transation.add():往 Activity 中添加一个 Fragment。方法里面的 containerViewId 参数是指要包含我们 Fragment 的布局id。
2、transaction.remove():从 Activity 中移除一个 Fragment,如果被移除的 Fragment 没有添加到回退栈,这个 Fragment 实例将会被销毁。
3、transaction.replace():使用另一个 Fragment替换当前的,相当于 add() 和 remove() 方法的合体。
4、transaction.hide():隐藏当前的 Fragment,仅仅是设为不可见,并不会销毁。
5、transaction.show():显示之前隐藏的 Fragment。
6、transaction.detach():会将 View 与 Fragment 分离,将此将 View 从 ViewTree 中删除,而且将 Fragment 从 Activity 的 ADD 队列中移除。所以在使用 detach() 后,使用 Fragment.isAdded() 返回的值是 false,但此 Fragment 实例并不会删除,此 Fragment 的状态依然保持着使用,所以在 FragmentManager 中仍然可以找到,即通过 FragmentManager.findViewByTag() 仍然是会有值的。
7、transaction.attach():显然这个方法与 detach() 所做的工作相反,它一方面利用 Fragment 的 onCreateView() 来重建视图,一方面将此 Fragment 添加到 ADD队列中。这里最值得注意的地方在这里:由于是将 Fragment 添加到 ADD队列,所以只能添加到列队头部,所以 attach() 操作的结果是,最新操作的页面始终显示在最前面,由于这里会将fragment添加到Activity的ADD队列中,所以在这里调用Fragment.isAdded()将返回True。
8、transatcion.commit():提交一个事务,对 Fragment 的所有操作将实现。
5、动态使用Fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Fragment fragment = new CustomFragment();
FragmentManager fm = getFragmentManager();
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.frame, fragment);
transaction.commit();
}
}
这篇博客主要介绍了 Fragment 的生命周期,一些类的相关方法和静态动态加载,但各种方法的交互使用,Fragment 的栈的工作原理等等也是学习 Fragment 必不可少的知识,这些内容在下一篇博客Android–关于Fragment的基础介绍(二)里介绍。
结束语:本文仅用来学习记录,参考查阅。