处女男学Android(九)---Fragment进阶篇之Fragment生命周期和回退栈



前言



上一篇blog(处女男学Android(八)---Fragment初体验之实现Tab导航)记录了fragment的基本概念和基本的使用方法,本篇将逐步深入记录关于fragment的几个重要知识点,包括:fragment的生命周期、fragment的back stack(回退栈)等等,下面就从fragmeng的生命周期说起。



一、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();
	}

}


类似的,同样声明了Activity生命周期中的所有方法以及打印语句,并且当Activity创建时将MyFragment动态添加到activity_main.xml中的FrameLayout布局文件里,可以看到这个布局文件是空的,所以它就相当于用来装fragment的容器了。


下面看一下运行效果和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);


下面通过例子来看看回退栈的应用,顺便再说说我在学习回退栈的同时学到的其它相关知识,对于我来说还是很有意义的。需求是这样的:同样是上面的英雄列表,当我点击ListView的某一项时,下面的fragment就替换成一个新的界面,这个界面有一个文本框,需要输入当前项的英雄名称,还有一个确定按钮,点击确定时,如果输入的和当前项的英雄名一致,就弹出土司提示正确并附加英雄名,如果输入的和当前项的英雄名不一致,也弹出土司提示错误,但不显示英雄名。重点是,我在二级界面点击返回的时候,要跳回ListView的界面,下面贴上我重构后的代码。


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>

MyFragment代码(只重构了onCreatView方法):

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();
	}

}

二级Fragment代码:

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中,所以我先理清思路,发现问题所在,并仔细思考了如何传递数据、在哪里保存数据又应该在哪里读取数据,最后完成需求。这样在无形之中又学到了更多,我也明白了学一个知识的时候不能仅仅满足于表面,往往在你发掘需求的同时就又会发现一些更有意思的事情,本篇先记录到这里,以后还要继续加油!更加努力!




  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值