前言
一、fragment生命周期概述
与Activity类似,Fragment作为一个容器也必定有它自己的生命周期,如果能熟练掌握一个fragment从创建到销毁过程中的每一个方法,以及它们的调用时机,那么我们将可以更好的去管理一个fragment,比如我们可以根据需求,在不同的状态中做一些有用的事情,下面看一下官方给出的流程图:
下面对这11个方法做一下大致的解释。
onAttach(Activity activity)
---Called when the fragment has been associated with the activity (the Activity
is passed in here).
---当该fragment被添加到Activity时回调,该方法只会被调用一次。
onCreat(Bundle savedInstanceState)
---创建fragment时被回调。该方法只会调用一次。
onCreatView(LayoutInflater inflater,ViewGroup container,Bundle savedInstanceState)
---Called to create the view hierarchy associated with the fragment.
---每次创建、绘制该fragment的View组件时回调该方法,fragment将会显示该方法的View组件。
onActivityCreated(Bundle savedInstanceState)
---Called when the activity's onCreate()
method has returned.
---当fragment所在的Activity被创建完成后调用该方法。
onStart()
---启动fragment时被回调。
onResume()
---恢复fragment时被回调,onStart()方法后一定会回调onResume()方法。
onPause()
---暂停fragment时被回调。
onStop()
---停止fragment时被回调。
onDestroyView()
---Called when the view hierarchy associated with the fragment is being removed.
---销毁该fragment所包含的View组件时调用。
onDestory()
---销毁fragment时被回调,该方法只会被调用一次。
onDetach()
---Called when the fragment is being disassociated from the activity.
---当该fragment从Activity中被删除、被替换完成时回调该方法,onDestory()方法后一定会回调onDetach()方法。该方法只会被调用一次。
其实学习生命周期最好的方法还是在程序中通过打印语句来观察每个阶段的状态和顺序,下面就通过一个简单的例子来看一下一个fragment完整的生命周期的过程。
二、一个简单的例子
Layout代码(fragment.xml):
<?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"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:background="#FFCC00"
android:gravity="center"
android:text="TextView2 in Fragment" />
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:entries="@array/heros" >
</ListView>
</LinearLayout>
MyFragment代码:
package com.example.fragmentlifecycledemo;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MyFragment extends Fragment {
private final String TAG = "--MyFragment--> ";
@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
System.out.println(TAG + "onAttach");
super.onAttach(activity);
}
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
System.out.println(TAG + "onCreate");
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
System.out.println(TAG + "onCreateView");
View view = inflater.inflate(R.layout.fragment, null);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
System.out.println(TAG + "onActivityCreated");
super.onActivityCreated(savedInstanceState);
}
@Override
public void onStart() {
// TODO Auto-generated method stub
System.out.println(TAG + "onStart");
super.onStart();
}
@Override
public void onResume() {
// TODO Auto-generated method stub
System.out.println(TAG + "onResume");
super.onResume();
}
@Override
public void onPause() {
// TODO Auto-generated method stub
System.out.println(TAG + "onPause");
super.onPause();
}
@Override
public void onStop() {
// TODO Auto-generated method stub
System.out.println(TAG + "onStop");
super.onStop();
}
@Override
public void onDestroyView() {
// TODO Auto-generated method stub
System.out.println(TAG + "onDestroyView");
super.onDestroyView();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
System.out.println(TAG + "onDestroy");
super.onDestroy();
}
@Override
public void onDetach() {
// TODO Auto-generated method stub
System.out.println(TAG + "onDetach");
super.onDetach();
}
}
可以看到上面的代码是fragment类和它所要加载的布局文件,并在fragment中声明了生命周期中的每个方法以及打印语句。fragment的布局文件很简单,只有一个TextView和一个固定内容的ListView。下面看看Activity的代码和布局文件。
Layout代码(activity_main.xml):
<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"
android:orientation="vertical"
tools:context="com.example.fragmentlifecycledemo.MainActivity" >
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#FFDDFF"
android:gravity="center"
android:height="0dp"
android:text="TextView1 in MainActivity" />
<FrameLayout
android:id="@+id/lt_frame"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3" >
</FrameLayout>
</LinearLayout>
MainActivity代码:
package com.example.fragmentlifecycledemo;
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
public class MainActivity extends Activity {
private final String TAG = "--MainActivity---> ";
private FragmentManager manager;
private FragmentTransaction transaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
System.out.println(TAG + " onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = getFragmentManager();
transaction = manager.beginTransaction();
transaction.add(R.id.lt_frame, new MyFragment());
transaction.commit();
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
System.out.println(TAG + " onStart");
super.onStart();
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
System.out.println(TAG + " onResume");
super.onResume();
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
System.out.println(TAG + " onPause");
super.onPause();
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
System.out.println(TAG + " onStop");
super.onStop();
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
System.out.println(TAG + " onDestroy");
super.onDestroy();
}
}
下面看一下运行效果和LogCat控制台的打印结果:
可以看到fragment正常被加载到了Activity的FrameLayout中了,那么现在看看LogCat:
可以看到首先是Activity被创建,紧接着fragment与Activity绑定(onAttach),然后fragment被创建(onCreat),fragment的视图被创建(onCreatView),fragment所在的Activity的onCreate()方法已经被返回(onActivityCreated),最后Actvity启动,fragment启动,Activity处于运行状态,fragment处于运行状态。
当我点返回键之后,再看一下运行效果和LogCat控制台的打印结果:
可以看到已经点击返回键退出应用了,那么现在看看LogCat:
可以看到退出的时候,首先是fragment暂停、Activity暂停,然后fragment停止、Activity停止,然后销毁fragment的视图(onDestoryView,与onCreatView相对应)、与之前绑定的Activity解除关联(onDetach,与onAttach相对应),最后是Activity的销毁。
通过上面的分析我们可以证实:
fragment的生命周期确实是伴随着Activity的生命周期变化的,并且它是依附于Activity的。先创建Activity,再创建fragment,先销毁fragment,再销毁Activity,这种层次结构的设计也是非常合理的,而且和“栈”的结构有些相似,即:“后进先出”。下面将记录fragment的另一个重要的概念,即“回退栈(back stack)”。
三、回退栈(back stack)
其实回退栈很简单,fragment的回退栈和Activity的回退栈是类似的,就是如果把一个fragment事务添加到回退栈,那么点返回按钮的时候也就可以看到上一次保存的fragment,不至于直接退出Activity。也就是通过这个方法来将fragment事务添加到回退栈的:
transaction.addToBackStack(String name);
Layout代码(f1.xml-->二级界面的布局文件):
<?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"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_textView_f1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.7"
android:background="#F7F7F7"
android:gravity="center"
android:text="我是HERO,我的名字是?"
android:textSize="20sp" />
<EditText
android:id="@+id/et_editText_e1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.15"
android:inputType="textPersonName"
android:hint="请输入英雄名" />
<Button
android:id="@+id/btn_button_b1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.15"
android:text="确定" />
</LinearLayout>
package com.example.fragmentlifecycledemo;
import android.app.Activity;
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.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
public class MyFragment extends Fragment {
private final String TAG = "--MyFragment--> ";
private ListView listView1;
private FragmentManager manager;
private FragmentTransaction transaction;
@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
System.out.println(TAG + "onAttach");
super.onAttach(activity);
}
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
System.out.println(TAG + "onCreate");
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
System.out.println(TAG + "onCreateView");
View view = inflater.inflate(R.layout.fragment, container, false);
listView1 = (ListView) view.findViewById(R.id.iv_listView_1);
manager = getFragmentManager();
transaction = manager.beginTransaction();
listView1.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// 创建二级Fragment对象
MySubFragment msf = new MySubFragment();
// 获取当前项的TextView
TextView tv1 = (TextView) view;
// 通过Bundle对象传递数据
Bundle bundle = new Bundle();
bundle.putCharSequence("heroName", tv1.getText().toString());
msf.setArguments(bundle);
// 当点击Item时将ListView替换成二级Fragment
transaction.replace(R.id.lt_frame, msf);
// 将事务添加到回退栈使得可以返回到上一级
transaction.addToBackStack(null);
transaction.commit();
}
});
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
System.out.println(TAG + "onActivityCreated");
super.onActivityCreated(savedInstanceState);
}
@Override
public void onStart() {
// TODO Auto-generated method stub
System.out.println(TAG + "onStart");
super.onStart();
}
@Override
public void onResume() {
// TODO Auto-generated method stub
System.out.println(TAG + "onResume");
super.onResume();
}
@Override
public void onPause() {
// TODO Auto-generated method stub
System.out.println(TAG + "onPause");
super.onPause();
}
@Override
public void onStop() {
// TODO Auto-generated method stub
System.out.println(TAG + "onStop");
super.onStop();
}
@Override
public void onDestroyView() {
// TODO Auto-generated method stub
System.out.println(TAG + "onDestroyView");
super.onDestroyView();
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
System.out.println(TAG + "onDestroy");
super.onDestroy();
}
@Override
public void onDetach() {
// TODO Auto-generated method stub
System.out.println(TAG + "onDetach");
super.onDetach();
}
}
package com.example.fragmentlifecycledemo;
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.EditText;
import android.widget.Toast;
public class MySubFragment extends Fragment implements OnClickListener {
private Button mButton;
private EditText editText1;
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View v = inflater.inflate(R.layout.f1, container, false);
editText1 = (EditText) v.findViewById(R.id.et_editText_e1);
mButton = (Button) v.findViewById(R.id.btn_button_b1);
mButton.setOnClickListener(this);
return v;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
}
@Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
}
@Override
public void onClick(View v) {
// 根据键值获取存放的英雄名字
String name = (String) getArguments().getCharSequence("heroName");
// 获取用户输入的文本
String input = editText1.getText().toString();
if (name.equals(input)) {
Toast.makeText(getActivity(), "回答正确!我的名字是:" + name,
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getActivity(), "回答错误!我不告诉你我的名字!", Toast.LENGTH_LONG)
.show();
}
}
}
可以看到点击Item跳转之后,再点击返回按钮又可以成功返回到ListView,而不会直接退出程序,这就是fragment回退栈的作用了,其实在重构这些代码时我思考更多的是如何将ListView中Item的值传到二级fragment中,之前的做法是:直接在MyFragment的onItemClick方法中得到Item的View和二级Fragment的View,通过xxx.getText(“”+xxx.setText)这种方式去做,结果一直报错,后来整理了思路,结合上面学习的生命周期,我理解了fragment的View都是在各自的onCreatView方法中完成的,这样把代码分开才最终解决了问题。
四、结合回退栈再看生命周期
加了回退栈之后,我们有必要看看点击返回按钮之后生命周期的变化,下面就贴上从桌面进入APP、点击一项Item、点击返回、再点击返回退出APP的完整的生命周期输出信息。
注意红色方框一级Fragment跳到二级Fragment的时候只调用的onDestoryView(),并没有调用onDestory,而点击返回的时候,自然就直接调用onCreatView,这也正是回退栈的作用了。
五、总结
其实我想重构这个例子只是测试一下回退栈,但设计需求的同时又想把ListView中每个Item项的文本信息传到跳转之后的二级fragment中,所以我先理清思路,发现问题所在,并仔细思考了如何传递数据、在哪里保存数据又应该在哪里读取数据,最后完成需求。这样在无形之中又学到了更多,我也明白了学一个知识的时候不能仅仅满足于表面,往往在你发掘需求的同时就又会发现一些更有意思的事情,本篇先记录到这里,以后还要继续加油!更加努力!