转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37992017

Hongyang

  • 目录视图
  • 摘要视图
  • 订阅
有奖征资源,博文分享有内涵         6月推荐文章汇总        CSDN博文大赛初赛晋级名单公布      

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

分类: android进阶 2014-07-21 09:09  1079人阅读  评论(7)  收藏  举报
Android Fragment DialogFragment MenuItem

目录(?)[+]

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37992017

上篇博客中已经介绍了Fragment产生原因,以及一些基本的用法和各种API,如果你还不了解,请看:Android Fragment 真正的完全解析(上)。

本篇将介绍上篇博客提到的:如何管理Fragment回退栈,Fragment如何与Activity交互,Fragment与Activity交互的最佳实践,没有视图的Fragment的用处,使用Fragment创建对话框,如何与ActionBar,MenuItem集成等~~

1、管理Fragment回退栈

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

看这样一个效果图:

点击第一个按钮,切换到第二个界面,点击第二个按钮,切换到第三个界面,然后点击Back键依次回退。这像不像初学Android时的Activity跳转,当然了,这里肯定不是,不然我就跪了。这里是Fragment实现的,用户点击Back,实际是Fragment回退栈不断的弹栈。

如何添加一个Fragment事务到回退栈:

FragmentTransaction.addToBackStack(String)

下面讲解代码:很明显一共是3个Fragment和一个Activity.

先看Activity的布局文件:

[html]  view plain copy
  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. <FrameLayout
  6. android:id="@+id/id_content"
  7. android:layout_width="fill_parent"
  8. android:layout_height="fill_parent" >
  9. </FrameLayout>
  10. </RelativeLayout>

不同的Fragment就在这个FrameLayout中显示。

MainActivity.java

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. public class MainActivity extends Activity
  8. {
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState)
  11. {
  12. super.onCreate(savedInstanceState);
  13. requestWindowFeature(Window.FEATURE_NO_TITLE);
  14. setContentView(R.layout.activity_main);
  15. FragmentManager fm = getFragmentManager();
  16. FragmentTransaction tx = fm.beginTransaction();
  17. tx.add(R.id.id_content, new FragmentOne(),"ONE");
  18. tx.commit();
  19. }
  20. }

很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.

下面是FragmentOne

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.View.OnClickListener;
  9. import android.view.ViewGroup;
  10. import android.widget.Button;
  11. public class FragmentOne extends Fragment implements OnClickListener
  12. {
  13. private Button mBtn;
  14. @Override
  15. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  16. Bundle savedInstanceState)
  17. {
  18. View view = inflater.inflate(R.layout.fragment_one, container, false);
  19. mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
  20. mBtn.setOnClickListener(this);
  21. return view;
  22. }
  23. @Override
  24. public void onClick(View v)
  25. {
  26. FragmentTwo fTwo = new FragmentTwo();
  27. FragmentManager fm = getFragmentManager();
  28. FragmentTransaction tx = fm.beginTransaction();
  29. tx.replace(R.id.id_content, fTwo, "TWO");
  30. tx.addToBackStack(null);
  31. tx.commit();
  32. }
  33. }

我们在点击FragmentOne中的按钮时,使用了replace方法,如果你看了前一篇博客,一定记得replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用tx.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,证据就是:仔细看上面的效果图,我们在跳转前在文本框输入的内容,在用户Back得到第一个界面的时候不见了。

接下来FragmentTwo

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.View.OnClickListener;
  9. import android.view.ViewGroup;
  10. import android.widget.Button;
  11. public class FragmentTwo extends Fragment implements OnClickListener
  12. {
  13. private Button mBtn ;
  14. @Override
  15. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  16. Bundle savedInstanceState)
  17. {
  18. View view = inflater.inflate(R.layout.fragment_two, container, false);
  19. mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
  20. mBtn.setOnClickListener(this);
  21. return view ;
  22. }
  23. @Override
  24. public void onClick(View v)
  25. {
  26. FragmentThree fThree = new FragmentThree();
  27. FragmentManager fm = getFragmentManager();
  28. FragmentTransaction tx = fm.beginTransaction();
  29. tx.hide(this);
  30. tx.add(R.id.id_content , fThree, "THREE");
  31. //      tx.replace(R.id.id_content, fThree, "THREE");
  32. tx.addToBackStack(null);
  33. tx.commit();
  34. }
  35. }

这里点击时,我们没有使用replace,而是先隐藏了当前的Fragment,然后添加了FragmentThree的实例,最后将事务添加到回退栈。这样做的目的是为了给大家提供一种方案:如果不希望视图重绘该怎么做,请再次仔细看效果图,我们在FragmentTwo的EditText填写的内容,用户Back回来时,数据还在~~~

最后FragmentThree就是简单的Toast了:

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. import android.widget.Toast;
  10. public class FragmentThree extends Fragment implements OnClickListener
  11. {
  12. private Button mBtn;
  13. @Override
  14. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  15. Bundle savedInstanceState)
  16. {
  17. View view = inflater.inflate(R.layout.fragment_three, container, false);
  18. mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);
  19. mBtn.setOnClickListener(this);
  20. return view;
  21. }
  22. @Override
  23. public void onClick(View v)
  24. {
  25. Toast.makeText(getActivity(), " i am a btn in Fragment three",
  26. Toast.LENGTH_SHORT).show();
  27. }
  28. }

好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。

这里极其注意一点:上面的整体代码不具有任何参考价值,纯粹为了显示回退栈,在后面讲解了Fragment与Activity通信以后,会重构上面的代码!

2、Fragment与Activity通信

因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。

3、Fragment与Activity通信的最佳实践

因为要考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。

下面我通过两种方式的代码,分别重构,FragmentOne和FragmentTwo的点击事件,以及Activity对点击事件的响应:

首先看FragmentOne

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. public class FragmentOne extends Fragment implements OnClickListener
  10. {
  11. private Button mBtn;
  12. /**
  13. * 设置按钮点击的回调
  14. * @author zhy
  15. *
  16. */
  17. public interface FOneBtnClickListener
  18. {
  19. void onFOneBtnClick();
  20. }
  21. @Override
  22. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  23. Bundle savedInstanceState)
  24. {
  25. View view = inflater.inflate(R.layout.fragment_one, container, false);
  26. mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
  27. mBtn.setOnClickListener(this);
  28. return view;
  29. }
  30. /**
  31. * 交给宿主Activity处理,如果它希望处理
  32. */
  33. @Override
  34. public void onClick(View v)
  35. {
  36. if (getActivity() instanceof FOneBtnClickListener)
  37. {
  38. ((FOneBtnClickListener) getActivity()).onFOneBtnClick();
  39. }
  40. }
  41. }

可以看到现在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;并且我们声明了一个接口,来回调其点击事件,想要管理其点击事件的Activity实现此接口就即可。可以看到我们在onClick中首先判断了当前绑定的Activity是否实现了该接口,如果实现了则调用。

再看FragmentTwo

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. public class FragmentTwo extends Fragment implements OnClickListener
  10. {
  11. private Button mBtn ;
  12. private FTwoBtnClickListener fTwoBtnClickListener ;
  13. public interface FTwoBtnClickListener
  14. {
  15. void onFTwoBtnClick();
  16. }
  17. //设置回调接口
  18. public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
  19. {
  20. this.fTwoBtnClickListener = fTwoBtnClickListener;
  21. }
  22. @Override
  23. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  24. Bundle savedInstanceState)
  25. {
  26. View view = inflater.inflate(R.layout.fragment_two, container, false);
  27. mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
  28. mBtn.setOnClickListener(this);
  29. return view ;
  30. }
  31. @Override
  32. public void onClick(View v)
  33. {
  34. if(fTwoBtnClickListener != null)
  35. {
  36. fTwoBtnClickListener.onFTwoBtnClick();
  37. }
  38. }
  39. }

与FragmentOne极其类似,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。

最后看Activity :

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;
  8. import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;
  9. public class MainActivity extends Activity implements FOneBtnClickListener,
  10. FTwoBtnClickListener
  11. {
  12. private FragmentOne mFOne;
  13. private FragmentTwo mFTwo;
  14. private FragmentThree mFThree;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState)
  17. {
  18. super.onCreate(savedInstanceState);
  19. requestWindowFeature(Window.FEATURE_NO_TITLE);
  20. setContentView(R.layout.activity_main);
  21. mFOne = new FragmentOne();
  22. FragmentManager fm = getFragmentManager();
  23. FragmentTransaction tx = fm.beginTransaction();
  24. tx.add(R.id.id_content, mFOne, "ONE");
  25. tx.commit();
  26. }
  27. /**
  28. * FragmentOne 按钮点击时的回调
  29. */
  30. @Override
  31. public void onFOneBtnClick()
  32. {
  33. if (mFTwo == null)
  34. {
  35. mFTwo = new FragmentTwo();
  36. mFTwo.setfTwoBtnClickListener(this);
  37. }
  38. FragmentManager fm = getFragmentManager();
  39. FragmentTransaction tx = fm.beginTransaction();
  40. tx.replace(R.id.id_content, mFTwo, "TWO");
  41. tx.addToBackStack(null);
  42. tx.commit();
  43. }
  44. /**
  45. * FragmentTwo 按钮点击时的回调
  46. */
  47. @Override
  48. public void onFTwoBtnClick()
  49. {
  50. if (mFThree == null)
  51. {
  52. mFThree = new FragmentThree();
  53. }
  54. FragmentManager fm = getFragmentManager();
  55. FragmentTransaction tx = fm.beginTransaction();
  56. tx.hide(mFTwo);
  57. tx.add(R.id.id_content, mFThree, "THREE");
  58. // tx.replace(R.id.id_content, fThree, "THREE");
  59. tx.addToBackStack(null);
  60. tx.commit();
  61. }
  62. }

代码重构结束,与开始的效果一模一样。上面两种通信方式都是值得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然Fragment和Activity可以通过getActivity与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。

4、如何处理运行时配置发生变化

运行时配置发生变化,最常见的就是屏幕发生旋转,如果你不知道如何处理屏幕变化可以参考:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

这里提一下:很多人觉得强制设置屏幕的方向就可以了,但是有一点,当你的应用被至于后台(例如用户点击了home),长时间没有返回的时候,你的应用也会被重新启动。比如上例:如果你把上面的例子你至于FragmentThree界面,然后处于后台状态,长时间后你会发现当你再次通过home打开时,上面FragmentThree与FragmentOne叠加在一起,这就是因为你的Activity重新启动,在原来的FragmentThree上又绘制了一个FragmentOne。

好了,下面看一段代码:

Activity:

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. public class MainActivity extends Activity
  8. {
  9. private FragmentOne mFOne;
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState)
  12. {
  13. super.onCreate(savedInstanceState);
  14. requestWindowFeature(Window.FEATURE_NO_TITLE);
  15. setContentView(R.layout.activity_main);
  16. mFOne = new FragmentOne();
  17. FragmentManager fm = getFragmentManager();
  18. FragmentTransaction tx = fm.beginTransaction();
  19. tx.add(R.id.id_content, mFOne, "ONE");
  20. tx.commit();
  21. }
  22. }

Fragment

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. public class FragmentOne extends Fragment
  9. {
  10. private static final String TAG = "FragmentOne";
  11. @Override
  12. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  13. Bundle savedInstanceState)
  14. {
  15. Log.e(TAG, "onCreateView");
  16. View view = inflater.inflate(R.layout.fragment_one, container, false);
  17. return view;
  18. }
  19. @Override
  20. public void onCreate(Bundle savedInstanceState)
  21. {
  22. // TODO Auto-generated method stub
  23. super.onCreate(savedInstanceState);
  24. Log.e(TAG, "onCreate");
  25. }
  26. @Override
  27. public void onDestroyView()
  28. {
  29. // TODO Auto-generated method stub
  30. super.onDestroyView();
  31. Log.e(TAG, "onDestroyView");
  32. }
  33. @Override
  34. public void onDestroy()
  35. {
  36. // TODO Auto-generated method stub
  37. super.onDestroy();
  38. Log.e(TAG, "onDestroy");
  39. }
  40. }

很简单的代码,当你运行之后,不断的旋转屏幕,你会发现每旋转一次屏幕,屏幕上就多了一个FragmentOne的实例,并且后台log会打印出许多套生命周期的回调。

类似:

[html]  view plain copy
  1. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  2. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  3. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  4. 07-20 08:18:46.681: E/FragmentOne(1633): onCreateView
  5. 07-20 08:18:46.831: E/FragmentOne(1633): onCreateView
  6. 07-20 08:18:46.891: E/FragmentOne(1633): onCreateView

这是为什么呢,因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建;这样造成当旋转的时候,本身存在的Fragment会重新启动,然后当执行Activity的onCreate时,又会再次实例化一个新的Fragment,这就是出现的原因。

那么如何解决呢:

其实通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建:

默认的savedInstanceState会存储一些数据,包括Fragment的实例:通过打印可以看出:

[java]  view plain copy
  1. 07-20 08:23:12.952: E/FragmentOne(1782): Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8, android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721, android:views=android.util.SparseArray@40d0af68}]}]

所以,我们简单改一下代码,只有在savedInstanceState==null时,才进行创建Fragment实例:

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.util.Log;
  7. import android.view.Window;
  8. public class MainActivity extends Activity
  9. {
  10. private static final String TAG = "FragmentOne";
  11. private FragmentOne mFOne;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState)
  14. {
  15. super.onCreate(savedInstanceState);
  16. requestWindowFeature(Window.FEATURE_NO_TITLE);
  17. setContentView(R.layout.activity_main);
  18. Log.e(TAG, savedInstanceState+"");
  19. if(savedInstanceState == null)
  20. {
  21. mFOne = new FragmentOne();
  22. FragmentManager fm = getFragmentManager();
  23. FragmentTransaction tx = fm.beginTransaction();
  24. tx.add(R.id.id_content, mFOne, "ONE");
  25. tx.commit();
  26. }
  27. }
  28. }

现在无论进行多次旋转都只会有一个Fragment实例在Activity中。

现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持?

其实和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。

由于篇幅原因,就不贴测试代码了。

5、Fragmeny与ActionBar和MenuItem集成

Fragment可以添加自己的MenuItem到Activity的ActionBar或者可选菜单中。

a、在Fragment的onCreate中调用 setHasOptionsMenu(true);

b、然后在Fragment子类中实现onCreateOptionsMenu

c、如果希望在Fragment中处理MenuItem的点击,也可以实现onOptionsItemSelected;当然了Activity也可以直接处理该MenuItem的点击事件。

代码:

Fragment

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.Menu;
  6. import android.view.MenuInflater;
  7. import android.view.MenuItem;
  8. import android.view.View;
  9. import android.view.ViewGroup;
  10. import android.widget.Toast;
  11. public class FragmentOne extends Fragment
  12. {
  13. @Override
  14. public void onCreate(Bundle savedInstanceState)
  15. {
  16. super.onCreate(savedInstanceState);
  17. setHasOptionsMenu(true);
  18. }
  19. @Override
  20. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  21. Bundle savedInstanceState)
  22. {
  23. View view = inflater.inflate(R.layout.fragment_one, container, false);
  24. return view;
  25. }
  26. @Override
  27. public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
  28. {
  29. inflater.inflate(R.menu.fragment_menu, menu);
  30. }
  31. @Override
  32. public boolean onOptionsItemSelected(MenuItem item)
  33. {
  34. switch (item.getItemId())
  35. {
  36. case R.id.id_menu_fra_test:
  37. Toast.makeText(getActivity(), "FragmentMenuItem1", Toast.LENGTH_SHORT).show();
  38. break;
  39. }
  40. return true;
  41. }
  42. }

Activity

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.util.Log;
  7. import android.view.Menu;
  8. import android.view.MenuItem;
  9. import android.view.Window;
  10. import android.widget.Toast;
  11. public class MainActivity extends Activity
  12. {
  13. private static final String TAG = "FragmentOne";
  14. private FragmentOne mFOne;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState)
  17. {
  18. super.onCreate(savedInstanceState);
  19. requestWindowFeature(Window.FEATURE_NO_TITLE);
  20. setContentView(R.layout.activity_main);
  21. Log.e(TAG, savedInstanceState + "");
  22. if (savedInstanceState == null)
  23. {
  24. mFOne = new FragmentOne();
  25. FragmentManager fm = getFragmentManager();
  26. FragmentTransaction tx = fm.beginTransaction();
  27. tx.add(R.id.id_content, mFOne, "ONE");
  28. tx.commit();
  29. }
  30. }
  31. @Override
  32. public boolean onCreateOptionsMenu(Menu menu)
  33. {
  34. super.onCreateOptionsMenu(menu);
  35. getMenuInflater().inflate(R.menu.main, menu);
  36. return true;
  37. }
  38. @Override
  39. public boolean onOptionsItemSelected(MenuItem item)
  40. {
  41. switch (item.getItemId())
  42. {
  43. case R.id.action_settings:
  44. Toast.makeText(this, "setting", Toast.LENGTH_SHORT).show();
  45. return true;
  46. default:
  47. //如果希望Fragment自己处理MenuItem点击事件,一定不要忘了调用super.xxx
  48. return super.onOptionsItemSelected(item);
  49. }
  50. }
  51. }

效果图:

好了,可以很好的看到,Fragment可以添加MenuItem,也可以自己处理点击~~~

6、没有布局的Fragment的作用

没有布局文件Fragment实际上是为了保存,当Activity重启时,保存大量数据准备的

请参考博客:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

7、使用Fragment创建对话框

这是Google推荐的方式,我也单独写过博客介绍,请参考:Android 官方推荐 : DialogFragment 创建对话框

好了,终于把Fragment相关的联系到一起了,上述基本包含了Fragment所有的用法~~~相信大家如果能够看完,一定有不少的收获~~~

有任何问题,欢迎留言~~~

更多 0
  • 上一篇Android Fragment 真正的完全解析(上)
6
0

主题推荐
android relativelayout 对话框 interface asynctask
猜你在找
Android UI 使用HTML布局(直接打开服务器网页)
android仿win8 metro磁贴布局
C++学习笔记31,指向引用的指针(3)
Android如何完全调试framework层代码
动手学Android之八——搞定列表
“割绳子”的作者,你如此歧视、无视、鄙视中国人,这是何苦呢
Android Studio使用
【Android UI设计与开发】第15期:顶部标题栏(六)实现悬浮式顶部和底部标题栏效果
Android版本百度地图开发(五)——覆盖物
去哪儿网面试问题
查看评论
6楼  DanteStones 3小时前发表 [回复]
支持鸿洋 外加 写的真不错
5楼  feong 4小时前发表 [回复]
文章好棒,图文并茂,学到不少知识。
4楼  绿领巾童鞋 6小时前发表 [回复]
想要看DEMO源码~
3楼  Michael_Yang1024 14小时前发表 [回复]
mark
2楼  soledadzz 昨天 14:59发表 [回复]
您的文章已被推荐到CSDN首页,感谢您的分享。

Re:  鸿洋_ 昨天 19:26发表 [回复]
回复soledadzz:多谢
1楼  天桥下的程序猿 昨天 09:15发表 [回复]
顶完再看~~~
发表评论
  • 用 户 名:
  • u010906868
  • 评论内容:
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
id="ad_frm_0" frameborder="0" scrolling="no" src="http://blog.csdn.net/common/ad.html?t=5&containerId=ad_bot&frmId=ad_frm_0" style="border-width: 0px; overflow: hidden; width: 746px; height: 0px;">
核心技术类目
全部主题  Java  VPN  Android  iOS  ERP  IE10  Eclipse  CRM  JavaScript  Ubuntu  NFC WAP  jQuery  数据库  BI  HTML5  Spring  Apache  Hadoop  .NET  API  HTML  SDK  IIS Fedora  XML  LBS  Unity  Splashtop  UML  components  Windows Mobile  Rails  QEMU  KDE Cassandra  CloudStack  FTC  coremail  OPhone  CouchBase  云计算  iOS6  Rackspace Web App  SpringSide  Maemo  Compuware  大数据  aptech  Perl  Tornado  Ruby  Hibernate ThinkPHP  Spark  HBase  Pure  Solr  Angular  Cloud Foundry  Redis  Scala  Django Bootstrap

  • 个人资料
  •  
    鸿洋_
     
    • 访问:58410次
    • 积分:1926分
    • 排名:第6819名
    • 原创:86篇
    • 转载:0篇
    • 译文:5篇
    • 评论:253条
  • 博客专栏
  • HTML5 & CSS3 实战

    文章:11篇

    阅读:5834

    设计模式融入生活

    文章:10篇

    阅读:4140

    Android 精彩案例

    文章:35篇

    阅读:30519

  • 文章分类
  • android(19)
  • xml(0)
  • javase(17)
  • javaee(4)
  • android基础(15)
  • github Android控件(2)
  • 设计模式(11)
  • android进阶(20)
  • javascript(9)
  • html5 css3(12)
  • rabbitMQ(5)
  • Android百度地图SDK(4)
  • 文章存档
    • 2014年07月(16)
    • 2014年06月(17)
    • 2014年05月(18)
    • 2014年04月(39)
    • 2014年03月(1)
    • 展开
  • 阅读排行
  • Android推送 百度云推送 入门篇(2900)
  • Android 完美实现图片圆角和圆形(对实现进行分析)(2208)
  • Android 省市县 三级联动(android-wheel的使用)(2190)
  • 高仿微信5.2.1主界面架构 包含消息通知(2088)
  • Android项目Tab类型主界面大总结 Fragment+TabPageIndicator+ViewPager(1854)
  • Android SwipeRefreshLayout 官方下拉刷新控件介绍(1575)
  • Android 仿Win8的metro的UI界面(上)(1573)
  • Java / Android 基于Http的多线程下载的实现(1543)
  • CSDN Android客户端的制作 导航帖(1387)
  • Android Fragment 真正的完全解析(下)(1047)
  • 评论排行
  • CSDN Android客户端的制作 导航帖(27)
  • Android 自定义ViewGroup手把手教你实现ArcMenu(16)
  • Java / Android 基于Http的多线程下载的实现(13)
  • Android 官方推荐 : DialogFragment 创建对话框(13)
  • Android AdapterView View的复用机制 分析(9)
  • Android 完美实现图片圆角和圆形(对实现进行分析)(9)
  • 高仿微信5.2.1主界面架构 包含消息通知(8)
  • Android推送 百度云推送 入门篇(8)
  • Android 自定义View (二) 进阶(8)
  • Android项目Tab类型主界面大总结 Fragment+TabPageIndicator+ViewPager(8)
  • 最新评论
  • Android Fragment 真正的完全解析(上)

    鸿洋_: @shunjian_miss:注意Activity和Fragment通信~

  • Android Fragment 真正的完全解析(上)

    鸿洋_: @soledadzz:多谢

  • Android 官方推荐 : DialogFragment 创建对话框

    天桥下的程序猿: 顶~

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

    DanteStones: 支持鸿洋 外加 写的真不错

  • Android 使用Fragment,ViewPagerIndicator 制作csdn app主要框架

    anroidfinalbreak: TabPageIndicator 这个事自定义的吧,源码中为何没有

  • Android Java汉字转拼音总结

    wp2745405: 这些转换方式都只能对单音字进行处理,如果存在多音字,这些转换方式都存在bug

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

    feong: 文章好棒,图文并茂,学到不少知识。

  • Android Fragment 真正的完全解析(上)

    soledadzz: 您的文章已被推荐到博客首页和个人页侧边栏推荐文章,感谢您的分享。

  • Android Fragment 真正的完全解析(上)

    七夜Jungle: 很好的东西诶

  • Android SwipeRefreshLayout 官方下拉刷新控件介绍

    scort1: 很好 , 很容易理解 ,慢慢的再加上动画效果就好了,慢慢来

公司简介| 招贤纳士| 广告服务| 银行汇款帐号| 联系方式| 版权声明| 法律顾问| 问题报告| 合作伙伴| 论坛反馈
网站客服  杂志客服  微博客服  webmaster@csdn.net  400-600-2320
京 ICP 证 070598 号
北京创新乐知信息技术有限公司 版权所有
江苏乐知网络技术有限公司 提供商务支持
Copyright © 1999-2014, CSDN.NET, All Rights Reserved 

  src="http://zz.csdn.net/bin/logs.php" frameborder="0" width="0" height="0">

Hongyang

  • 目录视图
  • 摘要视图
  • 订阅
有奖征资源,博文分享有内涵         6月推荐文章汇总        CSDN博文大赛初赛晋级名单公布      

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

分类: android进阶 2014-07-21 09:09  1079人阅读  评论(7)  收藏  举报
Android Fragment DialogFragment MenuItem

目录(?)[+]

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37992017

上篇博客中已经介绍了Fragment产生原因,以及一些基本的用法和各种API,如果你还不了解,请看:Android Fragment 真正的完全解析(上)。

本篇将介绍上篇博客提到的:如何管理Fragment回退栈,Fragment如何与Activity交互,Fragment与Activity交互的最佳实践,没有视图的Fragment的用处,使用Fragment创建对话框,如何与ActionBar,MenuItem集成等~~

1、管理Fragment回退栈

类似与Android系统为Activity维护一个任务栈,我们也可以通过Activity维护一个回退栈来保存每次Fragment事务发生的变化。如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。一旦Fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前Activity。

看这样一个效果图:

点击第一个按钮,切换到第二个界面,点击第二个按钮,切换到第三个界面,然后点击Back键依次回退。这像不像初学Android时的Activity跳转,当然了,这里肯定不是,不然我就跪了。这里是Fragment实现的,用户点击Back,实际是Fragment回退栈不断的弹栈。

如何添加一个Fragment事务到回退栈:

FragmentTransaction.addToBackStack(String)

下面讲解代码:很明显一共是3个Fragment和一个Activity.

先看Activity的布局文件:

[html]  view plain copy
  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. <FrameLayout
  6. android:id="@+id/id_content"
  7. android:layout_width="fill_parent"
  8. android:layout_height="fill_parent" >
  9. </FrameLayout>
  10. </RelativeLayout>

不同的Fragment就在这个FrameLayout中显示。

MainActivity.java

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. public class MainActivity extends Activity
  8. {
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState)
  11. {
  12. super.onCreate(savedInstanceState);
  13. requestWindowFeature(Window.FEATURE_NO_TITLE);
  14. setContentView(R.layout.activity_main);
  15. FragmentManager fm = getFragmentManager();
  16. FragmentTransaction tx = fm.beginTransaction();
  17. tx.add(R.id.id_content, new FragmentOne(),"ONE");
  18. tx.commit();
  19. }
  20. }

很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.

下面是FragmentOne

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.View.OnClickListener;
  9. import android.view.ViewGroup;
  10. import android.widget.Button;
  11. public class FragmentOne extends Fragment implements OnClickListener
  12. {
  13. private Button mBtn;
  14. @Override
  15. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  16. Bundle savedInstanceState)
  17. {
  18. View view = inflater.inflate(R.layout.fragment_one, container, false);
  19. mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
  20. mBtn.setOnClickListener(this);
  21. return view;
  22. }
  23. @Override
  24. public void onClick(View v)
  25. {
  26. FragmentTwo fTwo = new FragmentTwo();
  27. FragmentManager fm = getFragmentManager();
  28. FragmentTransaction tx = fm.beginTransaction();
  29. tx.replace(R.id.id_content, fTwo, "TWO");
  30. tx.addToBackStack(null);
  31. tx.commit();
  32. }
  33. }

我们在点击FragmentOne中的按钮时,使用了replace方法,如果你看了前一篇博客,一定记得replace是remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。这里很明显,我们调用tx.addToBackStack(null);将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView,证据就是:仔细看上面的效果图,我们在跳转前在文本框输入的内容,在用户Back得到第一个界面的时候不见了。

接下来FragmentTwo

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.LayoutInflater;
  7. import android.view.View;
  8. import android.view.View.OnClickListener;
  9. import android.view.ViewGroup;
  10. import android.widget.Button;
  11. public class FragmentTwo extends Fragment implements OnClickListener
  12. {
  13. private Button mBtn ;
  14. @Override
  15. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  16. Bundle savedInstanceState)
  17. {
  18. View view = inflater.inflate(R.layout.fragment_two, container, false);
  19. mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
  20. mBtn.setOnClickListener(this);
  21. return view ;
  22. }
  23. @Override
  24. public void onClick(View v)
  25. {
  26. FragmentThree fThree = new FragmentThree();
  27. FragmentManager fm = getFragmentManager();
  28. FragmentTransaction tx = fm.beginTransaction();
  29. tx.hide(this);
  30. tx.add(R.id.id_content , fThree, "THREE");
  31. //      tx.replace(R.id.id_content, fThree, "THREE");
  32. tx.addToBackStack(null);
  33. tx.commit();
  34. }
  35. }

这里点击时,我们没有使用replace,而是先隐藏了当前的Fragment,然后添加了FragmentThree的实例,最后将事务添加到回退栈。这样做的目的是为了给大家提供一种方案:如果不希望视图重绘该怎么做,请再次仔细看效果图,我们在FragmentTwo的EditText填写的内容,用户Back回来时,数据还在~~~

最后FragmentThree就是简单的Toast了:

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. import android.widget.Toast;
  10. public class FragmentThree extends Fragment implements OnClickListener
  11. {
  12. private Button mBtn;
  13. @Override
  14. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  15. Bundle savedInstanceState)
  16. {
  17. View view = inflater.inflate(R.layout.fragment_three, container, false);
  18. mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);
  19. mBtn.setOnClickListener(this);
  20. return view;
  21. }
  22. @Override
  23. public void onClick(View v)
  24. {
  25. Toast.makeText(getActivity(), " i am a btn in Fragment three",
  26. Toast.LENGTH_SHORT).show();
  27. }
  28. }

好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。

这里极其注意一点:上面的整体代码不具有任何参考价值,纯粹为了显示回退栈,在后面讲解了Fragment与Activity通信以后,会重构上面的代码!

2、Fragment与Activity通信

因为所有的Fragment都是依附于Activity的,所以通信起来并不复杂,大概归纳为:

a、如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法

b、如果Activity中未保存任何Fragment的引用,那么没关系,每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()或者findFragmentById()获得任何Fragment实例,然后进行操作。

c、在Fragment中可以通过getActivity得到当前绑定的Activity的实例,然后进行操作。

注:如果在Fragment中需要Context,可以通过调用getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity().getApplicationContext()。

3、Fragment与Activity通信的最佳实践

因为要考虑Fragment的重复使用,所以必须降低Fragment与Activity的耦合,而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。

下面我通过两种方式的代码,分别重构,FragmentOne和FragmentTwo的点击事件,以及Activity对点击事件的响应:

首先看FragmentOne

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. public class FragmentOne extends Fragment implements OnClickListener
  10. {
  11. private Button mBtn;
  12. /**
  13. * 设置按钮点击的回调
  14. * @author zhy
  15. *
  16. */
  17. public interface FOneBtnClickListener
  18. {
  19. void onFOneBtnClick();
  20. }
  21. @Override
  22. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  23. Bundle savedInstanceState)
  24. {
  25. View view = inflater.inflate(R.layout.fragment_one, container, false);
  26. mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
  27. mBtn.setOnClickListener(this);
  28. return view;
  29. }
  30. /**
  31. * 交给宿主Activity处理,如果它希望处理
  32. */
  33. @Override
  34. public void onClick(View v)
  35. {
  36. if (getActivity() instanceof FOneBtnClickListener)
  37. {
  38. ((FOneBtnClickListener) getActivity()).onFOneBtnClick();
  39. }
  40. }
  41. }

可以看到现在的FragmentOne不和任何Activity耦合,任何Activity都可以使用;并且我们声明了一个接口,来回调其点击事件,想要管理其点击事件的Activity实现此接口就即可。可以看到我们在onClick中首先判断了当前绑定的Activity是否实现了该接口,如果实现了则调用。

再看FragmentTwo

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.view.ViewGroup;
  8. import android.widget.Button;
  9. public class FragmentTwo extends Fragment implements OnClickListener
  10. {
  11. private Button mBtn ;
  12. private FTwoBtnClickListener fTwoBtnClickListener ;
  13. public interface FTwoBtnClickListener
  14. {
  15. void onFTwoBtnClick();
  16. }
  17. //设置回调接口
  18. public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)
  19. {
  20. this.fTwoBtnClickListener = fTwoBtnClickListener;
  21. }
  22. @Override
  23. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  24. Bundle savedInstanceState)
  25. {
  26. View view = inflater.inflate(R.layout.fragment_two, container, false);
  27. mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
  28. mBtn.setOnClickListener(this);
  29. return view ;
  30. }
  31. @Override
  32. public void onClick(View v)
  33. {
  34. if(fTwoBtnClickListener != null)
  35. {
  36. fTwoBtnClickListener.onFTwoBtnClick();
  37. }
  38. }
  39. }

与FragmentOne极其类似,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。

最后看Activity :

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;
  8. import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;
  9. public class MainActivity extends Activity implements FOneBtnClickListener,
  10. FTwoBtnClickListener
  11. {
  12. private FragmentOne mFOne;
  13. private FragmentTwo mFTwo;
  14. private FragmentThree mFThree;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState)
  17. {
  18. super.onCreate(savedInstanceState);
  19. requestWindowFeature(Window.FEATURE_NO_TITLE);
  20. setContentView(R.layout.activity_main);
  21. mFOne = new FragmentOne();
  22. FragmentManager fm = getFragmentManager();
  23. FragmentTransaction tx = fm.beginTransaction();
  24. tx.add(R.id.id_content, mFOne, "ONE");
  25. tx.commit();
  26. }
  27. /**
  28. * FragmentOne 按钮点击时的回调
  29. */
  30. @Override
  31. public void onFOneBtnClick()
  32. {
  33. if (mFTwo == null)
  34. {
  35. mFTwo = new FragmentTwo();
  36. mFTwo.setfTwoBtnClickListener(this);
  37. }
  38. FragmentManager fm = getFragmentManager();
  39. FragmentTransaction tx = fm.beginTransaction();
  40. tx.replace(R.id.id_content, mFTwo, "TWO");
  41. tx.addToBackStack(null);
  42. tx.commit();
  43. }
  44. /**
  45. * FragmentTwo 按钮点击时的回调
  46. */
  47. @Override
  48. public void onFTwoBtnClick()
  49. {
  50. if (mFThree == null)
  51. {
  52. mFThree = new FragmentThree();
  53. }
  54. FragmentManager fm = getFragmentManager();
  55. FragmentTransaction tx = fm.beginTransaction();
  56. tx.hide(mFTwo);
  57. tx.add(R.id.id_content, mFThree, "THREE");
  58. // tx.replace(R.id.id_content, fThree, "THREE");
  59. tx.addToBackStack(null);
  60. tx.commit();
  61. }
  62. }

代码重构结束,与开始的效果一模一样。上面两种通信方式都是值得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然Fragment和Activity可以通过getActivity与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类似总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。

4、如何处理运行时配置发生变化

运行时配置发生变化,最常见的就是屏幕发生旋转,如果你不知道如何处理屏幕变化可以参考:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

这里提一下:很多人觉得强制设置屏幕的方向就可以了,但是有一点,当你的应用被至于后台(例如用户点击了home),长时间没有返回的时候,你的应用也会被重新启动。比如上例:如果你把上面的例子你至于FragmentThree界面,然后处于后台状态,长时间后你会发现当你再次通过home打开时,上面FragmentThree与FragmentOne叠加在一起,这就是因为你的Activity重新启动,在原来的FragmentThree上又绘制了一个FragmentOne。

好了,下面看一段代码:

Activity:

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.view.Window;
  7. public class MainActivity extends Activity
  8. {
  9. private FragmentOne mFOne;
  10. @Override
  11. protected void onCreate(Bundle savedInstanceState)
  12. {
  13. super.onCreate(savedInstanceState);
  14. requestWindowFeature(Window.FEATURE_NO_TITLE);
  15. setContentView(R.layout.activity_main);
  16. mFOne = new FragmentOne();
  17. FragmentManager fm = getFragmentManager();
  18. FragmentTransaction tx = fm.beginTransaction();
  19. tx.add(R.id.id_content, mFOne, "ONE");
  20. tx.commit();
  21. }
  22. }

Fragment

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.util.Log;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. public class FragmentOne extends Fragment
  9. {
  10. private static final String TAG = "FragmentOne";
  11. @Override
  12. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  13. Bundle savedInstanceState)
  14. {
  15. Log.e(TAG, "onCreateView");
  16. View view = inflater.inflate(R.layout.fragment_one, container, false);
  17. return view;
  18. }
  19. @Override
  20. public void onCreate(Bundle savedInstanceState)
  21. {
  22. // TODO Auto-generated method stub
  23. super.onCreate(savedInstanceState);
  24. Log.e(TAG, "onCreate");
  25. }
  26. @Override
  27. public void onDestroyView()
  28. {
  29. // TODO Auto-generated method stub
  30. super.onDestroyView();
  31. Log.e(TAG, "onDestroyView");
  32. }
  33. @Override
  34. public void onDestroy()
  35. {
  36. // TODO Auto-generated method stub
  37. super.onDestroy();
  38. Log.e(TAG, "onDestroy");
  39. }
  40. }

很简单的代码,当你运行之后,不断的旋转屏幕,你会发现每旋转一次屏幕,屏幕上就多了一个FragmentOne的实例,并且后台log会打印出许多套生命周期的回调。

类似:

[html]  view plain copy
  1. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  2. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  3. 07-20 08:18:46.651: E/FragmentOne(1633): onCreate
  4. 07-20 08:18:46.681: E/FragmentOne(1633): onCreateView
  5. 07-20 08:18:46.831: E/FragmentOne(1633): onCreateView
  6. 07-20 08:18:46.891: E/FragmentOne(1633): onCreateView

这是为什么呢,因为当屏幕发生旋转,Activity发生重新启动,默认的Activity中的Fragment也会跟着Activity重新创建;这样造成当旋转的时候,本身存在的Fragment会重新启动,然后当执行Activity的onCreate时,又会再次实例化一个新的Fragment,这就是出现的原因。

那么如何解决呢:

其实通过检查onCreate的参数Bundle savedInstanceState就可以判断,当前是否发生Activity的重新创建:

默认的savedInstanceState会存储一些数据,包括Fragment的实例:通过打印可以看出:

[java]  view plain copy
  1. 07-20 08:23:12.952: E/FragmentOne(1782): Bundle[{android:fragments=android.app.FragmentManagerState@40d0b7b8, android:viewHierarchyState=Bundle[{android:focusedViewId=2131230721, android:views=android.util.SparseArray@40d0af68}]}]

所以,我们简单改一下代码,只有在savedInstanceState==null时,才进行创建Fragment实例:

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.util.Log;
  7. import android.view.Window;
  8. public class MainActivity extends Activity
  9. {
  10. private static final String TAG = "FragmentOne";
  11. private FragmentOne mFOne;
  12. @Override
  13. protected void onCreate(Bundle savedInstanceState)
  14. {
  15. super.onCreate(savedInstanceState);
  16. requestWindowFeature(Window.FEATURE_NO_TITLE);
  17. setContentView(R.layout.activity_main);
  18. Log.e(TAG, savedInstanceState+"");
  19. if(savedInstanceState == null)
  20. {
  21. mFOne = new FragmentOne();
  22. FragmentManager fm = getFragmentManager();
  23. FragmentTransaction tx = fm.beginTransaction();
  24. tx.add(R.id.id_content, mFOne, "ONE");
  25. tx.commit();
  26. }
  27. }
  28. }

现在无论进行多次旋转都只会有一个Fragment实例在Activity中。

现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持?

其实和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。

由于篇幅原因,就不贴测试代码了。

5、Fragmeny与ActionBar和MenuItem集成

Fragment可以添加自己的MenuItem到Activity的ActionBar或者可选菜单中。

a、在Fragment的onCreate中调用 setHasOptionsMenu(true);

b、然后在Fragment子类中实现onCreateOptionsMenu

c、如果希望在Fragment中处理MenuItem的点击,也可以实现onOptionsItemSelected;当然了Activity也可以直接处理该MenuItem的点击事件。

代码:

Fragment

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Fragment;
  3. import android.os.Bundle;
  4. import android.view.LayoutInflater;
  5. import android.view.Menu;
  6. import android.view.MenuInflater;
  7. import android.view.MenuItem;
  8. import android.view.View;
  9. import android.view.ViewGroup;
  10. import android.widget.Toast;
  11. public class FragmentOne extends Fragment
  12. {
  13. @Override
  14. public void onCreate(Bundle savedInstanceState)
  15. {
  16. super.onCreate(savedInstanceState);
  17. setHasOptionsMenu(true);
  18. }
  19. @Override
  20. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  21. Bundle savedInstanceState)
  22. {
  23. View view = inflater.inflate(R.layout.fragment_one, container, false);
  24. return view;
  25. }
  26. @Override
  27. public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
  28. {
  29. inflater.inflate(R.menu.fragment_menu, menu);
  30. }
  31. @Override
  32. public boolean onOptionsItemSelected(MenuItem item)
  33. {
  34. switch (item.getItemId())
  35. {
  36. case R.id.id_menu_fra_test:
  37. Toast.makeText(getActivity(), "FragmentMenuItem1", Toast.LENGTH_SHORT).show();
  38. break;
  39. }
  40. return true;
  41. }
  42. }

Activity

[java]  view plain copy
  1. package com.zhy.zhy_fragments;
  2. import android.app.Activity;
  3. import android.app.FragmentManager;
  4. import android.app.FragmentTransaction;
  5. import android.os.Bundle;
  6. import android.util.Log;
  7. import android.view.Menu;
  8. import android.view.MenuItem;
  9. import android.view.Window;
  10. import android.widget.Toast;
  11. public class MainActivity extends Activity
  12. {
  13. private static final String TAG = "FragmentOne";
  14. private FragmentOne mFOne;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState)
  17. {
  18. super.onCreate(savedInstanceState);
  19. requestWindowFeature(Window.FEATURE_NO_TITLE);
  20. setContentView(R.layout.activity_main);
  21. Log.e(TAG, savedInstanceState + "");
  22. if (savedInstanceState == null)
  23. {
  24. mFOne = new FragmentOne();
  25. FragmentManager fm = getFragmentManager();
  26. FragmentTransaction tx = fm.beginTransaction();
  27. tx.add(R.id.id_content, mFOne, "ONE");
  28. tx.commit();
  29. }
  30. }
  31. @Override
  32. public boolean onCreateOptionsMenu(Menu menu)
  33. {
  34. super.onCreateOptionsMenu(menu);
  35. getMenuInflater().inflate(R.menu.main, menu);
  36. return true;
  37. }
  38. @Override
  39. public boolean onOptionsItemSelected(MenuItem item)
  40. {
  41. switch (item.getItemId())
  42. {
  43. case R.id.action_settings:
  44. Toast.makeText(this, "setting", Toast.LENGTH_SHORT).show();
  45. return true;
  46. default:
  47. //如果希望Fragment自己处理MenuItem点击事件,一定不要忘了调用super.xxx
  48. return super.onOptionsItemSelected(item);
  49. }
  50. }
  51. }

效果图:

好了,可以很好的看到,Fragment可以添加MenuItem,也可以自己处理点击~~~

6、没有布局的Fragment的作用

没有布局文件Fragment实际上是为了保存,当Activity重启时,保存大量数据准备的

请参考博客:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

7、使用Fragment创建对话框

这是Google推荐的方式,我也单独写过博客介绍,请参考:Android 官方推荐 : DialogFragment 创建对话框

好了,终于把Fragment相关的联系到一起了,上述基本包含了Fragment所有的用法~~~相信大家如果能够看完,一定有不少的收获~~~

有任何问题,欢迎留言~~~

更多 0
  • 上一篇Android Fragment 真正的完全解析(上)
6
0

主题推荐
android relativelayout 对话框 interface asynctask
猜你在找
Android UI 使用HTML布局(直接打开服务器网页)
android仿win8 metro磁贴布局
C++学习笔记31,指向引用的指针(3)
Android如何完全调试framework层代码
动手学Android之八——搞定列表
“割绳子”的作者,你如此歧视、无视、鄙视中国人,这是何苦呢
Android Studio使用
【Android UI设计与开发】第15期:顶部标题栏(六)实现悬浮式顶部和底部标题栏效果
Android版本百度地图开发(五)——覆盖物
去哪儿网面试问题
width="728" height="90" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" id="aswift_0" name="aswift_0" style="left: 0px; position: absolute; top: 0px;">
查看评论
6楼  DanteStones 3小时前发表 [回复]
支持鸿洋 外加 写的真不错
5楼  feong 4小时前发表 [回复]
文章好棒,图文并茂,学到不少知识。
4楼  绿领巾童鞋 6小时前发表 [回复]
想要看DEMO源码~
3楼  Michael_Yang1024 14小时前发表 [回复]
mark
2楼  soledadzz 昨天 14:59发表 [回复]
您的文章已被推荐到CSDN首页,感谢您的分享。
Re:  鸿洋_ 昨天 19:26发表 [回复]
回复soledadzz:多谢
1楼  天桥下的程序猿 昨天 09:15发表 [回复]
顶完再看~~~
发表评论
  • 用 户 名:
  • u010906868
  • 评论内容:
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
核心技术类目
全部主题  Java  VPN  Android  iOS  ERP  IE10  Eclipse  CRM  JavaScript  Ubuntu  NFC WAP  jQuery  数据库  BI  HTML5  Spring  Apache  Hadoop  .NET  API  HTML  SDK  IIS Fedora  XML  LBS  Unity  Splashtop  UML  components  Windows Mobile  Rails  QEMU  KDE Cassandra  CloudStack  FTC  coremail  OPhone  CouchBase  云计算  iOS6  Rackspace Web App  SpringSide  Maemo  Compuware  大数据  aptech  Perl  Tornado  Ruby  Hibernate ThinkPHP  Spark  HBase  Pure  Solr  Angular  Cloud Foundry  Redis  Scala  Django Bootstrap

  • 个人资料
  •  
    鸿洋_
     
    • 访问:58410次
    • 积分:1926分
    • 排名:第6819名
    • 原创:86篇
    • 转载:0篇
    • 译文:5篇
    • 评论:253条
  • 博客专栏
  • HTML5 & CSS3 实战

    文章:11篇

    阅读:5834

    设计模式融入生活

    文章:10篇

    阅读:4140

    Android 精彩案例

    文章:35篇

    阅读:30519

  • 文章分类
  • android(19)
  • xml(0)
  • javase(17)
  • javaee(4)
  • android基础(15)
  • github Android控件(2)
  • 设计模式(11)
  • android进阶(20)
  • javascript(9)
  • html5 css3(12)
  • rabbitMQ(5)
  • Android百度地图SDK(4)
  • 文章存档
    • 2014年07月(16)
    • 2014年06月(17)
    • 2014年05月(18)
    • 2014年04月(39)
    • 2014年03月(1)
      展开
  • 阅读排行
  • Android推送 百度云推送 入门篇(2900)
  • Android 完美实现图片圆角和圆形(对实现进行分析)(2208)
  • Android 省市县 三级联动(android-wheel的使用)(2190)
  • 高仿微信5.2.1主界面架构 包含消息通知(2088)
  • Android项目Tab类型主界面大总结 Fragment+TabPageIndicator+ViewPager(1854)
  • Android SwipeRefreshLayout 官方下拉刷新控件介绍(1575)
  • Android 仿Win8的metro的UI界面(上)(1573)
  • Java / Android 基于Http的多线程下载的实现(1543)
  • CSDN Android客户端的制作 导航帖(1387)
  • Android Fragment 真正的完全解析(下)(1047)
  • 评论排行
  • CSDN Android客户端的制作 导航帖(27)
  • Android 自定义ViewGroup手把手教你实现ArcMenu(16)
  • Java / Android 基于Http的多线程下载的实现(13)
  • Android 官方推荐 : DialogFragment 创建对话框(13)
  • Android AdapterView View的复用机制 分析(9)
  • Android 完美实现图片圆角和圆形(对实现进行分析)(9)
  • 高仿微信5.2.1主界面架构 包含消息通知(8)
  • Android推送 百度云推送 入门篇(8)
  • Android 自定义View (二) 进阶(8)
  • Android项目Tab类型主界面大总结 Fragment+TabPageIndicator+ViewPager(8)
  • 最新评论
  • Android Fragment 真正的完全解析(上)

    鸿洋_: @shunjian_miss:注意Activity和Fragment通信~

  • Android Fragment 真正的完全解析(上)

    鸿洋_: @soledadzz:多谢

  • Android 官方推荐 : DialogFragment 创建对话框

    天桥下的程序猿: 顶~

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

    DanteStones: 支持鸿洋 外加 写的真不错

  • Android 使用Fragment,ViewPagerIndicator 制作csdn app主要框架

    anroidfinalbreak: TabPageIndicator 这个事自定义的吧,源码中为何没有

  • Android Java汉字转拼音总结

    wp2745405: 这些转换方式都只能对单音字进行处理,如果存在多音字,这些转换方式都存在bug

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

    feong: 文章好棒,图文并茂,学到不少知识。

  • Android Fragment 真正的完全解析(上)

    soledadzz: 您的文章已被推荐到博客首页和个人页侧边栏推荐文章,感谢您的分享。

  • Android Fragment 真正的完全解析(上)

    七夜Jungle: 很好的东西诶

  • Android SwipeRefreshLayout 官方下拉刷新控件介绍

    scort1: 很好 , 很容易理解 ,慢慢的再加上动画效果就好了,慢慢来

公司简介| 招贤纳士| 广告服务| 银行汇款帐号| 联系方式| 版权声明| 法律顾问| 问题报告| 合作伙伴| 论坛反馈
网站客服  杂志客服  微博客服  webmaster@csdn.net  400-600-2320
京 ICP 证 070598 号
北京创新乐知信息技术有限公司 版权所有
江苏乐知网络技术有限公司 提供商务支持
Copyright © 1999-2014, CSDN.NET, All Rights Reserved 

Android Fragment 真正的完全解析相关推荐

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

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

  2. Android Fragment 真正的完全解析(上)

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37970961 自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fra ...

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

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

  4. Android fragment源码全解析

    Fragment 相信基本上每个android developer都用过,但是知晓其原理 用的好的还是不多,今天就从源码的角度上来带着大家分析一下Fragment的源码,对fragment有了更深层次 ...

  5. Android Fragment 真正的完全解析(上) (转载)

    原处: http://blog.csdn.net/lmj623565791/article/details/37970961 自从Fragment出现,曾经有段时间,感觉大家谈什么都能跟Fragmen ...

  6. Android Fragment 简单实例

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

  7. Android Fragment 生命周期及其正确使用(建议使用自定义View替换Fragment)

    使用Fragment 官方例子中显示: 例如:一个学生Fragment,需要传入studentId,进行http请求显示,那么setArguments后防止杀掉Fragment后,参数为0,显示不了数 ...

  8. Android Fragment 真正彻底的解决(下一个)

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/37992017 上篇博客中已经介绍了Fragment产生原因.以及一些主要的使用方 ...

  9. Android Fragment 从源码的角度去解析(上)

    ###1.概述 本来想着昨天星期五可以早点休息,今天可以早点起来跑步,可没想到事情那么的多,晚上有人问我主页怎么做到点击才去加载Fragment数据,而不是一进入主页就去加载所有的数据,在这里自己就对 ...

最新文章

  1. BCH扩容的摩尔定律——为什么BCH目前不选择分片?
  2. linux 桌面管理器 xfce 用户自动登录
  3. 巧用Graphviz和pvtrace等工具可视化C函数调用
  4. 【转】强大的B树B+树
  5. html隐藏定位,html – 如何使绝对定位(溢出:隐藏)容器的绝对定位的子项显示在其父项区域之外?...
  6. mybatis学习(34):动态sql-choose
  7. [转] Asp.net Session常见问题集锦
  8. 【C语言】C语言常量和变量
  9. 二阶采样matlab程序,Sea-clutter-simulation 海杂波仿真程序,包括单基地雷达和双 的一阶、二阶 matlab 272万源代码下载- www.pudn.com...
  10. js function
  11. sql根据身份证计算年龄
  12. 常用等价无穷小以及泰勒公式
  13. eigen求特征值和特征向量
  14. JAVA:实现求StandardDeviation标准差算法(附完整源码)
  15. ClipCap: CLIP Prefix for Image Captioning
  16. 学生专用计算机怎样开启关机,怎么设置电脑自动关机?
  17. 经历--比赛绿盟_安全研究员
  18. 为什么换了固态硬盘电脑会快?详解硬盘与内存的关系
  19. Siebel Adapter在WMB上的应用
  20. 微信小程序支付流程!

热门文章

  1. QT之Excel表格操作
  2. 修改Office文件的默认打开方式(含C#代码)
  3. Excel 序号自动增长,变更
  4. 「卷无人道」,手机app定制
  5. photoshop批处理改变图片大小
  6. 异步电机三相电流滞环矢量控制
  7. http://level3.tasteless.eu/ 获得服务器目录及其内部文件信息
  8. fps php,帧率60帧是什么意思
  9. AssertionError: Attempted unscale_ but _scale is None
  10. 二维数组的花式遍历技巧盘点