面试的时候被问到Fragment是什么,下面就对它对进一步的认识吧
TabActivity在Android4.0以后已经被完全弃用,取而代之的是Fragment。Fragment是Android3.0新增的概念,Fragment翻译成中文是碎片的意思,不过却和Activity十分的相似。以下内容适用于3.0及以上的版本,3.0以下就不再赘述。
一、Fragment的基础知识介绍
1.Fragment的特性
Android是在Android 3.0 (API level 11)开始引入Fragment的。
可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的生命周期,单独处理自己的输入,在Activity运行的时候可以加载或者移除Fragment模块。
可以把Fragment设计成可以在多个Activity中复用的模块。
当开发的应用程序同时适用于平板电脑和手机时,可以利用Fragment实现灵活的布局,改善用户体验。如图
2 .Fragment的生命周期
因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activity是密切相关的。
如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果Activity是stopped状态,这个Activity中所有的Fragment都不能被启动;如果Activity被销毁,那么它其中的所有Fragment都会被销毁。
但是,当Activity在活动状态,可以独立控制Fragment的状态,比如加上或者移除Fragment。
当这样进行fragment transaction(转换)的时候,可以把fragment放入Activity的back stack中,这样用户就可以进行返回操作。
可以看到Fragment比Activity多了几个额外的生命周期回调方法:
onAttach(Activity)
当Fragment与Activity发生关联时调用。
onCreateView(LayoutInflater, ViewGroup,Bundle)
创建该Fragment的视图
onActivityCreated(Bundle)
当Activity的onCreate方法返回时调用
onDestoryView()
与onCreateView想对应,当该Fragment的视图被移除时调用
onDetach()
与onAttach相对应,当Fragment与Activity关联被取消时调用
注意:除了onCreateView,其他的所有方法如果你重写了,必须调用父类对于该方法的实现,
onCreate()
系统在创建Fragment的时候调用这个方法,这里应该初始化相关的组件,一些即便是被暂停或者被停止时依然需要保留的东西。
onPause()
当用户离开Fragment时第一个调用这个方法,需要提交一些变化,因为用户很可能不再返回来。
3.Fragment的使用
(1)实现Fragment的UI
提供Fragment的UI,必须实现onCreateView()方法。
假设Fragment的布局设置写在frag_list.xml资源文件中,那么onCreateView()方法可以如下写:
public class FragementList extends Fragment{
/**
* 显示指定的视图
* @inflater resource ID,指明了当前的Fragment对应的资源文件
* @container 该Fragment在Activity中的父容器控件
* @savedInstanceState 是否连接该布局和其父容器控件,在这里的情况设置为false,因为系统已经插入了这个布局到父控件,设置为true将会产生多余的一个View Group。
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.frag_list, container, false);
}
}
(2)把Fragment加入Activity
当Fragment被加入Activity中时,它会处在对应的View Group中。
Fragment有两种加载方式:一种是在Activity的layout中使用标签声明;另一种方法是在代码中把它加入到一个指定的ViewGroup中。
另外,Fragment它可以并不是Activity布局中的任何一部分,它可以是一个不可见的部分。
加载方式1:通过Activity的布局文件将Fragment加入Activity
在Activity的布局文件中,将Fragment作为一个子标签加入即可。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="horizontal" >
<fragment
android:id="@+id/frag_list"
android:name="com.yanis.ui.FragementList"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/frag_detail"
android:name="com.yanis.ui.FragementDetails"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
其中android:name属性填上创建的fragment的完整类名。
当系统创建这个Activity的布局文件时,系统会实例化每一个fragment,并且调用它们的onCreateView()方法,来获得相应fragment的布局,并将返回值插入fragment标签所在的地方。
有三种方法为Fragment提供ID:
android:id属性:唯一的id
android:tag属性:唯一的字符串
如果上面两个都没提供,系统使用容器view的ID。
加载方式2:通过编程的方式将Fragment加入到一个ViewGroup中
当Activity处于Running状态下的时候,可以在Activity的布局中动态地加入Fragment,只需要指定加入这个Fragment的父View Group即可。
首先,需要一个FragmentTransaction实例:
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
(注,如果import android.support.v4.app.FragmentManager;即用的是Support Library,那么使用的是:FragmentManager fragmentManager = getSupportFragmentManager();)
之后,用add()方法加上Fragment的对象:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
其中第一个参数是这个fragment的容器,即父控件组。
最后需要调用commit()方法使得FragmentTransaction实例的改变生效。
以下演示的是动态添加Fragment
主界面布局如下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="horizontal" >
<LinearLayout
android:id="@+id/frag_list"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="List" />
</LinearLayout>
<LinearLayout
android:id="@+id/frag_detail" android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="2" >
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Details" />
</LinearLayout>
</LinearLayout>
Activity类代码如下
package com.yanis.ui;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
setContentView(R.layout.activity_maino);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager
.beginTransaction();
FragementList fragment1 = new FragementList();
fragmentTransaction.add(R.id.frag_list, fragment1);
FragementDetails fragment2 = new FragementDetails();
fragmentTransaction.add(R.id.frag_detail, fragment2);
fragmentTransaction.commit();
}
}
效果如下:
4.Fragment家族常用的API
Fragment常用的三个类:
android.app.Fragment 主要用于定义Fragment
android.app.FragmentManager 主要用于在Activity中操作Fragment
android.app.FragmentTransaction 保证一些列Fragment操作的原子性,熟悉事务这个词,一定能明白~
a、获取FragmentManage的方式:
getFragmentManager() // v4中,getSupportFragmentManager
b、主要的操作都是FragmentTransaction的方法
FragmentTransaction transaction = fm.benginTransatcion();//开启一个事务
transaction.add()
往Activity中添加一个Fragment
transaction.remove()
从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈(回退栈后面会详细说),这个Fragment实例将会被销毁。
transaction.replace()
使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~
transaction.hide()
隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
transaction.show()
显示之前隐藏的Fragment
detach()
会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
attach()
重建view视图,附加到UI上并显示。
transatcion.commit()//提交一个事务
注意:常用Fragment的哥们,可能会经常遇到这样Activity状态不一致:State loss这样的错误。主要是因为:commit方法一定要在Activity.onSaveInstance()之前调用。
上述,基本是操作Fragment的所有的方式了,在一个事务开启到提交可以进行多个的添加、移除、替换等操作。
值得注意的是:如果你喜欢使用Fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。
a、比如:我在FragmentA中的EditText填了一些数据,当切换到FragmentB时,如果希望会到A还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,进行下非null判断。
b、再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace()这个和remove,add是相同的效果。
c、remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个Fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach