android fragment详解,安卓Fragment使用详解

Fragment是在安卓3.0版本中添加的,主要是为了解决安卓设备尺寸多样化后界面的显示问题。Fragment比Activity较轻量级,也可以提供与用户交互的界面并且有自己的生命周期,也不用在Manifest.xml中注册但它必须嵌套在Activity中使用。之前需要使用多个Activity显示的内容,现在可以用一个Activity嵌套多个Fragment来实现。下面就来说说Fragment的知识点和使用注意事项。

一 Fragment的生命周期

先来一张google官网Fragment的生命周期图

12bac945a6e4

fragment_liftcycle.png

Fragment的生命周期方法与Activity的类似但比Activity的稍微多了点,虽然多了点,对比着Activity的生命周期也是不难记忆滴。注意一点,Fragment的生命周期不是从new Fragment()开始的,而是从Fragment增加到Activity开始的。

二 Fragment的管理方式

Fragment需要嵌套在Activity中使用,Activity通过FragmentManager管理其拥有的Fragment。由于Fragment在3.0以后才有,为了兼容之前的版本google在V4包中添加了

android.support.v4.app.Fragment

android.support.v4.app.FragmentManager

android.support.v4.app.FragmentTransaction

需不需要兼容3.0以下版本根据产品来决定,但在开发过程中一定要保持统一,就是使用V4包下面的Fragment对应的FragmentManager,FragmentTransaction也使用V4包下面的,否则会编译报错。不同的管理方式会走Fragment不同的生命周期方法,下面来分别说下Activity通过FragmentManager管理Fragment的几种方式:

replace(替换模式)

所谓替换模式就是通过调用public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag)方法根据需求显示不同的Fragment,来看下google文档对这个方法的说明

Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here

大致意思就是说在容器(放Fragment的布局)中已经被add的Fragment都会被替换,相当于调用了remove方法,然后把新的Fragment添加到相同的容器中。

我们来通过实例演示下replace模式Fragment的生命周期,我们有两个Fragment 分别为BlankFragment1 和 BlankFragment2 ,通过MainActivity来管理它们,在MainActivity的onCreate方法中我们先Add BlankFragment1,代码如下:

FragmentManager fragmentManager = getSupportFragmentManager();

FragmentTransaction ft = fragmentManager.beginTransaction();

blankFragment1 = new BlankFragment1();

blankFragment2 = new BlankFragment2();

Log.e(getClass().getSimpleName(),"");

ft.add(R.id.fl_frame,blankFragment1,tag);

ft.commit();

这样当我们打开应用进入MainActivity,就会先把blankFragment1添加到Activity,此时打印blankFragment1的生命周期如下:

07-18 10:02:17.485 719-719/com.jrmf360.fragmentliftcycle E/MainActivity:

07-18 10:02:17.488 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onAttach

07-18 10:02:17.489 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onCreate

07-18 10:02:17.490 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onCreateView

07-18 10:02:17.492 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onActivityCreated

07-18 10:02:17.492 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onStart

07-18 10:02:17.493 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onResume

然后我们在MainActivity中添加两个按钮 btn_fragment2和btn_fragment1,点击各个按钮的代码逻辑如下:

@Override

public void onClick(View v) {

int id = v.getId();

if (id == R.id.btn_fragment2){

Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);

if (fragment != null){

Log.e(getClass().getSimpleName(),"");

getSupportFragmentManager().beginTransaction()

.replace(R.id.fl_frame,blankFragment2,tag2)

.commit();

}

}else if (id == R.id.btn_fragment1){

Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag2);

if (fragment != null){

Log.e(getClass().getSimpleName(),"");

getSupportFragmentManager().beginTransaction()

.replace(R.id.fl_frame,blankFragment1,tag)

.commit();

}

}

}

从代码中可以看到,当点击btn_fragment2时,通过replace()方法把当前已经增加到容器中的Fragment(其实就是blankFragment1)替换成blankFragment2;当点击btn_fragment1按钮时,把已经增加到容器中的Fragment(blankFragment2)替换成blankFragment1。在上面已经在onCreate()方法中Add了blankFragment1的基础上,下面我们来看看调用replace()方法Fragment的生命周期

点击btn_fragment2

当点击btn_fragment2时,blankFragment1被替换成blankFragment2,生命周期如下:

07-18 10:14:24.593 719-719/com.jrmf360.fragmentliftcycle E/MainActivity:

07-18 10:14:24.595 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onAttach

07-18 10:14:24.595 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onCreate

07-18 10:14:24.596 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onPause

07-18 10:14:24.596 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onStop

07-18 10:14:24.597 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onDestroyView

07-18 10:14:24.599 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onDestroy

07-18 10:14:24.599 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onDetach

07-18 10:14:24.600 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onCreateView

07-18 10:14:24.605 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onActivityCreated

07-18 10:14:24.606 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onStart

07-18 10:14:24.606 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onResume

可以看到blankFragment1销毁了,blankFragment2创建并显示。

点击btn_fragment1

当点击btn_fragment1时,blankFragment2被替换成blankFragment1,生命周期如下:

07-18 10:16:37.395 719-719/com.jrmf360.fragmentliftcycle E/MainActivity:

07-18 10:16:37.397 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onAttach

07-18 10:16:37.398 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onCreate

07-18 10:16:37.398 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onPause

07-18 10:16:37.398 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onStop

07-18 10:16:37.398 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onDestroyView

07-18 10:16:37.400 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onDestroy

07-18 10:16:37.401 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment2: onDetach

07-18 10:16:37.401 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onCreateView

07-18 10:16:37.407 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onActivityCreated

07-18 10:16:37.407 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onStart

07-18 10:16:37.407 719-719/com.jrmf360.fragmentliftcycle E/BlankFragment1: onResume

可以看到blankFragment2被销毁,blankFragment1被重新创建。

由此可见通过replace(替换模式),已经存在的Fragment会被完全销毁,替换的Fragment会被重新创建。这样每次用户点击都会重新加载页面有时并伴有大量的网络请求去获得数据,一般我们不会使用这种模式,除非需要每次加载最新数据。

show(显示)hide(隐藏)模式

在MainActivity的onCreate()方法中,我们先Add blankFragment1并把mCurrFragment设置为blankFragment1,代码如下:

FragmentManager fragmentManager = getSupportFragmentManager();

FragmentTransaction ft = fragmentManager.beginTransaction();

blankFragment1 = new BlankFragment1();

blankFragment2 = new BlankFragment2();

Log.e(getClass().getSimpleName(),"");

ft.add(R.id.fl_frame,blankFragment1,tag);

ft.commit();

mCurrFragment = blankFragment1;

然后给按钮添加点击事件

@Override

public void onClick(View v) {

int id = v.getId();

if (id == R.id.btn_fragment2){

if (blankFragment2.isAdded()){

if (blankFragment2.isHidden()){

Log.e(getClass().getSimpleName(),"");

getSupportFragmentManager().beginTransaction().hide(mCurrFragment).show(blankFragment2).commit();

}

}else{

Log.e(getClass().getSimpleName(),"");

getSupportFragmentManager().beginTransaction().hide(mCurrFragment).add(R.id.fl_frame,blankFragment2).commit();

}

mCurrFragment = blankFragment2;

}else if (id == R.id.btn_fragment1){

if (blankFragment1.isAdded()){

if (blankFragment1.isHidden()){

Log.e(getClass().getSimpleName(),"");

getSupportFragmentManager().beginTransaction().hide(mCurrFragment).show(blankFragment1).commit();

}

}else{

Log.e(getClass().getSimpleName(),"");

getSupportFragmentManager().beginTransaction().hide(mCurrFragment).add(R.id.fl_frame,blankFragment1).commit();

}

mCurrFragment = blankFragment1;

}

}

第一次点击btn_fragment2

第一次点击btn_fragment2,此时blankFragment2并没有添加到容器,因此会先Add blankFragment2,日志信息如下:

07-18 10:50:16.755 4444-4444/com.jrmf360.fragmentliftcycle E/MainActivity:

07-18 10:50:16.757 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment2: onAttach

07-18 10:50:16.757 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment2: onCreate

07-18 10:50:16.758 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment1: onHiddenChanged:true

07-18 10:50:16.758 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment2: onCreateView

07-18 10:50:16.764 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment2: onActivityCreated

07-18 10:50:16.764 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment2: onStart

07-18 10:50:16.764 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment2: onResume

此时blankFragment2走正常的添加流程,但是我们看到日志中打印了 onHiddenChanged:true,这是因为我们在Add blankFragment2时,隐藏了blankFragment1,所以调用了blankFragment1的public void onHiddenChanged(boolean hidden)方法,下面来看下这个方法:

@Override

public void onHiddenChanged(boolean hidden) {

super.onHiddenChanged(hidden);

Log.e(getClass().getSimpleName(),"onHiddenChanged:" + hidden);

}

此方法当Fragment Hide或者show时调用,hidden 为true 表示当前Fragment隐藏。

继续点击btn_fragment1

此时blankFragment1,和blankFragment2都已经Add到了容器中,相关日志为:

07-18 10:50:20.722 4444-4444/com.jrmf360.fragmentliftcycle E/MainActivity:

07-18 10:50:20.724 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment2: onHiddenChanged:true

07-18 10:50:20.726 4444-4444/com.jrmf360.fragmentliftcycle E/BlankFragment1: onHiddenChanged:false

可以看到并没有走Fragment的销毁和重新创建流程,而是把直接把blankFragment2隐藏并显示blankFragment1。此种方式可以很好的复用已经存在的Fragment。

attach和detach模式

我们常用的ViewPager就是使用这种模式来管理Fragment的,这种方式Fragment并不会完全销毁而仅仅是调用onDestroyView()方法来销毁view层级,然后需要该Fragment的时候也不会重新创建该Fragment而仅仅是从onCreateView()方法开始调用,重新构建该Fragment的view层级。下面贴一下FragmentPagerAdapter显示和销毁Fragment的源码

显示Fragment

public Object instantiateItem(ViewGroup container, int position) {

if (mCurTransaction == null) {

mCurTransaction = mFragmentManager.beginTransaction();

}

final long itemId = getItemId(position);

// Do we already have this fragment?

String name = makeFragmentName(container.getId(), itemId);

Fragment fragment = mFragmentManager.findFragmentByTag(name);

if (fragment != null) {

if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);

//已经存在的Fragment直接attach

mCurTransaction.attach(fragment);

} else {

fragment = getItem(position);

if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);

//不存在的Fragment先Add

mCurTransaction.add(container.getId(), fragment,

makeFragmentName(container.getId(), itemId));

}

if (fragment != mCurrentPrimaryItem) {

fragment.setMenuVisibility(false);

fragment.setUserVisibleHint(false);

}

return fragment;

}

销毁Fragment

@Override

public void destroyItem(ViewGroup container, int position, Object object) {

if (mCurTransaction == null) {

mCurTransaction = mFragmentManager.beginTransaction();

}

if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object

+ " v=" + ((Fragment)object).getView());

//直接通过detach方法销毁Fragment的View层级

mCurTransaction.detach((Fragment)object);

}

为了验证结论我们用ViewPager加载三个Fragment分别为BlankFragment1,BlankFragment2,BlankFragment3且ViewPager的OffscreenPageLimit为1,这样当显示BlankFragment3时,BlankFragment1就会被detach,看下日志:

07-18 15:04:44.015 23543-23543/com.jrmf360.fragmentliftcycle E/MainActivity:

07-18 15:04:44.652 23543-23543/com.jrmf360.fragmentliftcycle E/BlankFragment1: onPause

07-18 15:04:44.653 23543-23543/com.jrmf360.fragmentliftcycle E/BlankFragment1: onStop

07-18 15:04:44.653 23543-23543/com.jrmf360.fragmentliftcycle E/BlankFragment1: onDestroyView

通过日志看出BlankFragment1调用了onDestroyView方法,销毁了view层级。然后我们在显示BlankFragment2,重新加载BlankFragment1,看下打印日志:

07-18 15:04:46.421 23543-23543/com.jrmf360.fragmentliftcycle E/MainActivity:

07-18 15:04:46.853 23543-23543/com.jrmf360.fragmentliftcycle E/BlankFragment1: onCreateView

07-18 15:04:46.862 23543-23543/com.jrmf360.fragmentliftcycle E/BlankFragment1: onActivityCreated

07-18 15:04:46.863 23543-23543/com.jrmf360.fragmentliftcycle E/BlankFragment1: onStart

07-18 15:04:46.863 23543-23543/com.jrmf360.fragmentliftcycle E/BlankFragment1: onResume

可以看到BlankFragment1并没有新建而是从onCreateView方法开始执行。

以上就是Fragment的三种管理方式,我们根据具体的需求选择合适的方式,以达到事半功倍的效果。

三 Fragment的数据传递

Fragment的数据传递这个大家都比较熟悉了,这里就分三种情况简单说下。

Activity向Fragment传递数据

方式一

Activity向Fragment传递数据如果是新建Fragment的时候我们可以通过Bundle传递,看一段google新建Fragment的代码

public static BlankFragment4 newInstance(String param1, String param2) {

BlankFragment4 fragment = new BlankFragment4();

Bundle args = new Bundle();

args.putString(ARG_PARAM1, param1);

args.putString(ARG_PARAM2, param2);

fragment.setArguments(args);

return fragment;

}

在new Fragment的时候通过Bundle传递数据,并调用fragment.setArguments()方法给Fragment设置bundle。那么怎么获得bundle中的数据呢,继续看google的示例代码

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (getArguments() != null) {

mParam1 = getArguments().getString(ARG_PARAM1);

mParam2 = getArguments().getString(ARG_PARAM2);

}

}

通过getArguments()方法获得bundle,然后拿到bundle中的数据。

方式二

在Activity中通过调用getSupportFragmentManager().findFragmentByTag(tag)方法,获得对应的Fragment,拿到Fragment对象以后就可以调用该对象的方法去设置数据。

Fragment向Activity传递数据

方式一

通过在Fragment中调用getActivity()方法获得与之绑定的Activity对象,拿到Activity对象之后就可以调用Activity中的方法传递数据。

方式二

通过回调接口的方式,这种方式也是google推荐的做法,继续看段google代码示例:

/**

* 在Fragment中创建接口

*/

public interface OnFragmentInteractionListener {

void onFragmentInteraction(Uri uri);

}

/**

* 在onAttach把传递过来的context也就是与Fragment关联的Activity

* 给属性 OnFragmentInteractionListener mListener 赋值。这就需要

* 对应的Activity实现OnFragmentInteractionListener接口

*

* @param context

*/

@Override

public void onAttach(Context context) {

super.onAttach(context);

if (context instanceof OnFragmentInteractionListener) {

mListener = (OnFragmentInteractionListener) context;

} else {

throw new RuntimeException(context.toString()

+ " must implement OnFragmentInteractionListener");

}

}

/**

* 当Fragment与对应的Activity解绑时释放mListener

*/

@Override

public void onDetach() {

super.onDetach();

mListener = null;

}

Fragment与Fragment传递数据

在Fragment中调用 getActivity().getSupportFragmentManager().findFragmentByTag(tag),找到对应的Fragment对象,通过对象直接调用方法传递数据。

数据传递就简单介绍到这里,当然并不全面,欢迎各位大神补充。

四 创建Fragment并传递参数

Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().

英语太烂,翻译不到位的还请各位高台贵手。大致意思就是说:每个Fragment都必须有一个无参构造函数,当Fragment的状态发生变化重新初始化时可以调用该构造函数创建。并且强烈建议不要有其它的构造函数,因为这些构造函数在重新初始化的时候不会被调用,因此传递的参数也就丢失了。可以通过setArguments(Bundle)方式传递参数,当Fragment重新初始化的时候会从新获得bundle中传递的参数。

看了文档也就明白了我们要使用如下方式创建Fragment:

public static BlankFragment4 newInstance(String param1, String param2) {

BlankFragment4 fragment = new BlankFragment4();

Bundle args = new Bundle();

args.putString(ARG_PARAM1, param1);

args.putString(ARG_PARAM2, param2);

fragment.setArguments(args);

return fragment;

}

拒绝通过构造函数传递参数的方式创建Fragment。

关于Fragment的知识就介绍到这里,如果有不对的地方还望各位大神指正~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值