放QQ菜单栏:消息,电话菜单

转载自:http://blog.csdn.net/johnnyz1234/article/details/45919907

在实际项目开发使用Fragment的时候,也碰到一些异常和存在的问题,下面做下简单的总结笔记,后面还会不定时补充更新。
1.关于Fragment的生命周期的几点认识
  • Fragment的完整生命周期开始于绑定到它的父Activity,结束于从父Activity上分离。通过分别调用onAttach和onDetach来表示这些事件。
  • 在Fragment/Activity 被暂停之后,由于任何其他处理程序都可以被调用,可能就会出现它的父Activity进程没有完成它的全部生命周期被终止从而导致onDetach不会被调用的情况。
  • onAttach事件在Fragment的UI被创建之前,以及Fragment自身或它的父Activity完成它们的初始化之前被触发。通常情况下,onAttach事件用来获取一个Fragment的父Activity的引用,为进一步的初始化工作准备。
  • 对Activity的进程来说,在没有响应的onDestroy方法被调用而被终止的情况很常见,所以Fragment不能依赖触发onDestory方法来销毁它。
  • 如果Fragment需要和它的父Activity的UI交互,需要一直等到onActivityCreated事件被触发。该事件被触发意味着Frament所在的Activity已经完成了对初始化并且它的UI也已经完全构建好了。
2.Fragment开发中遇到的问题
  • Fragment getActivity为空的情况解决办法
我们模仿QQ首页的实现Demo来模拟解决getActivity为空的问题,实现界面如下:

[java] view plain copy

  1. public class SwitchActivity extends FragmentActivity {
  2. private Button btn_message,btn_call;
  3. private CallFragment callFragment;
  4. private MessageFragment messageFragment;
  5. public static final int MESSAGE_FRAGMENT_TYPE = 1;
  6. public static final int CALL_FRAGMENT_TYPE = 2;
  7. public int currentFragmentType = -1;
  8. @Override
  9. protected void onCreate(Bundle savedInstanceState) {
  10. super.onCreate(savedInstanceState);
  11. this.requestWindowFeature(Window.FEATURE_NO_TITLE);
  12. setContentView(R.layout.activity_switch);
  13. btn_message = (Button)findViewById(R.id.btn_message);
  14. btn_call = (Button)findViewById(R.id.btn_call);
  15. btn_message.setOnClickListener(onClicker);
  16. btn_call.setOnClickListener(onClicker);
  17. FragmentManager fragmentManager = getSupportFragmentManager();
  18. if (savedInstanceState != null) {
  19. //当savedInstanceState不为空的时候,说明当前的Activity是被回收后重建,
  20. //我们重新建立Fragment和Activity的联系。
  21. int type = savedInstanceState.getInt("currentFragmentType");
  22. messageFragment = (MessageFragment)fragmentManager.findFragmentByTag("message");
  23. callFragment = (CallFragment)fragmentManager.findFragmentByTag("call");
  24. if(type > 0)
  25. loadFragment(type);
  26. } else {
  27. FragmentTransaction transaction = fragmentManager
  28. .beginTransaction();
  29. Fragment mainFragment = fragmentManager.findFragmentByTag("message");
  30. if (mainFragment != null) {
  31. transaction.replace(R.id.fl_content, mainFragment);
  32. transaction.commit();
  33. } else {
  34. loadFragment(MESSAGE_FRAGMENT_TYPE);
  35. }
  36. }
  37. }
  38. /**
  39. * 当某种原因Activity被销毁回收掉(如:App进入后台运行或者Activity压入任务栈内存不足时候被回收),
  40. * onSaveIntanceState方法保存当前的状态。当用户操作当前Activity要返回前台或者
  41. * 我们的Activity会被重建(如:横屏操作),此时Activity与Fragment间失去联系,
  42. * 我们这个时候调用getActivity()会返回为null
  43. */
  44. @Override
  45. protected void onSaveInstanceState(Bundle outState) {
  46. super.onSaveInstanceState(outState);
  47. outState.putInt("lastFragmentTag", currentFragmentType);
  48. }
  49. private void switchFragment(int type) {
  50. switch (type) {
  51. case MESSAGE_FRAGMENT_TYPE:
  52. loadFragment(MESSAGE_FRAGMENT_TYPE);
  53. break;
  54. case CALL_FRAGMENT_TYPE:
  55. loadFragment(CALL_FRAGMENT_TYPE);
  56. break;
  57. }
  58. }
  59. private void loadFragment(int type) {
  60. FragmentManager fragmentManager = getSupportFragmentManager();
  61. FragmentTransaction transaction = fragmentManager.beginTransaction();
  62. if (type == CALL_FRAGMENT_TYPE) {
  63. if (callFragment == null) {
  64. callFragment = new CallFragment();
  65. transaction.add(R.id.fl_content, callFragment, "zhishi");
  66. } else {
  67. transaction.show(callFragment);
  68. }
  69. if (messageFragment != null) {
  70. transaction.hide(messageFragment);
  71. }
  72. currentFragmentType = MESSAGE_FRAGMENT_TYPE;
  73. } else {
  74. if (messageFragment == null) {
  75. messageFragment = new MessageFragment();
  76. transaction.add(R.id.fl_content, messageFragment, "wenda");
  77. } else {
  78. transaction.show(messageFragment);
  79. }
  80. if (callFragment != null) {
  81. transaction.hide(callFragment);
  82. }
  83. currentFragmentType = CALL_FRAGMENT_TYPE;
  84. }
  85. transaction.commitAllowingStateLoss();
  86. }
  87. private OnClickListener onClicker = new OnClickListener() {
  88. @Override
  89. public void onClick(View v) {
  90. switch (v.getId()) {
  91. case R.id.btn_message:
  92. btn_message.setTextColor(Color.parseColor("#df3031"));
  93. btn_call.setTextColor(Color.WHITE);
  94. btn_message.setBackgroundResource(R.drawable.baike_btn_pink_left_f_96);
  95. btn_call.setBackgroundResource(R.drawable.baike_btn_trans_right_f_96);
  96. switchFragment(MESSAGE_FRAGMENT_TYPE);
  97. break;
  98. case R.id.btn_call:
  99. btn_message.setTextColor(Color.WHITE);
  100. btn_call.setTextColor(Color.parseColor("#df3031"));
  101. btn_message.setBackgroundResource(R.drawable.baike_btn_trans_left_f_96);
  102. btn_call.setBackgroundResource(R.drawable.baike_btn_pink_right_f_96);
  103. switchFragment(CALL_FRAGMENT_TYPE);
  104. break;
  105. }
  106. }
  107. };
  108. }
从以上代码我们知道,Activity页面实现对MessageFragment和CallFragment的切换功能。我们的页面可能在某种原因下系统被销毁回收掉(如:App进入后台运行或者Activity压入任务栈内存不足时候被回收), 在Activity被回收的时候我们可以调用onSaveIntanceState方法保存当前的某些状态。当用户操作当前Activity要返回前台或者我们的Activity会被重建(如:横屏操作),此时Activity与Fragment间失去联系, 我们这个时候调用getActivity()会返回为null。针对这种情况,我们可以的解决方案如下:
当Activity被重新建立的时候,onCreate的时候,我们判断savedInstanceState参数不为空的时候,即知道我们的Activity是重建的,此时我们的Fragment可能还存在,我们就没有必要重建Fragment,我们只需要通过调用FragmentManager的findFragmentByTag方法重新连接Fragment和Activity. 
针对以上的解决方案,我们可以在Android系统的实现代码里找到类似的实现。我们看到从Fragment诞生开始,很多App开始用ViewPager+PagerAdapter的子类(即:FragmentPagerAdapter和FragmentStatePagerAdapter)来制作首页。在ViewPager + Fragment的首页实现组合情况下,我们不用担心Fragment里调用getActivity()为空的问题。我们看FragmentPagerAdapter的源码,我们知道FragmentPagerAdapter和FragmentStatePagerAdapter都是继承抽象基类PagerAdapter,我们重点看FragmentPagerAdapter的源码instantiateItem方法的实现:

[java] view plain copy

  1. @Override
  2. public Object instantiateItem(ViewGroup container, int position) {
  3. if (mCurTransaction == null) {
  4. mCurTransaction = mFragmentManager.beginTransaction();
  5. }
  6. final long itemId = getItemId(position);
  7. // Do we already have this fragment?
  8. String name = makeFragmentName(container.getId(), itemId);
  9. Fragment fragment = mFragmentManager.findFragmentByTag(name);
  10. if (fragment != null) {
  11. if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
  12. mCurTransaction.attach(fragment);
  13. } else {
  14. fragment = getItem(position);
  15. if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
  16. mCurTransaction.add(container.getId(), fragment,
  17. makeFragmentName(container.getId(), itemId));
  18. }
  19. if (fragment != mCurrentPrimaryItem) {
  20. FragmentCompat.setMenuVisibility(fragment, false);
  21. FragmentCompat.setUserVisibleHint(fragment, false);
  22. }
  23. return fragment;
  24. }
我们从Android官方文档可以知道,instantiateItem方法的作用:Create the page for the given position. The adapter is responsible for adding the view to the container given here, although it only must ensure this is done by the time it returns from finishUpdate(ViewGroup).我们知道instantiateItem会给我们制定位置返回一个页面。我们看到以下两句话:

[java] view plain copy

  1. // Do we already have this fragment?
  2. String name = makeFragmentName(container.getId(), itemId);
  3. Fragment fragment = mFragmentManager.findFragmentByTag(name);
如果我们的Fragment存在的话,我们直接关联fragment和Activity,如果不存在的话,我们新建Fragment.
  • Fragment Tansactions 和Activity的状态丢失的问题
我们的代码出现过如下异常:

[plain] view plain copy

  1. java.lang.IllegalStateException:Can not perform this action after onSaveInstanceState
  2. at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
  3. at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
  4. at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
  5. at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
这种异常的出现是由于,在Activity的状态保存之后,尝试去提交一个FragmentTransaction。这种现象被称为活动状态丢失(Activity State Loss)。我在实际开发中,异常小概率出现的情况发生在我们切换消息和电话按钮的时候,抛出以上的异常。通过后来查找问题,发现原因由于在onSaveInstanceState()方法调用后会调用FragmentTransaction的commit方法。这个transaction将不会被记住,因为它没有在第一时间记录为这个Activity的状态的一部分。这个transaction将会丢失,可能导致UI状态不一致。此处,Android系统检测到不一致会抛出一个IllegalStateException异常。最简单的解决办法就是在除onCreate方法外的周期,尽量去忽视状态一致性的检查,我们将commit方法改为commitAllowingStateLoss。更复杂的状态的丢失解决办法参考这篇文字http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html。
本文总结会继续不定时更新,有错误的地方还望园友指正,附上Demo
时间: 2024-10-22 06:40:28

放QQ菜单栏:消息,电话菜单的相关文章

【WP8】仿QQ提示消息

WP版的QQ提示消息的时候从顶部滑入,3秒后从顶部滑出,本文模仿该效果实现一个MessageToastManager类用于显示提示消息 思路很简单,就是动画而已,支持配置颜色和回掉 // ************************************************* // // 作者:bomo // 小组:WP开发组 // 创建日期:2014/7/7 15:18:12 // 版本号:V1.00 // 说明: // // ****************************

关于 使用python向qq好友发送消息(对爬虫的作用----当程序执行完毕或者报错无限给自己qq发送消息,直到关闭)

以前看到网上一些小程序,在处理完事物后会自动发送qq消息,但是一直搞不懂是说明原理.也在网上找过一些python登陆qq发送消息的文字,但是都太复杂了.今天偶然看到一篇文章,是用python调用win32的接口发送qq消息的,觉得不错,就先记录下来,日后肯定会用得上这些小工具. 发送qq消息要求已经登陆qq,而且qq的窗口是独立的,现在新版的qq一般都是将所有的聊天窗口聚合在一起,因此要设置将qq窗口分离,或者将需要发送消息的那个窗口单独分离出来. 上代码吧. # 原理是先将需要发送的文本放到剪

C# WPF QQ新消息托盘悬浮窗效果实现

今天在做一个项目的时候需要这么一个效果,但是网上找了一会发现并没有现成的给我参考(复制),但是呢,我千(到)辛(处)万(抄)苦(袭)想(复)破(制)头(粘)脑(贴)终于还是给做出来了~嘿嘿嘿 QQ新消息悬浮窗即:QQ有新消息时托盘图标会闪动,此时移动鼠标到托盘图标上就会显示一个弹框了,那么呢我把这个弹框称为“QQ新消息托盘悬浮窗”.当鼠标从托盘图标移走后弹框隐藏,我们要做的效果就是这样的. 项目效果图: 涉及到的内容主要有:Popup,win32api,DispatcherTimer(定时器).

Android自定义类似于QQ的消息提示框

看到QQ的信息提示框,感觉效果很不错,做了一个类似的,展示如下: 点击查看短视频 效果还不错,整体上是Translate动画和FrameLayout布局的结合,下面看一下代码: activiy_main.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

android:QQ多种側滑菜单的实现

在这篇文章中写了 自己定义HorizontalScrollView实现qq側滑菜单 然而这个菜单效果仅仅是普通的側拉效果 我们还能够实现抽屉式側滑菜单 就像这样 第一种效果 另外一种效果 第三种效果 第四种效果 其他代码都和上篇文章同样,仅仅是在MyHorizontalScrollView.class重写onScrollChanged这种方法 第一种的側滑效果代码非常easy @Override protected void onScrollChanged(int l, int t, int o

JavaScript实战(带收放动画效果的导航菜单)

虽然有很多插件可用,但为了共同提高,我做了一系列JavaScript实战系列的实例,分享给大家,前辈们若有好的建议,请务必指出,免得误人子弟啊! ( 原创文章,转摘请注明:苏服:http://www.cnblogs.com/susufufu/p/5768402.html ) 今天是第一战:带收放动画效果的菜单,效果如下图:(样式有点丑(-^-)) 动画效果:鼠标hover改变所有目标的背景和字体颜色,鼠标移动到‘首页导航’,显示下面的分组菜单,分组菜单有子菜单,点击可缩放,带动画过度效果! 如何

全国行政区划数据大放送——包含邮政编码-电话区号-简拼-区划路径

因为一些情况需要用到全国行政区划数据库,本身没有库的就自己在网上找,但是找到的基本上不是我想要的,因为只包含了基本信息, 最后自己下了一个原始库准备自己组装数据,网上的XX数据网啊,web services都有限制,还是自己想办法把数据拿到手最放心. 原始数据是2015年4月的,原始数据只有基本的三级(省市县)区划信息,我自己通过一些查询区划网站,把四级数据抓取导入到自己的数据 库中(省市县乡),数据还是比较新的,觉得应该会有园友用到所以将数据放上来. 组装后的格式:区划ID-区划名称-父级ID

ArrayAdapter适配器的用法,模拟QQ发消息界面。

1 import java.util.ArrayList; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.os.Bundle; 6 import android.view.LayoutInflater; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import androi

visual Studio 2017 扩展开发(一)《向Visual Studio菜单栏新增一个菜单》

最近有接触到关于visual studio 2017 扩展的开发,特此记录,也是为了督促自己去深入了解其原理. 开始开发Visual Studio 扩展,在这里我安装了visual studio 2017, 在安装的时候记得勾选上visual studio 扩展开发. 创建一个项目 我们打开编译器,文件→新建项目,模板→Visual C#→Extensibility 选择 VSIX Project.创建一个项目. 添加自定义命令 右键项目,添加新项.Visual C#项→Extensibilit