Fragment

      Android在3.0中引入了fragments的概念,主要目的是用在大屏幕设备上--例如平板电脑上,支持更加动态和灵活的UI设计。平板电脑的屏幕要比手机的大得多,有更多的空间来放更多的UI组件,并且这些组件之间会产生更多的交互。Fragment允许这样的一种设计,而不需要你亲自来管理 viewhierarchy的复杂变化。 通过将activity的布局分散到fragment中, 你可以在运行时修改activity的外观,并在由activity管理的back stack中保存那些变化。当一个片段指定了自身的布局时,它能和其他片段配置成不同的组合,在活动中为不同的屏幕尺寸修改布局配置(小屏幕可能每次显示一个片段,而大屏幕则可以显示两个或更多)。

                        

二、谁在使用Fragment

 

  • 网易新闻
  • 网易云音乐
  • 百度音乐
  • 多米
  • 豌豆荚
  • 小米app
  • Path
  • Pocket
  • Fuubo

 三、Fragment的优点和缺点

 

 

  • adding and removing Fragment可以做动画的效果,平滑过度
  • 自动化堆栈管理,所以返回键可以删除动态添加的Fragment,最后销毁Activity,无需做过多判断
  • 集成ActionBar的标签,可以替代TabHost,ActivityGrounp,与谷歌设计风格紧密结合
  • 布局更加模块化.与原Activity中的Layout分块化,VCBase的分块化道理相同
  • 灵活准确的生命周期去管理当前View的状态记录以及横竖屏处理
  • Fragment管理的View,可同时使用在Phone和Pad上,一份代码两份机器,可重用性高
  • Is a View, More than View
  • 可以从startActivityForResult中接收到返回结果,但是View不能
  • 唯一Id标识,可以从FragmentManager中获取id对应的Fragment

与其说是Fragment的缺点,不如说是每个应用程序模块之间的通讯都面临地耦合问题

 

 

  • Fragment之间的通讯依赖Activity使用接口管理并通知

 

 

四、如何使用Fragment

        如果你的应用版本在3.0以上,那就直接使用Fragment就可以了,它在android.app.Fragment这个里面。如果版本低于3.0,那就需要使用android-support-v4.jar这个jar包了,Android Support Library(支持库)提供了包含一个API库的JAR文件,当你的应用运行在Android早期版本时,Support Library(支持库)允许你的应用使用一些最近版本的Android API。注意:现在创建程序的时候,默认会有android-support-v4.jar这个包,所以如果是3.0之后在继承Fragment时会有两个Fragment包,要选择android.app.Fragment。

        你可以把fragment看作是activity的模块化组件,它拥有自己的生命周期,

接受它自己的输入事件,你也可以在运行activity的时候添加或者移除它(有点像可以在不同的activity中重用的“子activity”)。这节课演示怎么样使用Support Library继承Fragment类,如此你的app(应用)就能与运行android1.6老版本的系统设备兼容 。注意:如果你因为一些其他原因决定你的app需要的最低API版本为11或者更高,那你就没必要使用Support Library,而可以直接使用框架内置的Fragment类和相关API。我们分别使用来自Support Library的API和内置在平台(API),使用特定包名下的API跟内置在平台(API)的还是略有不同。

         使用Fragment有两种方式:一是像在布局文件添加一个控件一样,在xml里面声明一个Fragment就行;例如下面这样:

 

  1.    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:orientation="horizontal"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent">  
  5.   
  6.     <fragment android:name="com.example.android.fragments.HeadlinesFragment"  
  7.               android:id="@+id/headlines_fragment"  
  8.               android:layout_weight="1"  
  9.               android:layout_width="0dp"  
  10.               android:layout_height="match_parent" />  
  11.   
  12.     <fragment android:name="com.example.android.fragments.ArticleFragment"  
  13.               android:id="@+id/article_fragment"  
  14.               android:layout_weight="2"  
  15.               android:layout_width="0dp"  
  16.               android:layout_height="match_parent" />  
  17.   
  18. </LinearLayout>  
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

    <fragment android:name="com.example.android.fragments.ArticleFragment"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>

            二是在程序里面动态的创建、删除、替换等。这个需要使用到事务,类似于sqlitel里面的事务。

 

 

  1. //得到一个fragment 事务(类似sqlite的操作)  
  2. FragmentTransaction ft = getFragmentManager()  
  3.         .beginTransaction();  
  4. ft.replace(R.id.details, details);//将得到的fragment 替换当前的viewGroup内容,add则不替换会依次累加  
  5. ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);//设置动画效果  
  6. ft.commit();//提交  
                    //得到一个fragment 事务(类似sqlite的操作)
                    FragmentTransaction ft = getFragmentManager()
                            .beginTransaction();
                    ft.replace(R.id.details, details);//将得到的fragment 替换当前的viewGroup内容,add则不替换会依次累加
                    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);//设置动画效果
                    ft.commit();//提交

             1、先用第一中方式接受如何使用Fragment。

                    要创建一个fragment需要继承Fragment类,然后重写关键的生命周期方法插入你自己的应用逻辑,操作的方式跟创建一个Activity类似。

不同的是当你创建一个Fragment时,你必须使用onCreateView()回调去定义你的布局。事实上,这是唯一的需要让fragment获得运行的回调函数。例如,这里有一个简单的指定了自己布局的fragment:

 

  1. import android.os.Bundle;  
  2.   import android.support.v4.app.Fragment;  
  3.   import android.view.LayoutInflater;  
  4.   import android.view.ViewGroup;  
  5.   
  6.   public class ArticleFragment extends Fragment {  
  7.   @Override  
  8.   public View onCreateView(LayoutInflater inflater, ViewGroup container,   
  9.       Bundle savedInstanceState) {  
  10.       // Inflate the layout for this fragment  
  11.       return inflater.inflate(R.layout.article_view, container, false);  
  12.   }  
  13.   }  
  import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.view.LayoutInflater;
    import android.view.ViewGroup;

    public class ArticleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
        Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.article_view, container, false);
    }
    }

                    就跟activity一样,fragment也应该要实现其他的生命周期方法来管理它的状态,比如当fragment被activity添加或者删除,当activity生命周期状态转换时(类似这种操作都会影响到fragment的状态)。例如,当activity的onPuase()方法被调用的时候,所有activity中的fragment也都会接收到onPause()方法的调用。

 

                    因为fragment是可重用的,模块化的UI组件,每一个Fragment实例必须与父类FragmentActivity相关联。你可以通过在你Activity布局XML文件中定义每一个fragment来获得关联。注意:FragmentActivity是在系统版本低于API level 11时由Support Library提供用来管理fragment的特殊activity。如果你支持的最低系统版本是API level 11或者更高,那你可以直接使用常规的Activity。以下是一个例子,当设备屏幕被认为“大”的时候,一个布局文件添加了两个fragment到activity。当屏幕比较大的时候(比如平板)是可以同时显示两个fragment的,但是屏幕比较小(比如普通手机)同一时间只能显示一个fragment,这是由于它们的屏幕尺寸造成的。这个布局文件被指定在“高”分辨率的目录名下。(译者注:请注意下面xml的目录结构:是在res目录下的layout-large目录下,这样的目录下存放的文件通常都是用来支持高分辨率的布局文件)res/layout-large/news_articles.xml:

 

  1.    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:orientation="horizontal"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent">  
  5.   
  6.     <fragment android:name="com.example.android.fragments.HeadlinesFragment"  
  7.               android:id="@+id/headlines_fragment"  
  8.               android:layout_weight="1"  
  9.               android:layout_width="0dp"  
  10.               android:layout_height="match_parent" />  
  11.   
  12.     <fragment android:name="com.example.android.fragments.ArticleFragment"  
  13.               android:id="@+id/article_fragment"  
  14.               android:layout_weight="2"  
  15.               android:layout_width="0dp"  
  16.               android:layout_height="match_parent" />  
  17.   
  18. </LinearLayout>  
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

    <fragment android:name="com.example.android.fragments.ArticleFragment"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>

                     这里展示了在activity中怎样运用这个布局:

 

  1. import android.os.Bundle;  
  2.     import android.support.v4.app.FragmentActivity;  
  3.   
  4.     public class MainActivity extends FragmentActivity {  
  5.     @Override  
  6.     public void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.news_articles);  
  9.     }  
  10.     }  
import android.os.Bundle;
    import android.support.v4.app.FragmentActivity;

    public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);
    }
    }

                      注意:当你通过在布局XML文件中定义fragment以达到添加一个fragment到activity中时,你不能在运行时移除此fragment。在Activity中定义了两个Fragment,一个是放在左边的LeftFragment,一个是放在右边的RightFragment.以下是代码:首先我们要实现自己的Fragment类。

 

                   LeftFragment类:

 

  1. package com.example.teblets;  
  2.   
  3. import android.app.Activity;  
  4. import android.app.Fragment;  
  5. import android.os.Bundle;  
  6. import android.view.LayoutInflater;  
  7. import android.view.View;  
  8. import android.view.ViewGroup;  
  9.   
  10. public class LeftFragment extends Fragment {  
  11.   
  12.     @Override  
  13.     public void onAttach(Activity activity) {  
  14.         // TODO Auto-generated method stub  
  15.         super.onAttach(activity);  
  16.         System.out.println("LeftFragment onAttach");  
  17.     }  
  18.   
  19.     @Override  
  20.     public void onCreate(Bundle savedInstanceState) {  
  21.         // TODO Auto-generated method stub  
  22.         super.onCreate(savedInstanceState);  
  23.         System.out.println("LeftFragment onCreate");  
  24.     }  
  25.   
  26.     @Override  
  27.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  28.             Bundle savedInstanceState) {  
  29.         // TODO Auto-generated method stub  
  30.         return inflater.inflate(R.layout.fragment_left, container,true);  
  31.           
  32.     }  
  33.   
  34.     @Override  
  35.     public void onDestroy() {  
  36.         // TODO Auto-generated method stub  
  37.         super.onDestroy();  
  38.         System.out.println("LeftFragment onDestroy");  
  39.     }  
  40.   
  41.     @Override  
  42.     public void onDestroyView() {  
  43.         // TODO Auto-generated method stub  
  44.         super.onDestroyView();  
  45.         System.out.println("LeftFragment onDestroyView");  
  46.     }  
  47.   
  48.     @Override  
  49.     public void onPause() {  
  50.         // TODO Auto-generated method stub  
  51.         super.onPause();  
  52.         System.out.println("LeftFragment onPause");  
  53.     }  
  54.   
  55.     @Override  
  56.     public void onResume() {  
  57.         // TODO Auto-generated method stub  
  58.         super.onResume();  
  59.         System.out.println("LeftFragment onResume");  
  60.     }  
  61.   
  62.     @Override  
  63.     public void onStart() {  
  64.         // TODO Auto-generated method stub  
  65.         super.onStart();  
  66.         System.out.println("LeftFragment onStart");  
  67.     }  
  68.   
  69.     @Override  
  70.     public void onStop() {  
  71.         // TODO Auto-generated method stub  
  72.         super.onStop();  
  73.         System.out.println("LeftFragment onStop");  
  74.     }  
  75.   
  76.   
  77.   
  78.       
  79. }  
package com.example.teblets;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class LeftFragment extends Fragment {

	@Override
	public void onAttach(Activity activity) {
		// TODO Auto-generated method stub
		super.onAttach(activity);
		System.out.println("LeftFragment onAttach");
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		System.out.println("LeftFragment onCreate");
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		return inflater.inflate(R.layout.fragment_left, container,true);
		
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		System.out.println("LeftFragment onDestroy");
	}

	@Override
	public void onDestroyView() {
		// TODO Auto-generated method stub
		super.onDestroyView();
		System.out.println("LeftFragment onDestroyView");
	}

	@Override
	public void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		System.out.println("LeftFragment onPause");
	}

	@Override
	public void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		System.out.println("LeftFragment onResume");
	}

	@Override
	public void onStart() {
		// TODO Auto-generated method stub
		super.onStart();
		System.out.println("LeftFragment onStart");
	}

	@Override
	public void onStop() {
		// TODO Auto-generated method stub
		super.onStop();
		System.out.println("LeftFragment onStop");
	}



	
}

                        这里实现了几种回调函数,主要是为了看清Activity和Fragment生命周期之间的关系.其中onCreateView()方法是将本Fragment对应的布局返回给Activity的布局,让Activity进行加载. inflater.inflate(R.layout.leftfragment, container, true)方法中的第一个参数R.layout.leftfragment是这个Fragment对应的布局文件ID, 第二个参数container是要插入的目标Activity, 第三个参数是决定这个Fragment是否依附于这个container。

                         LeftFragment对应的布局文件:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2.  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.      android:layout_width="match_parent"  
  4.      android:layout_height="match_parent"  
  5.      android:background="@android:color/holo_orange_dark"  
  6.      android:orientation="vertical" >  
  7.    
  8.      <Button  
  9.  android:id="@+id/previous_button"  
  10.          android:layout_width="fill_parent"  
  11.          android:layout_height="wrap_content"  
  12.          android:text="@string/previous_button" />  
  13.    
  14.      <Button  
  15.  android:id="@+id/next_button"  
  16.          android:layout_width="fill_parent"  
  17.          android:layout_height="wrap_content"  
  18.          android:text="@string/next_button" />  
  19.    
  20.      <Button  
  21.  android:id="@+id/exit_button"  
  22.          android:layout_width="fill_parent"  
  23.          android:layout_height="wrap_content"  
  24.          android:text="@string/exit_button" />  
  25.    
  26.  </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:background="@android:color/holo_orange_dark"
     android:orientation="vertical" >
 
     <Button
 android:id="@+id/previous_button"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="@string/previous_button" />
 
     <Button
 android:id="@+id/next_button"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="@string/next_button" />
 
     <Button
 android:id="@+id/exit_button"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="@string/exit_button" />
 
 </LinearLayout>

                           RightFragment类:

 

  1. package com.example.teblets;  
  2.   
  3. import android.app.Activity;  
  4. import android.app.Fragment;  
  5. import android.os.Bundle;  
  6. import android.view.LayoutInflater;  
  7. import android.view.View;  
  8. import android.view.ViewGroup;  
  9.   
  10. public class RightFragment extends Fragment {  
  11.   
  12.     @Override  
  13.     public void onAttach(Activity activity) {  
  14.         // TODO Auto-generated method stub  
  15.         super.onAttach(activity);  
  16.         System.out.println("LeftFragment onAttach");  
  17.     }  
  18.   
  19.     @Override  
  20.     public void onCreate(Bundle savedInstanceState) {  
  21.         // TODO Auto-generated method stub  
  22.         super.onCreate(savedInstanceState);  
  23.         System.out.println("LeftFragment onCreate");  
  24.     }  
  25.   
  26.     @Override  
  27.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  28.             Bundle savedInstanceState) {  
  29.         // TODO Auto-generated method stub  
  30.         return inflater.inflate(R.layout.fragment_right, container,true);  
  31.           
  32.     }  
  33.   
  34.     @Override  
  35.     public void onDestroy() {  
  36.         // TODO Auto-generated method stub  
  37.         super.onDestroy();  
  38.         System.out.println("LeftFragment onDestroy");  
  39.     }  
  40.   
  41.     @Override  
  42.     public void onDestroyView() {  
  43.         // TODO Auto-generated method stub  
  44.         super.onDestroyView();  
  45.         System.out.println("LeftFragment onDestroyView");  
  46.     }  
  47.   
  48.     @Override  
  49.     public void onPause() {  
  50.         // TODO Auto-generated method stub  
  51.         super.onPause();  
  52.         System.out.println("LeftFragment onPause");  
  53.     }  
  54.   
  55.     @Override  
  56.     public void onResume() {  
  57.         // TODO Auto-generated method stub  
  58.         super.onResume();  
  59.         System.out.println("LeftFragment onResume");  
  60.     }  
  61.   
  62.     @Override  
  63.     public void onStart() {  
  64.         // TODO Auto-generated method stub  
  65.         super.onStart();  
  66.         System.out.println("LeftFragment onStart");  
  67.     }  
  68.   
  69.     @Override  
  70.     public void onStop() {  
  71.         // TODO Auto-generated method stub  
  72.         super.onStop();  
  73.         System.out.println("LeftFragment onStop");  
  74.     }  
  75.   
  76.   
  77.   
  78.       
  79. }  
package com.example.teblets;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class RightFragment extends Fragment {

	@Override
	public void onAttach(Activity activity) {
		// TODO Auto-generated method stub
		super.onAttach(activity);
		System.out.println("LeftFragment onAttach");
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		System.out.println("LeftFragment onCreate");
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		return inflater.inflate(R.layout.fragment_right, container,true);
		
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		System.out.println("LeftFragment onDestroy");
	}

	@Override
	public void onDestroyView() {
		// TODO Auto-generated method stub
		super.onDestroyView();
		System.out.println("LeftFragment onDestroyView");
	}

	@Override
	public void onPause() {
		// TODO Auto-generated method stub
		super.onPause();
		System.out.println("LeftFragment onPause");
	}

	@Override
	public void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
		System.out.println("LeftFragment onResume");
	}

	@Override
	public void onStart() {
		// TODO Auto-generated method stub
		super.onStart();
		System.out.println("LeftFragment onStart");
	}

	@Override
	public void onStop() {
		// TODO Auto-generated method stub
		super.onStop();
		System.out.println("LeftFragment onStop");
	}



	
}

                      RightFragment对应的布局文件:

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2.  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.      android:layout_width="match_parent"  
  4.      android:layout_height="match_parent"  
  5.      android:orientation="vertical" >  
  6.    
  7.      <TextView  
  8.  android:id="@+id/show_message"  
  9.          android:layout_width="fill_parent"  
  10.          android:layout_height="fill_parent"  
  11.          android:background="@android:color/holo_blue_dark"  
  12.          android:text="@string/show_message" />  
  13.    
  14.  </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical" >
 
     <TextView
 android:id="@+id/show_message"
         android:layout_width="fill_parent"
         android:layout_height="fill_parent"
         android:background="@android:color/holo_blue_dark"
         android:text="@string/show_message" />
 
 </LinearLayout>

                       最后是Activity的布局文件:

 

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:baselineAligned="false"   
  6.     android:orientation="horizontal" >  
  7.   
  8.     <fragment  
  9.         android:id="@+id/left_fragment"  
  10.         android:name="com.example.teblets.LeftFragment"  
  11.         android:layout_width="match_parent"  
  12.         android:layout_height="fill_parent"  
  13.         android:layout_weight="3" />  
  14.   
  15.     <fragment  
  16.         android:id="@+id/right_fragment"  
  17.         android:name="com.example.teblets.RightFragment"  
  18.         android:layout_width="match_parent"  
  19.         android:layout_height="fill_parent"  
  20.         android:layout_weight="1" />  
  21.   
  22. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:baselineAligned="false" 
    android:orientation="horizontal" >

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.example.teblets.LeftFragment"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_weight="3" />

    <fragment
        android:id="@+id/right_fragment"
        android:name="com.example.teblets.RightFragment"
        android:layout_width="match_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1" />

</LinearLayout>

                 在Activity中的布局文件中加入Fragment标签,其中android:name属性对应的就是自定义Fragment类的全名,系统会根据这个调用指定的Fragment的onCreateView()方法来得到这个Fragment的布局,然后加入Activity中. onCreateView()方法中的Container参数就是这时候传递过去的。显示结果:

打开程序时生命周期显示:         

 

按返回键时生命周期显示:

 

                         2、动态使用Fragment

                               相比上节课提到的使用标签在布局文件中为activity定义一个fragment组件,更好的方式是在activity运行时添加fragment。如果你想在activity的生命周期中变换fragment的话就必须这样做。执行类似添加或者删除fragment的事务,你必须使用FragmentManager创建一个FragmentTransaction,它提供了添加,删除以及其他fragment事务的API。如果你的activity允许移除或者替换fragment,你应该在activity的onCreate())方法中添加初始化的fragment。在你处理fragment(尤其是你在运行时添加的那些)的时候,有一个很重要的规则就是你的fragment放置位置的布局中必须有一个视图容器。下面这个布局是上节课在同一时间只显示一个fragment布局的替代品。为了将一个fragment替换成另一个,这个activity布局包含了一个空的FrameLayout作为fragment容器。注意这个文件名跟上节课的布局文件名字一样,但是这个布局并没有指定在“高分辨率”目录中(译者注:请注意下面xml的路径,res/layout这个layout文件夹并没有像上节课提到的是一个layout-large文件夹),如此这个布局是用在比large更小的设备屏幕上,因为这个屏幕不能在同一时间充满两个fragment。res/layout/news_articles.xml:

 

  1. <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.    android:id="@+id/fragment_container"  
  3.    android:layout_width="match_parent"  
  4.    android:layout_height="match_parent" />  
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

                                在你的activity中,使用Support LibraryAPI,调用getSupportFragmentManager()可以得到一个FragmentManager对象,之后调用beginTransaction去创建一个FragmentTransaction对象,再调用add()方法即可添加一个fragment。
你可以对activity使用同一个FragmentTransaction对象去执行多个fragment事务,当你确定要做这些操作时,你必须调用 commit()方法。
例如,以下代码演示怎样添加一个fragment到前面的layout:

 

 

  1. import android.os.Bundle;  
  2. import android.support.v4.app.FragmentActivity;  
  3.   
  4.     public class MainActivity extends FragmentActivity {  
  5.     @Override  
  6.     public void onCreate(Bundle savedInstanceState) {  
  7.         super.onCreate(savedInstanceState);  
  8.         setContentView(R.layout.news_articles);  
  9.   
  10.         // Check that the activity is using the layout version with  
  11.         // the fragment_container FrameLayout  
  12.         if (findViewById(R.id.fragment_container) != null) {  
  13.   
  14.             // However, if we're being restored from a previous state,  
  15.             // then we don't need to do anything and should return or else  
  16.             // we could end up with overlapping fragments.  
  17.             if (savedInstanceState != null) {  
  18.                 return;  
  19.             }  
  20.   
  21.             // Create an instance of ExampleFragment  
  22.             HeadlinesFragment firstFragment = new HeadlinesFragment();  
  23.   
  24.             // In case this activity was started with special instructions from an Intent,  
  25.             // pass the Intent's extras to the fragment as arguments  
  26.             firstFragment.setArguments(getIntent().getExtras());  
  27.   
  28.             // Add the fragment to the 'fragment_container' FrameLayout  
  29.             getSupportFragmentManager().beginTransaction()  
  30.                     .add(R.id.fragment_container, firstFragment).commit();  
  31.         }  
  32.     }  
  33.     }  
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

    public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);

        // Check that the activity is using the layout version with
        // the fragment_container FrameLayout
        if (findViewById(R.id.fragment_container) != null) {

            // However, if we're being restored from a previous state,
            // then we don't need to do anything and should return or else
            // we could end up with overlapping fragments.
            if (savedInstanceState != null) {
                return;
            }

            // Create an instance of ExampleFragment
            HeadlinesFragment firstFragment = new HeadlinesFragment();

            // In case this activity was started with special instructions from an Intent,
            // pass the Intent's extras to the fragment as arguments
            firstFragment.setArguments(getIntent().getExtras());

            // Add the fragment to the 'fragment_container' FrameLayout
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.fragment_container, firstFragment).commit();
        }
    }
    }

                       由于fragment是在运行时添加到FrameLayout,而不是直接使用标签定义在activity的布局中,activity可以移除它或者使用另外一个不同的fragment替换它。替换一个fragment的过程跟添加差不多,但是需要的是replace()方法,而不是add()方法。
                       需要注意的是,当你执行fragment事务时,比如替换或者删除一个fragment。允许用户“后退”或者“撤销”改变通常是比较合适的做法。为了让用户可以通过fragment事务“后退”,你必须在你提交fragment事务之前调用addToBackStack()方法。当你移除或者替换fragment且将事务添加到堆栈中时,被移除的fragment是被停止了(没有消亡)。如果用户导航回来重新加载这个fragment,它将会重新启动;如果你没有把事务加入到堆栈中,当fragment被删除或者替换时,这个fragment也就消亡了;

 

以下使用fragment替换另一个的例子:

  1. // Create fragment and give it an argument specifying the article it should show  
  2. ArticleFragment newFragment = new ArticleFragment();  
  3. Bundle args = new Bundle();  
  4. args.putInt(ArticleFragment.ARG_POSITION, position);  
  5. newFragment.setArguments(args);  
  6.   
  7. FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();  
  8.   
  9. // Replace whatever is in the fragment_container view with this fragment,  
  10. // and add the transaction to the back stack so the user can navigate back  
  11. transaction.replace(R.id.fragment_container, newFragment);  
  12. transaction.addToBackStack(null);  
  13.   
  14. // Commit the transaction  
  15. transaction.commit();  
    // Create fragment and give it an argument specifying the article it should show
    ArticleFragment newFragment = new ArticleFragment();
    Bundle args = new Bundle();
    args.putInt(ArticleFragment.ARG_POSITION, position);
    newFragment.setArguments(args);

    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

    // Replace whatever is in the fragment_container view with this fragment,
    // and add the transaction to the back stack so the user can navigate back
    transaction.replace(R.id.fragment_container, newFragment);
    transaction.addToBackStack(null);

    // Commit the transaction
    transaction.commit();

                         addToBackStack()方法有一个可选的字符串参数,用来指定事务的唯一名称。这个名称不是必须的除非你打算使用FragmentManager.BackStackEntry API执行更高级的fragment操作。
                          为了重用Fragment UI组件,你应该将Fragment建立成完全独立,模块化并且定义了自己布局和行为的组件。一旦你定义了这些可重用的Fragment,你可以通过activity,应用程序逻辑使它们关联,交互以组成一个整体复合型UI。
通常情况下,你希望一个Fragment可以与另一个交互,比如基于用户事件去修改内容。所有Fragment到Fragment的交互都是通过相关联的activity来做的。两个fragment应该从不直接交互。
                           为了允许Fragment与它的activity交互,你可以在fragment类中定义一个接口并且在activity中实现它。fragment可以在生命周期中的onAttach()方法获取接口的实现并调用接口的方法与activity交互。以下是fragment到activity的交互例子:

  1. public class HeadlinesFragment extends ListFragment {  
  2.     OnHeadlineSelectedListener mCallback;  
  3.   
  4.     // Container Activity must implement this interface  
  5.     public interface OnHeadlineSelectedListener {  
  6.         public void onArticleSelected(int position);  
  7.     }  
  8.   
  9.     @Override  
  10.     public void onAttach(Activity activity) {  
  11.         super.onAttach(activity);  
  12.   
  13.         // This makes sure that the container activity has implemented  
  14.         // the callback interface. If not, it throws an exception  
  15.         try {  
  16.             mCallback = (OnHeadlineSelectedListener) activity;  
  17.         } catch (ClassCastException e) {  
  18.             throw new ClassCastException(activity.toString()  
  19.                     + " must implement OnHeadlineSelectedListener");  
  20.         }  
  21.     }  
  22.   
  23.     ...  
  24. }  
public class HeadlinesFragment extends ListFragment {
    OnHeadlineSelectedListener mCallback;

    // Container Activity must implement this interface
    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try {
            mCallback = (OnHeadlineSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnHeadlineSelectedListener");
        }
    }

    ...
}

                          现在fragment可以使用OnHeadlineSelectedListener的实例mCallback调用onArticleSelected()方法(或者其他接口内的方法)提供信息给activity了。
例如,当用户点击list item(list子项)时就会调用下面在fragment的方法。fragment使用回调接口提供事件到父的activity。

 

  1. @Override  
  2.     public void onListItemClick(ListView l, View v, int position, long id) {  
  3.         // Send the event to the host activity  
  4.         mCallback.onArticleSelected(position);  
  5.     }  
@Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Send the event to the host activity
        mCallback.onArticleSelected(position);
    }

                           为了接收来自fragment的事件回调,包含fragment的activity(你需要用来与fragment交互的activity)必须实现定义在fragment类中的接口。
例如:下面这个activity就实现了上一例子中的接口:

 

  1. public static class MainActivity extends Activity  
  2.         implements HeadlinesFragment.OnHeadlineSelectedListener{  
  3.     ...  
  4.   
  5.     public void onArticleSelected(Uri articleUri) {  
  6.         // The user selected the headline of an article from the HeadlinesFragment  
  7.         // Do something here to display that article  
  8.     }  
  9. }  
public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(Uri articleUri) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

                             宿主activity可以使用findFragmentById()方法获取Fragment实例,然后直接调用fragment的公共方法提供信息给fragment。

例如,假设在上面显示的那个activity中可能包含另外一个fragment,并且用来显示由上面那个回调方法返回的数据指定的项目。在这个案例中,这个activity可以从回调函数中获得信息并且传递给其他显示项目的fragment:

 

  1. public static class MainActivity extends Activity  
  2.         implements HeadlinesFragment.OnHeadlineSelectedListener{  
  3.     ...  
  4.   
  5.     public void onArticleSelected(int position) {  
  6.         // The user selected the headline of an article from the HeadlinesFragment  
  7.         // Do something here to display that article  
  8.   
  9.         ArticleFragment articleFrag = (ArticleFragment)  
  10.                 getSupportFragmentManager().findFragmentById(R.id.article_fragment);  
  11.   
  12.         if (articleFrag != null) {  
  13.             // If article frag is available, we're in two-pane layout...  
  14.   
  15.             // Call a method in the ArticleFragment to update its content  
  16.             articleFrag.updateArticleView(position);  
  17.         } else {  
  18.             // Otherwise, we're in the one-pane layout and must swap frags...  
  19.   
  20.             // Create fragment and give it an argument for the selected article  
  21.             ArticleFragment newFragment = new ArticleFragment();  
  22.             Bundle args = new Bundle();  
  23.             args.putInt(ArticleFragment.ARG_POSITION, position);  
  24.             newFragment.setArguments(args);  
  25.   
  26.             FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();  
  27.   
  28.             // Replace whatever is in the fragment_container view with this fragment,  
  29.             // and add the transaction to the back stack so the user can navigate back  
  30.             transaction.replace(R.id.fragment_container, newFragment);  
  31.             transaction.addToBackStack(null);  
  32.   
  33.             // Commit the transaction  
  34.             transaction.commit();  
  35.         }  
  36.     }  
  37. }  
public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article

        ArticleFragment articleFrag = (ArticleFragment)
                getSupportFragmentManager().findFragmentById(R.id.article_fragment);

        if (articleFrag != null) {
            // If article frag is available, we're in two-pane layout...

            // Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position);
        } else {
            // Otherwise, we're in the one-pane layout and must swap frags...

            // Create fragment and give it an argument for the selected article
            ArticleFragment newFragment = new ArticleFragment();
            Bundle args = new Bundle();
            args.putInt(ArticleFragment.ARG_POSITION, position);
            newFragment.setArguments(args);

            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

            // Replace whatever is in the fragment_container view with this fragment,
            // and add the transaction to the back stack so the user can navigate back
            transaction.replace(R.id.fragment_container, newFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();
        }
    }
}

 

五、应用案例

                      1、Fragment+FragmentTabHost实现仿新浪微博底部菜单栏TabHost其实已经被弃用了,这个例子就是用来体现一下Fragment的用法,很简单在切换TabHost的时候加载Fragment。个人觉得意义不大,还不如那种直接加载Activity,里面再嵌套Fragment来的实在。

                      2、通过SlidingMenu+Fragment实现当前最流行的侧滑

                      3、 基于Android Fragment功能的例子:这个例子Fragment之间的交互没有使用接口的形式,而是直接调用,这样做比较方便,但是不规范。

                      4、Android开发 侧边滑动菜单栏SlidingMenu结合Fragment

                      5、Implementing ActionBarSherlock Fragment Tabs in Android

                      6、基于Fragment实现Tab的切换,滑出侧边栏

六、注意事项

        1、如果你想在Fragment 里面创建menu,则必须在onCreate的时候设置让它可以存在optionMenu才可以创建,代码为:

 

  1. public static class DetailsFragment extends Fragment {  
  2.      
  3.              @Override  
  4.   
  5.              public void onCreate(Bundle savedInstanceState) {  
  6.   
  7.                  // TODO Auto-generated method stub  
  8.   
  9.                  super.onCreate(savedInstanceState);  
  10.   
  11.                  setHasOptionsMenu(true);  
  12.   
  13.              }  
  14.   
  15.      }  
public static class DetailsFragment extends Fragment {
   
             @Override

             public void onCreate(Bundle savedInstanceState) {

                 // TODO Auto-generated method stub

                 super.onCreate(savedInstanceState);

                 setHasOptionsMenu(true);

             }

     }

          2、如何解决模块之间的通讯的耦合问题

                  1.使用接口,让Activity扮演管理角色,负责分发消息到该窗口的子View

        该方案的缺点

  • 不方便使用单元测试
  • 随着应用功能的增加,需要监听的事件越来越多,导致越来越多的接口声明以及绑定

                  2.使用LocalBroadcastManager + IntentFilter解决不同组件通讯,Intent负责搭载数据
          该方案的缺点

  • 不方便单元测试,需要实例化Intent,填装Intent的数据,实现Broadcast receivers以及再次提取Intent中的数据
  • receiver中不可做耗时操作,因为reciver是限时进程,10秒后会被系统kill掉,如果需要做耗时操作,需另外启Service来完成

                  3.EventBus

  • 消息订阅者:Activity or Fragment等订阅类注册自己到EventBus中
  • 消息发布者:只负责发布消息以及消息包装数据到EventBus
  • 回调基于命名约定以及消息包装对象
  • 方便的单元测试

         3、让多个Fragment 切换时不重新实例化

 

七、总结归纳

                       Fragment是3.0之后一个很重要的API,要深刻理解和掌握。

八、Fragment开源项目

       1、https://github.com/johnkil/Android-ProgressFragment

       2、Implementing Fragment Tabs in Android

八、Demo下载

                        例子1下载地址:

                        例子2下载地址:

九、推荐博客:http://www.vogella.com/articles/AndroidFragments/article.html

posted @ 2016-12-08 17:32  希小风  阅读(470)  评论(0编辑  收藏  举报