设为首页 收藏本站
查看: 1698|回复: 0

[Cloudstack] Android Fragment 真正的完全解析

[复制链接]

尚未签到

发表于 2015-10-14 07:55:59 | 显示全部楼层 |阅读模式
  转载请标明出处:http://blog.iyunv.com/lmj623565791/article/details/37992017
  







  • DSC0000.jpg

Hongyang


  • DSC0001.gif 目录视图
  • DSC0002.gif 摘要视图
  • DSC0003.gif 订阅
有奖征资源,博文分享有内涵        6月推荐文章汇总       CSDN博文大赛初赛晋级名单公布       Android Fragment 真正的完全解析(下)
分类: android进阶2014-07-2109:09 1079人阅读 评论(7) 收藏 举报AndroidFragmentDialogFragmentMenuItem
目录(?)[+]

  转载请标明出处:http://blog.iyunv.com/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。
  看这样一个效果图:
DSC0004.jpg

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

  下面讲解代码:很明显一共是3个Fragment和一个Activity.
  先看Activity的布局文件:
  
[html] viewplaincopy DSC0005.jpg DSC0006.jpg

  • <RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;  
  •     xmlns:tools=&quot;http://schemas.android.com/tools&quot;  
  •     android:layout_width=&quot;match_parent&quot;  
  •     android:layout_height=&quot;match_parent&quot; >  
  •   
  •     <FrameLayout  
  •         android:id=&quot;@&#43;id/id_content&quot;  
  •         android:layout_width=&quot;fill_parent&quot;  
  •         android:layout_height=&quot;fill_parent&quot; >  
  •     </FrameLayout>  
  •   
  • </RelativeLayout>  
不同的Fragment就在这个FrameLayout中显示。  
  MainActivity.java
  
[java] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.Window;  
  •   
  • public class MainActivity extends Activity  
  • {  
  •   
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.add(R.id.id_content, new FragmentOne(),&quot;ONE&quot;);  
  •         tx.commit();  
  •     }  
  •   
  • }  
很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.  
  下面是FragmentOne
  
[java] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  •   
  • public class FragmentOne extends Fragment implements OnClickListener  
  • {  
  •   
  •     private Button mBtn;  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view;  
  •     }  
  •   
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         FragmentTwo fTwo = new FragmentTwo();  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.replace(R.id.id_content, fTwo, &quot;TWO&quot;);  
  •         tx.addToBackStack(null);  
  •         tx.commit();  
  •   
  •     }  
  •   
  • }  

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

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  •   
  • public class FragmentTwo extends Fragment implements OnClickListener  
  • {  
  •   
  •     private Button mBtn ;  
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_two, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view ;   
  •     }  
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         FragmentThree fThree = new FragmentThree();  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.hide(this);  
  •         tx.add(R.id.id_content , fThree, &quot;THREE&quot;);  
  • //      tx.replace(R.id.id_content, fThree, &quot;THREE&quot;);  
  •         tx.addToBackStack(null);  
  •         tx.commit();  
  •     }  
  •   
  •   
  • }  

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

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  • import android.widget.Toast;  
  •   
  • public class FragmentThree extends Fragment implements OnClickListener  
  • {  
  •   
  •     private Button mBtn;  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_three, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view;  
  •     }  
  •   
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         Toast.makeText(getActivity(), &quot; i am a btn in Fragment three&quot;,  
  •                 Toast.LENGTH_SHORT).show();  
  •     }  
  •   
  • }  

好了,经过上面的介绍,应该已经知道Fragment回退栈是怎么一回事了,以及hide,replace等各自的应用的场景。  
  这里极其注意一点:上面的整体代码不具有任何参考价&#20540;,纯粹为了显示回退栈,在后面讲解了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] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  •   
  • public class FragmentOne extends Fragment implements OnClickListener  
  • {  
  •     private Button mBtn;  
  •   
  •     /**
  •      * 设置按钮点击的回调
  •      * @author zhy
  •      *
  •      */  
  •     public interface FOneBtnClickListener  
  •     {  
  •         void onFOneBtnClick();  
  •     }  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view;  
  •     }  
  •   
  •     /**
  •      * 交给宿主Activity处理,如果它希望处理
  •      */  
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         if (getActivity() instanceof FOneBtnClickListener)  
  •         {  
  •             ((FOneBtnClickListener) getActivity()).onFOneBtnClick();  
  •         }  
  •     }  
  •   
  • }  

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

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  •   
  • public class FragmentTwo extends Fragment implements OnClickListener  
  • {  
  •   
  •       
  •     private Button mBtn ;  
  •       
  •     private FTwoBtnClickListener fTwoBtnClickListener ;  
  •       
  •     public interface FTwoBtnClickListener  
  •     {  
  •         void onFTwoBtnClick();  
  •     }  
  •     //设置回调接口  
  •     public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)  
  •     {  
  •         this.fTwoBtnClickListener = fTwoBtnClickListener;  
  •     }  
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_two, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view ;   
  •     }  
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         if(fTwoBtnClickListener != null)  
  •         {  
  •             fTwoBtnClickListener.onFTwoBtnClick();  
  •         }  
  •     }  
  •   
  • }  

与FragmentOne极其类&#20284;,但是我们提供了setListener这样的方法,意味着Activity不仅需要实现该接口,还必须显示调用mFTwo.setfTwoBtnClickListener(this)。  
  最后看Activity :
  
[java] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.Window;  
  •   
  • import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;  
  • import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;  
  •   
  • public class MainActivity extends Activity implements FOneBtnClickListener,  
  •         FTwoBtnClickListener  
  • {  
  •   
  •     private FragmentOne mFOne;  
  •     private FragmentTwo mFTwo;  
  •     private FragmentThree mFThree;  
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         mFOne = new FragmentOne();  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.add(R.id.id_content, mFOne, &quot;ONE&quot;);  
  •         tx.commit();  
  •     }  
  •   
  •     /**
  •      * FragmentOne 按钮点击时的回调
  •      */  
  •     @Override  
  •     public void onFOneBtnClick()  
  •     {  
  •   
  •         if (mFTwo == null)  
  •         {  
  •             mFTwo = new FragmentTwo();  
  •             mFTwo.setfTwoBtnClickListener(this);  
  •         }  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.replace(R.id.id_content, mFTwo, &quot;TWO&quot;);  
  •         tx.addToBackStack(null);  
  •         tx.commit();  
  •     }  
  •   
  •     /**
  •      * FragmentTwo 按钮点击时的回调
  •      */  
  •     @Override  
  •     public void onFTwoBtnClick()  
  •     {  
  •         if (mFThree == null)  
  •         {  
  •             mFThree = new FragmentThree();  
  •   
  •         }  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.hide(mFTwo);  
  •         tx.add(R.id.id_content, mFThree, &quot;THREE&quot;);  
  •         // tx.replace(R.id.id_content, fThree, &quot;THREE&quot;);  
  •         tx.addToBackStack(null);  
  •         tx.commit();  
  •     }  
  •   
  • }  

代码重构结束,与开始的效果一模一样。上面两种通信方式都是&#20540;得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然Fragment和Activity可以通过getActivity与findFragmentByTag或者findFragmentById,进行任何操作,甚至在Fragment里面操作另外的Fragment,但是没有特殊理由是绝对不提倡的。Activity担任的是Fragment间类&#20284;总线一样的角色,应当由它决定Fragment如何操作。另外虽然Fragment不能响应Intent打开,但是Activity可以,Activity可以接收Intent,然后根据参数判断显示哪个Fragment。  
4、如何处理运行时配置发生变化
  运行时配置发生变化,最常见的就是屏幕发生旋转,如果你不知道如何处理屏幕变化可以参考:Android 屏幕旋转 处理 AsyncTask和 ProgressDialog 的最佳方案
  这里提一下:很多人觉得强制设置屏幕的方向就可以了,但是有一点,当你的应用被至于后台(例如用户点击了home),长时间没有返回的时候,你的应用也会被重新启动。比如上例:如果你把上面的例子你至于FragmentThree界面,然后处于后台状态,长时间后你会发现当你再次通过home打开时,上面FragmentThree与FragmentOne叠加在一起,这就是因为你的Activity重新启动,在原来的FragmentThree上又绘制了一个FragmentOne。
  好了,下面看一段代码:
  Activity:
  
[java] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.Window;  
  •   
  • public class MainActivity extends Activity  
  •   
  • {  
  •     private FragmentOne mFOne;  
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         mFOne = new FragmentOne();  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.add(R.id.id_content, mFOne, &quot;ONE&quot;);  
  •         tx.commit();  
  •   
  •     }  
  •   
  • }  

Fragment  
  
[java] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.util.Log;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.ViewGroup;  
  •   
  • public class FragmentOne extends Fragment  
  • {  
  •     private static final String TAG = &quot;FragmentOne&quot;;  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         Log.e(TAG, &quot;onCreateView&quot;);  
  •         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  •         return view;  
  •     }  
  •   
  •     @Override  
  •     public void onCreate(Bundle savedInstanceState)  
  •     {  
  •         // TODO Auto-generated method stub  
  •         super.onCreate(savedInstanceState);  
  •   
  •         Log.e(TAG, &quot;onCreate&quot;);  
  •     }  
  •   
  •     @Override  
  •     public void onDestroyView()  
  •     {  
  •         // TODO Auto-generated method stub  
  •         super.onDestroyView();  
  •         Log.e(TAG, &quot;onDestroyView&quot;);  
  •     }  
  •   
  •     @Override  
  •     public void onDestroy()  
  •     {  
  •         // TODO Auto-generated method stub  
  •         super.onDestroy();  
  •         Log.e(TAG, &quot;onDestroy&quot;);  
  •     }  
  •   
  • }  

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

  • 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
  • 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
  • 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
  • 07-20 08:18:46.681: E/FragmentOne(1633): onCreateView  
  • 07-20 08:18:46.831: E/FragmentOne(1633): onCreateView  
  • 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] viewplaincopy

  • 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] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.util.Log;  
  • import android.view.Window;  
  •   
  • public class MainActivity extends Activity  
  •   
  • {  
  •     private static final String TAG = &quot;FragmentOne&quot;;  
  •     private FragmentOne mFOne;  
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         Log.e(TAG, savedInstanceState&#43;&quot;&quot;);  
  •          
  •         if(savedInstanceState == null)  
  •         {  
  •             mFOne = new FragmentOne();  
  •             FragmentManager fm = getFragmentManager();  
  •             FragmentTransaction tx = fm.beginTransaction();  
  •             tx.add(R.id.id_content, mFOne, &quot;ONE&quot;);  
  •             tx.commit();  
  •         }  
  •          
  •          
  •   
  •     }  
  •   
  • }  

现在无论进行多次旋转都只会有一个Fragment实例在Activity中。  
  现在还存在一个问题,就是重新绘制时,Fragment发生重建,原本的数据如何保持?
  其实和Activity类&#20284;,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] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.Menu;  
  • import android.view.MenuInflater;  
  • import android.view.MenuItem;  
  • import android.view.View;  
  • import android.view.ViewGroup;  
  • import android.widget.Toast;  
  •   
  • public class FragmentOne extends Fragment  
  • {  
  •   
  •     @Override  
  •     public void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         setHasOptionsMenu(true);  
  •     }  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  •         return view;  
  •     }  
  •   
  •     @Override  
  •     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)  
  •     {  
  •         inflater.inflate(R.menu.fragment_menu, menu);  
  •     }  
  •   
  •     @Override  
  •     public boolean onOptionsItemSelected(MenuItem item)  
  •     {  
  •         switch (item.getItemId())  
  •         {  
  •         case R.id.id_menu_fra_test:  
  •             Toast.makeText(getActivity(), &quot;FragmentMenuItem1&quot;, Toast.LENGTH_SHORT).show();  
  •             break;  
  •         }  
  •         return true;  
  •     }  
  •   
  • }  

Activity  
  
[java] viewplaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.util.Log;  
  • import android.view.Menu;  
  • import android.view.MenuItem;  
  • import android.view.Window;  
  • import android.widget.Toast;  
  •   
  • public class MainActivity extends Activity  
  •   
  • {  
  •     private static final String TAG = &quot;FragmentOne&quot;;  
  •     private FragmentOne mFOne;  
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         Log.e(TAG, savedInstanceState &#43; &quot;&quot;);  
  •   
  •         if (savedInstanceState == null)  
  •         {  
  •             mFOne = new FragmentOne();  
  •             FragmentManager fm = getFragmentManager();  
  •             FragmentTransaction tx = fm.beginTransaction();  
  •             tx.add(R.id.id_content, mFOne, &quot;ONE&quot;);  
  •             tx.commit();  
  •         }  
  •   
  •     }  
  •   
  •     @Override  
  •     public boolean onCreateOptionsMenu(Menu menu)  
  •     {  
  •         super.onCreateOptionsMenu(menu);  
  •         getMenuInflater().inflate(R.menu.main, menu);  
  •         return true;  
  •     }  
  •   
  •     @Override  
  •     public boolean onOptionsItemSelected(MenuItem item)  
  •     {  
  •         switch (item.getItemId())  
  •         {  
  •         case R.id.action_settings:  
  •             Toast.makeText(this, &quot;setting&quot;, Toast.LENGTH_SHORT).show();  
  •             return true;  
  •         default:  
  •             //如果希望Fragment自己处理MenuItem点击事件,一定不要忘了调用super.xxx  
  •             return super.onOptionsItemSelected(item);  
  •         }  
  •     }  
  •   
  • }  

效果图:  
DSC0007.jpg
  好了,可以很好的看到,Fragment可以添加MenuItem,也可以自己处理点击~~~
6、没有布局的Fragment的作用
  没有布局文件Fragment实际上是为了保存,当Activity重启时,保存大量数据准备的
  请参考博客:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案


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

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

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

  

  

更多0

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

  • 用 户 名:
  • u010906868


  • 评论内容:
  • DSC00015.jpg

      
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场 DSC00016.gif 核心技术类目
全部主题 Java VPN Android iOS ERP IE10 Eclipse CRM JavaScript Ubuntu NFCWAP jQuery 数据库 BI HTML5 Spring Apache Hadoop .NET API HTML SDK IISFedora XML LBS Unity Splashtop UML components Windows Mobile Rails QEMU KDECassandra CloudStack FTC coremail OPhone CouchBase 云计算 iOS6 RackspaceWeb App SpringSide Maemo Compuware 大数据 aptech Perl Tornado Ruby HibernateThinkPHP Spark HBase Pure Solr Angular Cloud Foundry Redis Scala DjangoBootstrap
    个人资料

    DSC00017.gif
    鸿洋_ DSC00018.jpg DSC00019.jpg

    • 访问:58410次
    • 积分:1926分
    • 排名:第6819名


    • 原创:86篇
    • 转载:0篇
    • 译文:5篇
    • 评论:253条


    博客专栏

    DSC00020.jpg HTML5 & CSS3 实战文章:11篇
    阅读:5834 DSC00021.gif 设计模式融入生活文章:10篇
    阅读:4140Android 精彩案例文章: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@iyunv.com 400-600-2320京 ICP 证 070598 号北京创新乐知信息技术有限公司 版权所有江苏乐知网络技术有限公司 提供商务支持Copyright &copy; 1999-2014, CSDN.NET, All Rights Reserved  








Hongyang


  • 目录视图
  • 摘要视图
  • 订阅
有奖征资源,博文分享有内涵        6月推荐文章汇总       CSDN博文大赛初赛晋级名单公布       Android Fragment 真正的完全解析(下)
分类: android进阶2014-07-21 09:09 1079人阅读 评论(7) 收藏 举报AndroidFragmentDialogFragmentMenuItem
目录(?)[+]

  转载请标明出处:http://blog.iyunv.com/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 plaincopy

  • <RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;  
  •     xmlns:tools=&quot;http://schemas.android.com/tools&quot;  
  •     android:layout_width=&quot;match_parent&quot;  
  •     android:layout_height=&quot;match_parent&quot; >  
  •   
  •     <FrameLayout  
  •         android:id=&quot;@+id/id_content&quot;  
  •         android:layout_width=&quot;fill_parent&quot;  
  •         android:layout_height=&quot;fill_parent&quot; >  
  •     </FrameLayout>  
  •   
  • </RelativeLayout>  
不同的Fragment就在这个FrameLayout中显示。  
  MainActivity.java
  
[java] view plaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.Window;  
  •   
  • public class MainActivity extends Activity  
  • {  
  •   
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.add(R.id.id_content, new FragmentOne(),&quot;ONE&quot;);  
  •         tx.commit();  
  •     }  
  •   
  • }  
很简单,直接将FragmentOne添加到布局文件中的FrameLayout中,注意这里并没有调用FragmentTransaction.addToBackStack(String),因为我不喜欢在当前显示时,点击Back键出现白板。而是正确的相应Back键,即退出我们的Activity.  
  下面是FragmentOne
  
[java] view plaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  •   
  • public class FragmentOne extends Fragment implements OnClickListener  
  • {  
  •   
  •     private Button mBtn;  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view;  
  •     }  
  •   
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         FragmentTwo fTwo = new FragmentTwo();  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.replace(R.id.id_content, fTwo, &quot;TWO&quot;);  
  •         tx.addToBackStack(null);  
  •         tx.commit();  
  •   
  •     }  
  •   
  • }  

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

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  •   
  • public class FragmentTwo extends Fragment implements OnClickListener  
  • {  
  •   
  •     private Button mBtn ;  
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_two, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view ;   
  •     }  
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         FragmentThree fThree = new FragmentThree();  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.hide(this);  
  •         tx.add(R.id.id_content , fThree, &quot;THREE&quot;);  
  • //      tx.replace(R.id.id_content, fThree, &quot;THREE&quot;);  
  •         tx.addToBackStack(null);  
  •         tx.commit();  
  •     }  
  •   
  •   
  • }  

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

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  • import android.widget.Toast;  
  •   
  • public class FragmentThree extends Fragment implements OnClickListener  
  • {  
  •   
  •     private Button mBtn;  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_three, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_three_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view;  
  •     }  
  •   
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         Toast.makeText(getActivity(), &quot; i am a btn in Fragment three&quot;,  
  •                 Toast.LENGTH_SHORT).show();  
  •     }  
  •   
  • }  

好了,经过上面的介绍,应该已经知道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 plaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  •   
  • public class FragmentOne extends Fragment implements OnClickListener  
  • {  
  •     private Button mBtn;  
  •   
  •     /**
  •      * 设置按钮点击的回调
  •      * @author zhy
  •      *
  •      */  
  •     public interface FOneBtnClickListener  
  •     {  
  •         void onFOneBtnClick();  
  •     }  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view;  
  •     }  
  •   
  •     /**
  •      * 交给宿主Activity处理,如果它希望处理
  •      */  
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         if (getActivity() instanceof FOneBtnClickListener)  
  •         {  
  •             ((FOneBtnClickListener) getActivity()).onFOneBtnClick();  
  •         }  
  •     }  
  •   
  • }  

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

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.View.OnClickListener;  
  • import android.view.ViewGroup;  
  • import android.widget.Button;  
  •   
  • public class FragmentTwo extends Fragment implements OnClickListener  
  • {  
  •   
  •       
  •     private Button mBtn ;  
  •       
  •     private FTwoBtnClickListener fTwoBtnClickListener ;  
  •       
  •     public interface FTwoBtnClickListener  
  •     {  
  •         void onFTwoBtnClick();  
  •     }  
  •     //设置回调接口  
  •     public void setfTwoBtnClickListener(FTwoBtnClickListener fTwoBtnClickListener)  
  •     {  
  •         this.fTwoBtnClickListener = fTwoBtnClickListener;  
  •     }  
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_two, container, false);  
  •         mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);  
  •         mBtn.setOnClickListener(this);  
  •         return view ;   
  •     }  
  •     @Override  
  •     public void onClick(View v)  
  •     {  
  •         if(fTwoBtnClickListener != null)  
  •         {  
  •             fTwoBtnClickListener.onFTwoBtnClick();  
  •         }  
  •     }  
  •   
  • }  

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

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.Window;  
  •   
  • import com.zhy.zhy_fragments.FragmentOne.FOneBtnClickListener;  
  • import com.zhy.zhy_fragments.FragmentTwo.FTwoBtnClickListener;  
  •   
  • public class MainActivity extends Activity implements FOneBtnClickListener,  
  •         FTwoBtnClickListener  
  • {  
  •   
  •     private FragmentOne mFOne;  
  •     private FragmentTwo mFTwo;  
  •     private FragmentThree mFThree;  
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         mFOne = new FragmentOne();  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.add(R.id.id_content, mFOne, &quot;ONE&quot;);  
  •         tx.commit();  
  •     }  
  •   
  •     /**
  •      * FragmentOne 按钮点击时的回调
  •      */  
  •     @Override  
  •     public void onFOneBtnClick()  
  •     {  
  •   
  •         if (mFTwo == null)  
  •         {  
  •             mFTwo = new FragmentTwo();  
  •             mFTwo.setfTwoBtnClickListener(this);  
  •         }  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.replace(R.id.id_content, mFTwo, &quot;TWO&quot;);  
  •         tx.addToBackStack(null);  
  •         tx.commit();  
  •     }  
  •   
  •     /**
  •      * FragmentTwo 按钮点击时的回调
  •      */  
  •     @Override  
  •     public void onFTwoBtnClick()  
  •     {  
  •         if (mFThree == null)  
  •         {  
  •             mFThree = new FragmentThree();  
  •   
  •         }  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.hide(mFTwo);  
  •         tx.add(R.id.id_content, mFThree, &quot;THREE&quot;);  
  •         // tx.replace(R.id.id_content, fThree, &quot;THREE&quot;);  
  •         tx.addToBackStack(null);  
  •         tx.commit();  
  •     }  
  •   
  • }  

代码重构结束,与开始的效果一模一样。上面两种通信方式都是值得推荐的,随便选择一种自己喜欢的。这里再提一下:虽然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 plaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.view.Window;  
  •   
  • public class MainActivity extends Activity  
  •   
  • {  
  •     private FragmentOne mFOne;  
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         mFOne = new FragmentOne();  
  •         FragmentManager fm = getFragmentManager();  
  •         FragmentTransaction tx = fm.beginTransaction();  
  •         tx.add(R.id.id_content, mFOne, &quot;ONE&quot;);  
  •         tx.commit();  
  •   
  •     }  
  •   
  • }  

Fragment  
  
[java] view plaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.util.Log;  
  • import android.view.LayoutInflater;  
  • import android.view.View;  
  • import android.view.ViewGroup;  
  •   
  • public class FragmentOne extends Fragment  
  • {  
  •     private static final String TAG = &quot;FragmentOne&quot;;  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         Log.e(TAG, &quot;onCreateView&quot;);  
  •         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  •         return view;  
  •     }  
  •   
  •     @Override  
  •     public void onCreate(Bundle savedInstanceState)  
  •     {  
  •         // TODO Auto-generated method stub  
  •         super.onCreate(savedInstanceState);  
  •   
  •         Log.e(TAG, &quot;onCreate&quot;);  
  •     }  
  •   
  •     @Override  
  •     public void onDestroyView()  
  •     {  
  •         // TODO Auto-generated method stub  
  •         super.onDestroyView();  
  •         Log.e(TAG, &quot;onDestroyView&quot;);  
  •     }  
  •   
  •     @Override  
  •     public void onDestroy()  
  •     {  
  •         // TODO Auto-generated method stub  
  •         super.onDestroy();  
  •         Log.e(TAG, &quot;onDestroy&quot;);  
  •     }  
  •   
  • }  

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

  • 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
  • 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
  • 07-20 08:18:46.651: E/FragmentOne(1633): onCreate  
  • 07-20 08:18:46.681: E/FragmentOne(1633): onCreateView  
  • 07-20 08:18:46.831: E/FragmentOne(1633): onCreateView  
  • 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 plaincopy

  • 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 plaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.util.Log;  
  • import android.view.Window;  
  •   
  • public class MainActivity extends Activity  
  •   
  • {  
  •     private static final String TAG = &quot;FragmentOne&quot;;  
  •     private FragmentOne mFOne;  
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         Log.e(TAG, savedInstanceState+&quot;&quot;);  
  •          
  •         if(savedInstanceState == null)  
  •         {  
  •             mFOne = new FragmentOne();  
  •             FragmentManager fm = getFragmentManager();  
  •             FragmentTransaction tx = fm.beginTransaction();  
  •             tx.add(R.id.id_content, mFOne, &quot;ONE&quot;);  
  •             tx.commit();  
  •         }  
  •          
  •          
  •   
  •     }  
  •   
  • }  

现在无论进行多次旋转都只会有一个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 plaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Fragment;  
  • import android.os.Bundle;  
  • import android.view.LayoutInflater;  
  • import android.view.Menu;  
  • import android.view.MenuInflater;  
  • import android.view.MenuItem;  
  • import android.view.View;  
  • import android.view.ViewGroup;  
  • import android.widget.Toast;  
  •   
  • public class FragmentOne extends Fragment  
  • {  
  •   
  •     @Override  
  •     public void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         setHasOptionsMenu(true);  
  •     }  
  •   
  •     @Override  
  •     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  •             Bundle savedInstanceState)  
  •     {  
  •         View view = inflater.inflate(R.layout.fragment_one, container, false);  
  •         return view;  
  •     }  
  •   
  •     @Override  
  •     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)  
  •     {  
  •         inflater.inflate(R.menu.fragment_menu, menu);  
  •     }  
  •   
  •     @Override  
  •     public boolean onOptionsItemSelected(MenuItem item)  
  •     {  
  •         switch (item.getItemId())  
  •         {  
  •         case R.id.id_menu_fra_test:  
  •             Toast.makeText(getActivity(), &quot;FragmentMenuItem1&quot;, Toast.LENGTH_SHORT).show();  
  •             break;  
  •         }  
  •         return true;  
  •     }  
  •   
  • }  

Activity  
  
[java] view plaincopy

  • package com.zhy.zhy_fragments;  
  •   
  • import android.app.Activity;  
  • import android.app.FragmentManager;  
  • import android.app.FragmentTransaction;  
  • import android.os.Bundle;  
  • import android.util.Log;  
  • import android.view.Menu;  
  • import android.view.MenuItem;  
  • import android.view.Window;  
  • import android.widget.Toast;  
  •   
  • public class MainActivity extends Activity  
  •   
  • {  
  •     private static final String TAG = &quot;FragmentOne&quot;;  
  •     private FragmentOne mFOne;  
  •   
  •     @Override  
  •     protected void onCreate(Bundle savedInstanceState)  
  •     {  
  •         super.onCreate(savedInstanceState);  
  •         requestWindowFeature(Window.FEATURE_NO_TITLE);  
  •         setContentView(R.layout.activity_main);  
  •   
  •         Log.e(TAG, savedInstanceState + &quot;&quot;);  
  •   
  •         if (savedInstanceState == null)  
  •         {  
  •             mFOne = new FragmentOne();  
  •             FragmentManager fm = getFragmentManager();  
  •             FragmentTransaction tx = fm.beginTransaction();  
  •             tx.add(R.id.id_content, mFOne, &quot;ONE&quot;);  
  •             tx.commit();  
  •         }  
  •   
  •     }  
  •   
  •     @Override  
  •     public boolean onCreateOptionsMenu(Menu menu)  
  •     {  
  •         super.onCreateOptionsMenu(menu);  
  •         getMenuInflater().inflate(R.menu.main, menu);  
  •         return true;  
  •     }  
  •   
  •     @Override  
  •     public boolean onOptionsItemSelected(MenuItem item)  
  •     {  
  •         switch (item.getItemId())  
  •         {  
  •         case R.id.action_settings:  
  •             Toast.makeText(this, &quot;setting&quot;, Toast.LENGTH_SHORT).show();  
  •             return true;  
  •         default:  
  •             //如果希望Fragment自己处理MenuItem点击事件,一定不要忘了调用super.xxx  
  •             return super.onOptionsItemSelected(item);  
  •         }  
  •     }  
  •   
  • }  

效果图:  

  好了,可以很好的看到,Fragment可以添加MenuItem,也可以自己处理点击~~~
6、没有布局的Fragment的作用
  没有布局文件Fragment实际上是为了保存,当Activity重启时,保存大量数据准备的
  请参考博客:Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案


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

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

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

  

  

更多0

  • 上一篇Android Fragment 真正的完全解析(上)
顶6踩0主题推荐androidrelativelayout对话框interfaceasynctask猜你在找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小时前发表 [回复]mark2楼 soledadzz 昨天 14:59发表 [回复]您的文章已被推荐到CSDN首页,感谢您的分享。Re: 鸿洋_ 昨天 19:26发表 [回复]回复soledadzz:多谢1楼 天桥下的程序猿 昨天 09:15发表 [回复]顶完再看~~~发表评论

  • 用 户 名:
  • u010906868


  • 评论内容:


      
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场核心技术类目
全部主题 Java VPN Android iOS ERP IE10 Eclipse CRM JavaScript Ubuntu NFCWAP jQuery 数据库 BI HTML5 Spring Apache Hadoop .NET API HTML SDK IISFedora XML LBS Unity Splashtop UML components WindowsMobile Rails QEMU KDECassandra CloudStack FTC coremail OPhone CouchBase 云计算 iOS6 RackspaceWebApp SpringSide Maemo Compuware 大数据 aptech Perl Tornado Ruby HibernateThinkPHP Spark HBase Pure Solr Angular CloudFoundry Redis Scala DjangoBootstrap
    个人资料


    鸿洋_

    • 访问:58410次
    • 积分:1926分
    • 排名:第6819名


    • 原创:86篇
    • 转载:0篇
    • 译文:5篇
    • 评论:253条


    博客专栏

    HTML5 & CSS3 实战文章:11篇
    阅读:5834设计模式融入生活文章:10篇
    阅读:4140Android 精彩案例文章: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&#43;TabPageIndicator&#43;ViewPager(1854)
  • AndroidSwipeRefreshLayout 官方下拉刷新控件介绍(1575)
  • Android仿Win8的metro的UI界面(上)(1573)
  • Java/ Android 基于Http的多线程下载的实现(1543)
  • CSDNAndroid客户端的制作 导航帖(1387)
  • AndroidFragment 真正的完全解析(下)(1047)

    评论排行


  • CSDNAndroid客户端的制作 导航帖(27)
  • Android自定义ViewGroup手把手教你实现ArcMenu(16)
  • Java/ Android 基于Http的多线程下载的实现(13)
  • Android官方推荐 : DialogFragment 创建对话框(13)
  • AndroidAdapterView View的复用机制 分析(9)
  • Android完美实现图片圆角和圆形(对实现进行分析)(9)
  • 高仿微信5.2.1主界面架构包含消息通知(8)
  • Android推送百度云推送 入门篇(8)
  • Android自定义View (二) 进阶(8)
  • Android项目Tab类型主界面大总结Fragment&#43;TabPageIndicator&#43;ViewPager(8)

    最新评论


  • AndroidFragment 真正的完全解析(上)鸿洋_:@shunjian_miss:注意Activity和Fragment通信~
  • AndroidFragment 真正的完全解析(上)鸿洋_:@soledadzz:多谢
  • Android官方推荐 : DialogFragment 创建对话框天桥下的程序猿:顶~
  • AndroidFragment 真正的完全解析(下)DanteStones:支持鸿洋 外加 写的真不错
  • Android使用Fragment,ViewPagerIndicator 制作csdn app主要框架anroidfinalbreak:TabPageIndicator 这个事自定义的吧,源码中为何没有
  • AndroidJava汉字转拼音总结wp2745405:这些转换方式都只能对单音字进行处理,如果存在多音字,这些转换方式都存在bug
  • AndroidFragment 真正的完全解析(下)feong:文章好棒,图文并茂,学到不少知识。
  • AndroidFragment 真正的完全解析(上)soledadzz:您的文章已被推荐到博客首页和个人页侧边栏推荐文章,感谢您的分享。
  • AndroidFragment 真正的完全解析(上)七夜Jungle:很好的东西诶
  • AndroidSwipeRefreshLayout 官方下拉刷新控件介绍scort1:很好 , 很容易理解 ,慢慢的再加上动画效果就好了,慢慢来
公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈网站客服 杂志客服 微博客服 webmaster@iyunv.com 400-600-2320京 ICP 证 070598 号北京创新乐知信息技术有限公司 版权所有江苏乐知网络技术有限公司 提供商务支持Copyright © 1999-2014, CSDN.NET, All Rights Reserved

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-126421-1-1.html 上篇帖子: apache基金会开源项目简介 下篇帖子: 九月十月百度人搜,阿里巴巴,腾讯华为小米搜狗笔试面试八十题
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表