1、什么是Fragmenthtml
Android在3.0中引入了fragments的概念,主要目的是用在大屏幕设备上--例如平板电脑上,支持更加动态和灵活的UI设计。平板电脑的屏幕要比手机的大得多,有更多的空间来放更多的UI组件,而且这些组件之间会产生更多的交互。Fragment容许这样的一种设计,而不须要你亲自来管理 viewhierarchy的复杂变化。 经过将activity的布局分散到fragment中, 你能够在运行时修改activity的外观,并在由activity管理的back stack中保存那些变化。当一个片断指定了自身的布局时,它能和其余片断配置成不一样的组合,在活动中为不一样的屏幕尺寸修改布局配置(小屏幕可能每次显示一个片断,而大屏幕则能够显示两个或更多)。java
2、谁在使用Fragmentandroid
3、Fragment的优势和缺点git
与其说是Fragment的缺点,不如说是每一个应用程序模块之间的通信都面临地耦合问题github
4、如何使用Fragmentsql
若是你的应用版本在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。app
你能够把fragment看做是activity的模块化组件,它拥有本身的生命周期,框架
接受它本身的输入事件,你也能够在运行activity的时候添加或者移除它(有点像能够在不一样的activity中重用的“子activity”)。这节课演示怎么样使用Support Library继承Fragment类,如此你的app(应用)就能与运行android1.6老版本的系统设备兼容 。注意:若是你由于一些其余缘由决定你的app须要的最低API版本为11或者更高,那你就不必使用Support Library,而能够直接使用框架内置的Fragment类和相关API。咱们分别使用来自Support Library的API和内置在平台(API),使用特定包名下的API跟内置在平台(API)的仍是略有不一样。ide
使用Fragment有两种方式:一是像在布局文件添加一个控件同样,在xml里面声明一个Fragment就行;例以下面这样:模块化
<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里面的事务。
//获得一个fragment 事务(相似sqlite的操做) FragmentTransaction ft = getFragmentManager() .beginTransaction(); ft.replace(R.id.details, details);//将获得的fragment 替换当前的viewGroup内容,add则不替换会依次累加 ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);//设置动画效果 ft.commit();//提交
一、先用第一中方式接受如何使用Fragment。
要建立一个fragment须要继承Fragment类,而后重写关键的生命周期方法插入你本身的应用逻辑,操做的方式跟建立一个Activity相似。
不一样的是当你建立一个Fragment时,你必须使用onCreateView()回调去定义你的布局。事实上,这是惟一的须要让fragment得到运行的回调函数。例如,这里有一个简单的指定了本身布局的fragment:
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:
<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中怎样运用这个布局:
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类:
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对应的布局文件:
<?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类:
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对应的布局文件:
<?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的布局文件:
<?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参数就是这时候传递过去的。显示结果:
打开程序时生命周期显示:
按返回键时生命周期显示:
二、动态使用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:
<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:
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替换另外一个的例子:
// 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的交互例子:
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。
@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就实现了上一例子中的接口:
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:
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(); } } }
5、应用案例
一、Fragment+FragmentTabHost实现仿新浪微博底部菜单栏:TabHost其实已经被弃用了,这个例子就是用来体现一下Fragment的用法,很简单在切换TabHost的时候加载Fragment。我的以为意义不大,还不如那种直接加载Activity,里面再嵌套Fragment来的实在。
二、经过SlidingMenu+Fragment实现当前最流行的侧滑:
三、 基于Android Fragment功能的例子:这个例子Fragment之间的交互没有使用接口的形式,而是直接调用,这样作比较方便,可是不规范。
四、Android开发 侧边滑动菜单栏SlidingMenu结合Fragment
五、Implementing ActionBarSherlock Fragment Tabs in Android
6、注意事项
一、若是你想在Fragment 里面建立menu,则必须在onCreate的时候设置让它能够存在optionMenu才能够建立,代码为:
public static class DetailsFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setHasOptionsMenu(true); } }
二、如何解决模块之间的通信的耦合问题
1.使用接口,让Activity扮演管理角色,负责分发消息到该窗口的子View
该方案的缺点
2.使用LocalBroadcastManager + IntentFilter解决不一样组件通信,Intent负责搭载数据
该方案的缺点
3.EventBus
7、总结概括
Fragment是3.0以后一个很重要的API,要深入理解和掌握。
8、Fragment开源项目
一、https://github.com/johnkil/Android-ProgressFragment
二、Implementing Fragment Tabs in Android
8、Demo下载
9、推荐博客:http://www.vogella.com/articles/AndroidFragments/article.html