本文目标教你如何用好Fragment,即Fragment的一些使用的建议,(多数内容来自: Android programming the big nerd ranch guide 一书,直接百度,你懂的,虽然是基础书籍,还是很值得一看的)。

1、概述

首先我们简单回顾一下,相信大家对Fragment的都不陌生,对于Fragment的使用,一方面Activity需要在布局中为Fragment安排位置,另一方面需要管理好Fragment的生命周期。Activity中有个FragmentManager,其内部维护fragment队列,以及fragment事务的回退栈。

一般情况下,我们在Activity里面会这么添加Fragment:

[java] view plaincopy
  1. public class MainActivity extends FragmentActivity
  2. {
  3. private ContentFragment mContentFragment  ;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState)
  6. {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. FragmentManager fm = getSupportFragmentManager();
  10. mContentFragment = (ContentFragment) fm.findFragmentById(R.id.id_fragment_container);
  11. if(mContentFragment == null )
  12. {
  13. mContentFragment = new ContentFragment();
  14. fm.beginTransaction().add(R.id.id_fragment_container,mContentFragment).commit();
  15. }
  16. }
  17. }

针对上面代码,问两个问题:

1、为什么需要判null呢?

主要是因为,当Activity因为配置发生改变(屏幕旋转)或者内存不足被系统杀死,造成重新创建时,我们的fragment会被保存下来,但是会创建新的FragmentManager,新的FragmentManager会首先会去获取保存下来的fragment队列,重建fragment队列,从而恢复之前的状态。

2、add(R.id.id_fragment_container,mContentFragment)中的布局的id有何作用?

一方面呢,是告知FragmentManager,此fragment的位置;另一方面是此fragment的唯一标识;就像我们上面通过fm.findFragmentById(R.id.id_fragment_container)查找~~

好了,简单回顾了一下基本用法,具体的还请参考上面的博客或者其他资料,接下来,介绍一些使用的意见~~

2、Fragment Arguments

下面描述一个简单的场景,比如我们某个按钮触发Activity跳转,需要通过Intent传递参数到目标Activity的Fragment中,那么此Fragment如何获取当前的Intent的值呢?

有哥们会说,这个简单?看我的代码(问题代码):

[java] view plaincopy
  1. public class ContentFragment extends Fragment
  2. {
  3. private String mArgument ;
  4. public static final String ARGUMENT ="argument";
  5. @Override
  6. public void onCreate(Bundle savedInstanceState)
  7. {
  8. super.onCreate(savedInstanceState);
  9. mArgument = getActivity().getIntent().getStringExtra(ARGUMENT);
  10. }

我们直接在Fragment的onCreate中,拿到宿主Activty,宿主Activity中肯定能通过getIntent拿到Intent,然后通过get方法,随意拿参数~~

这么写,功能上是实现了,但是呢?存在一个大问题:我们用Fragment的一个很大的原因,就是为了复用。你这么写,相当于这个Fragment已经完全和当前这个宿主Activity绑定了,复用直接废了~~~所以呢?我们换种方式,推荐使用arguments来创建Fragment。

[java] view plaincopy
  1. public class ContentFragment extends Fragment
  2. {
  3. private String mArgument;
  4. public static final String ARGUMENT = "argument";
  5. @Override
  6. public void onCreate(Bundle savedInstanceState)
  7. {
  8. super.onCreate(savedInstanceState);
  9. // mArgument = getActivity().getIntent().getStringExtra(ARGUMENT);
  10. Bundle bundle = getArguments();
  11. if (bundle != null)
  12. mArgument = bundle.getString(ARGUMENT);
  13. }
  14. /**
  15. * 传入需要的参数,设置给arguments
  16. * @param argument
  17. * @return
  18. */
  19. public static ContentFragment newInstance(String argument)
  20. {
  21. Bundle bundle = new Bundle();
  22. bundle.putString(ARGUMENT, argument);
  23. ContentFragment contentFragment = new ContentFragment();
  24. contentFragment.setArguments(bundle);
  25. return contentFragment;
  26. }

给Fragment添加newInstance方法,将需要的参数传入,设置到bundle中,然后setArguments(bundle),最后在onCreate中进行获取;

这样就完成了Fragment和Activity间的解耦。当然了这里需要注意:

setArguments方法必须在fragment创建以后,添加给Activity前完成。千万不要,首先调用了add,然后设置arguments。

3、Fragment的startActivityForResult

依旧是一个简单的场景:两个Fragment,一个展示文章列表的Fragment(叫做ListTitleFragment),一个显示详细信息的Fragment(叫做:ContentFragment),当然了,这两个Fragment都有其宿主Activity。

现在,我们点击列表Fragment中的列表项,传入相应的参数,去详细信息的Fragment展示详细的信息,在详细信息页面,用户可以进行点评,当用户点击back以后,我们以往点评结果显示在列表的Fragment对于的列表项中;

也就是说,我们点击跳转到对应Activity的Fragment中,并且希望它能够返回参数,那么我们肯定是使用Fragment.startActivityForResult ;

在Fragment中存在startActivityForResult()以及onActivityResult()方法,但是呢,没有setResult()方法,用于设置返回的intent,这样我们就需要通过调用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);。

详细代码:

ListTitleFragment

[java] view plaincopy
  1. public class ListTitleFragment extends ListFragment
  2. {
  3. public static final int REQUEST_DETAIL = 0x110;
  4. private List<String> mTitles = Arrays.asList("Hello", "World", "Android");
  5. private int mCurrentPos ;
  6. private ArrayAdapter<String> mAdapter ;
  7. @Override
  8. public void onActivityCreated(Bundle savedInstanceState)
  9. {
  10. super.onActivityCreated(savedInstanceState);
  11. setListAdapter(mAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, mTitles));
  12. }
  13. @Override
  14. public void onListItemClick(ListView l, View v, int position, long id)
  15. {
  16. mCurrentPos = position ;
  17. Intent intent = new Intent(getActivity(),ContentActivity.class);
  18. intent.putExtra(ContentFragment.ARGUMENT, mTitles.get(position));
  19. startActivityForResult(intent, REQUEST_DETAIL);
  20. }
  21. @Override
  22. public void onActivityResult(int requestCode, int resultCode, Intent data)
  23. {
  24. Log.e("TAG", "onActivityResult");
  25. super.onActivityResult(requestCode, resultCode, data);
  26. if(requestCode == REQUEST_DETAIL)
  27. {
  28. mTitles.set(mCurrentPos, mTitles.get(mCurrentPos)+" -- "+data.getStringExtra(ContentFragment.RESPONSE));
  29. mAdapter.notifyDataSetChanged();
  30. }
  31. }
  32. }

ContentFragment

[java] view plaincopy
  1. public class ContentFragment extends Fragment
  2. {
  3. private String mArgument;
  4. public static final String ARGUMENT = "argument";
  5. public static final String RESPONSE = "response";
  6. @Override
  7. public void onCreate(Bundle savedInstanceState)
  8. {
  9. super.onCreate(savedInstanceState);
  10. Bundle bundle = getArguments();
  11. if (bundle != null)
  12. {
  13. mArgument = bundle.getString(ARGUMENT);
  14. Intent intent = new Intent();
  15. intent.putExtra(RESPONSE, "good");
  16. getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);
  17. }
  18. }
  19. public static ContentFragment newInstance(String argument)
  20. {
  21. Bundle bundle = new Bundle();
  22. bundle.putString(ARGUMENT, argument);
  23. ContentFragment contentFragment = new ContentFragment();
  24. contentFragment.setArguments(bundle);
  25. return contentFragment;
  26. }
  27. @Override
  28. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  29. Bundle savedInstanceState)
  30. {
  31. Random random = new Random();
  32. TextView tv = new TextView(getActivity());
  33. tv.setText(mArgument);
  34. tv.setGravity(Gravity.CENTER);
  35. tv.setBackgroundColor(Color.argb(random.nextInt(100),
  36. random.nextInt(255), random.nextInt(255), random.nextInt(255)));
  37. return tv;
  38. }
  39. }

贴出了两个Fragment的代码,可以看到我们在ListTitleFragment.onListItemClick,使用startActivityForResult()跳转到目标Activity,在目标Activity的Fragment(ContentFragment)中获取参数,然后调用getActivity().setResult(ListTitleFragment.REQUEST_DETAIL, intent);进行设置返回的数据;最后在ListTitleFragment.onActivityResult()拿到返回的数据进行回显;

为大家以后在遇到类似问题时,提供了解决方案;也说明了一个问题:fragment能够从Activity中接收返回结果,但是其自设无法产生返回结果,只有Activity拥有返回结果。

接下来我要贴一下,这两个Fragment的宿主Activity:

ListTitleActivity

[java] view plaincopy
  1. public class ListTitleActivity extends FragmentActivity
  2. {
  3. private ListTitleFragment mListFragment;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState)
  6. {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_single_fragment);
  9. FragmentManager fm = getSupportFragmentManager();
  10. mListFragment = (ListTitleFragment) fm.findFragmentById(R.id.id_fragment_container);
  11. if(mListFragment == null )
  12. {
  13. mListFragment = new ListTitleFragment();
  14. fm.beginTransaction().add(R.id.id_fragment_container,mListFragment).commit();
  15. }
  16. }
  17. }

ContentActivity:

[java] view plaincopy
  1. public class ContentActivity extends FragmentActivity
  2. {
  3. private ContentFragment mContentFragment;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState)
  6. {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_single_fragment);
  9. FragmentManager fm = getSupportFragmentManager();
  10. mContentFragment = (ContentFragment) fm.findFragmentById(R.id.id_fragment_container);
  11. if(mContentFragment == null )
  12. {
  13. String title = getIntent().getStringExtra(ContentFragment.ARGUMENT);
  14. mContentFragment = ContentFragment.newInstance(title);
  15. fm.beginTransaction().add(R.id.id_fragment_container,mContentFragment).commit();
  16. }
  17. }
  18. }

有没有发现两个Activity中的代码极其的类似,且使用了同一个布局文件:

activity_single_fragment.xml

[html] view plaincopy
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:id="@+id/id_fragment_container"
  6. >
  7. </RelativeLayout>

为什么要贴这Acticity的代码呢?因为我们项目中,如果原则上使用Fragment,会发现大量类似的代码,那么我们就可以抽象一个Activity出来,托管我们的Single Fragment。

详细见下一节。

4、SingleFragmentActivity

于是抽象出来的Activity的代码为:

[java] view plaincopy
  1. package com.example.demo_zhy_23_fragments;
  2. import android.os.Bundle;
  3. import android.support.v4.app.Fragment;
  4. import android.support.v4.app.FragmentActivity;
  5. import android.support.v4.app.FragmentManager;
  6. public abstract class SingleFragmentActivity extends FragmentActivity
  7. {
  8. protected abstract Fragment createFragment();
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState)
  11. {
  12. super.onCreate(savedInstanceState);
  13. setContentView(R.layout.activity_single_fragment);
  14. FragmentManager fm = getSupportFragmentManager();
  15. Fragment fragment =fm.findFragmentById(R.id.id_fragment_container);
  16. if(fragment == null )
  17. {
  18. fragment = createFragment() ;
  19. fm.beginTransaction().add(R.id.id_fragment_container,fragment).commit();
  20. }
  21. }
  22. }

那么,有了这个SingleFragmentActivity,我们的ContentActivity和ListTitleActivity也能大变身了~

[java] view plaincopy
  1. package com.example.demo_zhy_23_fragments;
  2. import android.support.v4.app.Fragment;
  3. public class ContentActivity extends SingleFragmentActivity
  4. {
  5. private ContentFragment mContentFragment;
  6. @Override
  7. protected Fragment createFragment()
  8. {
  9. String title = getIntent().getStringExtra(ContentFragment.ARGUMENT);
  10. mContentFragment = ContentFragment.newInstance(title);
  11. return mContentFragment;
  12. }
  13. }
[java] view plaincopy
  1. package com.example.demo_zhy_23_fragments;
  2. import android.support.v4.app.Fragment;
  3. public class ListTitleActivity extends SingleFragmentActivity
  4. {
  5. private ListTitleFragment mListFragment;
  6. @Override
  7. protected Fragment createFragment()
  8. {
  9. mListFragment = new ListTitleFragment();
  10. return mListFragment;
  11. }
  12. }

是不是简洁很多,相信优先使用Fragment的项目,类似的Activity非常多,使用SingleFragmentActivity来简化你的代码吧~~

好了,此代码是来自文章开始推荐的书中的,再次推荐一下~~。

5、FragmentPagerAdapter与FragmentStatePagerAdapter

相信这两个PagerAdapter的子类,大家都不陌生吧~~自从Fragment问世,使用ViewPager再结合上面任何一个实例的制作APP主页的案例特别多~~~

那么这两个类有何区别呢?

主要区别就在与对于fragment是否销毁,下面细说:

FragmentPagerAdapter:对于不再需要的fragment,选择调用detach方法,仅销毁视图,并不会销毁fragment实例。

FragmentStatePagerAdapter:会销毁不再需要的fragment,当当前事务提交以后,会彻底的将fragmeng从当前Activity的FragmentManager中移除,state标明,销毁时,会将其onSaveInstanceState(Bundle outState)中的bundle信息保存下来,当用户切换回来,可以通过该bundle恢复生成新的fragment,也就是说,你可以在onSaveInstanceState(Bundle outState)方法中保存一些数据,在onCreate中进行恢复创建。

如上所说,使用FragmentStatePagerAdapter当然更省内存,但是销毁新建也是需要时间的。一般情况下,如果你是制作主页面,就3、4个Tab,那么可以选择使用FragmentPagerAdapter,如果你是用于ViewPager展示数量特别多的条目时,那么建议使用FragmentStatePagerAdapter。

篇幅原因,具体的案例就不写了,大家自行测试。

6、Fragment间的数据传递

上面3中,我们展示了,一般的两个Fragment间的数据传递。

那么还有一种比较特殊的情况,就是两个Fragment在同一个Activity中:例如,点击当前Fragment中按钮,弹出一个对话框(DialogFragment),在对话框中的操作需要返回给触发的Fragment中,那么如何数据传递呢?对于对话框的使用推荐:Android 官方推荐 : DialogFragment 创建对话框

我们继续修改我们的代码:现在是ListTitleFragment , ContentFragment , 添加一个对话框:EvaluateDialog,用户点击ContentFragment 内容时弹出一个评价列表,用户选择评价。

现在我们的关注点在于:ContentFragment中如何优雅的拿到EvaluateDialog中返回的评价:

记住我们在一个Activity中,那么肯定不是使用startActivityForResult;但是我们返回的数据,依然在onActivityResult中进行接收。

好了看代码:

ContentFragment

[java] view plaincopy
  1. public class ContentFragment extends Fragment
  2. {
  3. private String mArgument;
  4. public static final String ARGUMENT = "argument";
  5. public static final String RESPONSE = "response";
  6. public static final String EVALUATE_DIALOG = "evaluate_dialog";
  7. public static final int REQUEST_EVALUATE = 0X110;
  8. //...
  9. @Override
  10. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  11. Bundle savedInstanceState)
  12. {
  13. Random random = new Random();
  14. TextView tv = new TextView(getActivity());
  15. ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
  16. LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
  17. tv.setLayoutParams(params);
  18. tv.setText(mArgument);
  19. tv.setGravity(Gravity.CENTER);
  20. tv.setBackgroundColor(Color.argb(random.nextInt(100),
  21. random.nextInt(255), random.nextInt(255), random.nextInt(255)));
  22. // set click
  23. tv.setOnClickListener(new OnClickListener()
  24. {
  25. @Override
  26. public void onClick(View v)
  27. {
  28. EvaluateDialog dialog = new EvaluateDialog();
  29. //注意setTargetFragment
  30. dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);
  31. dialog.show(getFragmentManager(), EVALUATE_DIALOG);
  32. }
  33. });
  34. return tv;
  35. }
  36. //接收返回回来的数据
  37. @Override
  38. public void onActivityResult(int requestCode, int resultCode, Intent data)
  39. {
  40. super.onActivityResult(requestCode, resultCode, data);
  41. if (requestCode == REQUEST_EVALUATE)
  42. {
  43. String evaluate = data
  44. .getStringExtra(EvaluateDialog.RESPONSE_EVALUATE);
  45. Toast.makeText(getActivity(), evaluate, Toast.LENGTH_SHORT).show();
  46. Intent intent = new Intent();
  47. intent.putExtra(RESPONSE, evaluate);
  48. getActivity().setResult(Activity.REQUEST_OK, intent);
  49. }
  50. }
  51. }

删除了一些无关代码,注意看,我们在onCreateView中为textview添加了click事件,用于弹出我们的dialog,注意一行代码:

dialog.setTargetFragment(ContentFragment.this, REQUEST_EVALUATE);

我们调用了Fragment.setTargetFragment ,这个方法,一般就是用于当前fragment由别的fragment启动,在完成操作后返回数据的,符合我们的需求吧~~~注意,这句很重要。

接下来看EvaluateDialog代码:

[java] view plaincopy
  1. package com.example.demo_zhy_23_fragments;
  2. import android.app.Activity;
  3. import android.app.AlertDialog;
  4. import android.app.Dialog;
  5. import android.content.DialogInterface;
  6. import android.content.DialogInterface.OnClickListener;
  7. import android.content.Intent;
  8. import android.os.Bundle;
  9. import android.support.v4.app.DialogFragment;
  10. public class EvaluateDialog extends DialogFragment
  11. {
  12. private String[] mEvaluteVals = new String[] { "GOOD", "BAD", "NORMAL" };
  13. public static final String RESPONSE_EVALUATE = "response_evaluate";
  14. @Override
  15. public Dialog onCreateDialog(Bundle savedInstanceState)
  16. {
  17. AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
  18. builder.setTitle("Evaluate :").setItems(mEvaluteVals,
  19. new OnClickListener()
  20. {
  21. @Override
  22. public void onClick(DialogInterface dialog, int which)
  23. {
  24. setResult(which);
  25. }
  26. });
  27. return builder.create();
  28. }
  29. // 设置返回数据
  30. protected void setResult(int which)
  31. {
  32. // 判断是否设置了targetFragment
  33. if (getTargetFragment() == null)
  34. return;
  35. Intent intent = new Intent();
  36. intent.putExtra(RESPONSE_EVALUATE, mEvaluteVals[which]);
  37. getTargetFragment().onActivityResult(ContentFragment.REQUEST_EVALUATE,
  38. Activity.RESULT_OK, intent);
  39. }
  40. }

重点就是看点击后的setResult了,我们首先判断是否设置了targetFragment,如果设置了,意味我们要返回一些数据到targetFragment。

我们创建intent封装好需要传递数据,最后手动调用onActivityResult进行返回数据~~

最后我们在ContentFragment的onActivityResult接收即可。

ok,终于把这些tips贯穿到一起了,到此我们的Fragment的一些建议的用法就结束了~~~那么,最后提供下源码,也顺便贴个效果图:

Android Fragment 你应该知道的一切相关推荐

  1. Android Fragment 基本介绍

    Android Fragment 基本介绍 Android Fragment 基本介绍 Fragment Android是在Android 3.0 (API level 11)开始引入Fragment ...

  2. android Fragment 学习资料推荐

    为什么80%的码农都做不了架构师?>>>    android   Fragment 学习资料推荐:android大神 郭霖 http://blog.csdn.net/guolin_ ...

  3. 【转】基于Android Fragment功能的例子

    原文网址:http://blog.csdn.net/eyu8874521/article/details/8252216 通过最近空闲时候对Fragment的学习,尝试着写了一个小Demo,将在开发的 ...

  4. [转]Android fragment 重叠问题——通过hide,show方式导致的解决方法

    [转]Android fragment 重叠问题--通过hide,show方式导致的解决方法 参考文章: (1)[转]Android fragment 重叠问题--通过hide,show方式导致的解决 ...

  5. 【转】 Android Fragment 真正的完全解析(下)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37992017 上篇博客中已经介绍了Fragment产生原因,以及一些基本的用法和 ...

  6. Android Fragment 真正的完全解析(下)

    本篇将介绍上篇博客提到的:如何管理Fragment回退栈,Fragment如何与Activity交互,Fragment与Activity交互的最佳实践,没有视图的Fragment的用处,使用Fragm ...

  7. Android Fragment嵌套导致的bug

    原文链接 Android 多个Fragment嵌套导致的三大BUG Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常见错误 http ...

  8. Android Fragment使用(三) Activity, Fragment, WebView的状态保存和恢复

    Android中的状态保存和恢复 Android中的状态保存和恢复, 包括Activity和Fragment以及其中View的状态处理. Activity的状态除了其中的View和Fragment的状 ...

  9. Android Fragment 简单实例

    Android上的界面展示都是通过Activity实现的.Activity实在是太经常使用了.我相信大家都已经很熟悉了,这里就不再赘述. 可是Activity也有它的局限性,相同的界面在手机上显示可能 ...

  10. android 底部通知,Android Fragment实现底部通知栏

    Android Fragment实现底部通知栏,供大家参考,具体内容如下 截图如下: 1. 第一步先要创建fragment(动态注册) 然后将两个勾选取消掉(还有一种是自己手动创建) 会自动生成相对应 ...

最新文章

  1. 6. 简单又复杂的“运算符”,建议你看一哈
  2. C中堆管理—浅谈malloc,free,calloc,realloc函数之间的区别
  3. php邮件通知,邮件通知设置
  4. javascript常用验证大全
  5. COSMIC功能规模度量方法
  6. U3D开发中关于脚本方面的限制-有关IOS反射和JIT的支持问题
  7. android AlertDialog.Builder
  8. 川大计算机高考分数,2020年四川大学录取分数是多少 2020年高考多少分可以报考四川大学...
  9. 开发composer包
  10. OpenCV与图像处理学习五——图像滤波与增强:线性、非线性滤波、直方图均衡化与Gamma变换
  11. 开发者如何快速精简容器云镜像?| 技术头条
  12. php fping,【Linux 命令】fping ping 包间隔时间详解
  13. 东南亚ERP仓储管理系统怎么样?
  14. All xxx functions were compiled because no usable IPDB/IOBJ from previous compilation was found.
  15. Netty网络编程聊天项目
  16. cs231n-svm作业
  17. app测试用例考虑点
  18. laypage ajax,laypage.js分页插件使用方法详解
  19. 如何修改网络游戏服务器端的数据,网络游戏服务器端编程:数据操纵语句
  20. 蜜雪冰城“土”里刨金

热门文章

  1. linux ctrl+r撤销_Linux入门基础,掌握这些命令,不再做小白
  2. python django报错 no such column:
  3. php数组转字符串 join,jQuery中将数组转换成字符串join()和push()使用
  4. 什么是java一句话一个例子_一句话讲清楚什么是JavaEE
  5. calico跨主机ping不通_戳穿 Calico 的谎言
  6. 19.VS属性管理器窗口不见了怎么办?
  7. 七、DNS报文及抓包分析
  8. matlab“机器学习和深度学习”系列工具箱作用总结
  9. skipping non-radio button in group解决方法
  10. vue避免重新渲染_小白也能懂的VUE的生命周期探寻