有過一些面試經驗的人基本都深有體會,每次面試通常都會問到Fragment的知識,因此,今天我就單獨把Fragment拿出來與你們分享一下.html
會涉及到Fragment如何產生,什麼是Fragment,Fragment生命週期,如何靜態和動態使用Fragment,Fragment回 退棧,Fragment事務,以及Fragment的一些特殊用途,例如:沒有佈局的Fragment有何用處?Fragment如何與Activiy交 互?Fragment如何建立對話框?Fragment如何與ActionBar集成等等...java
一、Fragment的產生與介紹android
Android運行在各類各樣的設備中,有小屏幕的手機,超大屏的平板甚至電視。針對屏幕尺寸的差距, 不少狀況下,都是先針對手機開發一套app,而後拷貝一份,修改佈局以適應什麼超級大屏的。難道沒法作到一個app能夠同時適應手機和平板嗎?答案是,當 然有,那就是Fragment.Fragment出現的初衷就是爲了解決這樣的問題。面試
你能夠把Fragment當成Activity一個界面的一部分,甚至Activity的界面由徹底不一樣的Fragment組成,更帥氣的是 Fragment有本身的聲明週期和接收、處理用戶的事件,這樣就沒必要要在一個Activity裏面寫一堆事件、控件的代碼了。更爲重要的是,你能夠動態 的添加、替換、移除某個Fragment。微信
二、Fragment的生命週期app
Fragment必須是依存於Activity而存在的,所以Activity的生命週期會直接影響到Fragment的生命週期。官網這張圖很好的說明了倆者的關係:ide
能夠看到Fragment比Activity多了幾個額外的生命週期回調函數:函數
onAttach(Activity); //當Activity與Fragment發生關聯時調用佈局
onCreateView(LayoutInflater,ViewGroup,Bundle); //建立該Fragment的視圖this
onActivityCreate(bundle); //當Activity的onCreate();方法返回時調用
onDestoryView(); //與onCreateView相對應,當改Fragment被移除時調用
onDetach(); //與onAttach()相對應,當Fragment與Activity的關聯被取消時調用
注意:除了onCreateView,其餘的全部方法若是你重寫了,必須調用父類對於該方法的實現。
三、靜態的使用Fragment
接下來,就是實踐的時候了,要注意了,開始寫代碼嘍~~~~
這是使用Fragment最簡單的一種方式,把Fragment當成普通的控件,直接寫在Activity的佈局文件中,用佈局文件調用Fragment。
步驟:
一、繼承Fragment,重寫onCreateView決定Fragment佈局。
二、在Activity中聲明此Fragment,就當和普通的View同樣。
下面展現一個例子(我使用倆個Fragment做爲Activity的佈局,一個Fragment用於標題佈局,一個Fragment用於內容佈局)。
TitleFragment的佈局文件,在這裏咱們能夠看出,咱們能夠每一個Fragment當中進行單獨的佈局:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="45dp" android:background="@drawable/title_bar" > <ImageButton android:id="@+id/id_title_left_btn" android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_centerVertical="true" android:background="@drawable/showleft_selector" /> <TextView android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:text="我不是微信" android:textColor="#fff" android:textSize="20sp" android:textStyle="bold" /> </RelativeLayout>
TitleFragment.java文件,在這裏咱們可以看到,能夠在各個Fragment當中進行獨立的初始化空間而且處理按鈕之類的事件,減輕了Activity的負擔,咱們在Activity中就沒有必要寫一大推初始化控件和事件響應的代碼了,這樣就使咱們的代碼看上去更加的簡潔了,可讀性大大提升了。
public class TitleFragment extends Fragment { private ImageButton mButton; @SuppressLint("NewApi") @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.title_fragment, container, false); mButton = (ImageButton)view.findViewById(R.id.id_title_left_btn); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getActivity(), "i am an ImageButton in TitleFragment ! ", Toast.LENGTH_SHORT).show(); } }); return view; } }
同理還有ContentFragment的佈局文件content_fragment.xml
<?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:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:text="使用Fragment作主面板" android:textSize="20sp" android:textStyle="bold" /> </LinearLayout>
同理還有ContentFragment.java文件
public class ContentFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.content_fragment, container,false); } }
下面就是主Activity以及他的佈局文件
MainActivity.java文件
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); } }
activity_main.xml文件,在這裏咱們能夠看到,咱們把Fragment就當作普通的控件同樣,在xml文件中使用。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.staticfragment.MainActivity" > <fragment android:name="com.example.staticfragment.TitleFragment" android:id="@+id/title" android:layout_height="45dp" android:layout_width="match_parent"/> <fragment android:layout_below="@id/title" android:name="com.example.staticfragment.ContentFragment" android:id="@+id/content" android:layout_height="fill_parent" android:layout_width="fill_parent"/> </RelativeLayout>
運行效果截圖以下:
注:因爲時間緣由,就沒有上傳源碼,若是須要demo源碼的話,能夠留言,我單獨發你一下...
四、動態的使用Fragment
上面已經演示了最簡單的使用Fragment的方式,下面分享一下如何動態的添加、更新、以及刪除Fragment。
首先是,MainActivity的佈局文件activity_main.xml,該文件佈局文件上面的頂部是一個TitleFragment,是一個靜態聲明的Fragment。
中間也是一個Fragment,可是這個Fragment是動態使用的。
最下面是四個按鈕。用include標籤包含外部的佈局文件進來的。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <fragment android:id="@+id/id_fragment_title" android:name="com.example.dynamicfragment.TitleFragment" android:layout_width="fill_parent" android:layout_height="45dp" /> <include android:id="@+id/id_ly_bottombar" android:layout_width="fill_parent" android:layout_height="55dp" android:layout_alignParentBottom="true" layout="@layout/bottombar" /> <FrameLayout android:id="@+id/id_content" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@id/id_ly_bottombar" android:layout_below="@id/id_fragment_title" /> </RelativeLayout>
而後是,MainActivity.java文件。也是咱們這個demo當中最重要的代碼文件,首先是將上面的佈局文件經過setContentView()加載進來.而後是經過setDefaultFragment();將默認的ContentFragment動態的加載進來。接下來就是經過咱們在最下面防止的四個按鈕能夠隨意的動態切換Fragment。這也是爲何Fragment會有如此火的緣由吧~~~^^
public class MainActivity extends ActionBarActivity implements OnClickListener { private ImageButton mTabWeixin; private ImageButton mTabFriend; private ImageButton mTabDiscover; private ImageButton mTabMe; private ContentFragment mWeiXinFragment; private FriendFragment mFriendFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); initView(); } public void initView() { // 初始化控件和聲明事件 mTabWeixin = (ImageButton) findViewById(R.id.weixin); mTabFriend = (ImageButton) findViewById(R.id.friend); mTabWeixin.setOnClickListener(this); mTabFriend.setOnClickListener(this); // 設置默認的Fragment setDefaultFragment(); } @SuppressLint("NewApi") private void setDefaultFragment() { FragmentManager manager = getFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); mWeiXinFragment = new ContentFragment(); transaction.replace(R.id.id_content, mWeiXinFragment); transaction.commit(); } @SuppressLint("NewApi") @Override public void onClick(View v) { FragmentManager fm = getFragmentManager(); // 開啓Fragment事務 FragmentTransaction transaction = fm.beginTransaction(); switch (v.getId()) { case R.id.weixin: if (mWeiXinFragment == null) { mWeiXinFragment = new ContentFragment(); } // 使用當前Fragment的佈局替代id_content的控件 transaction.replace(R.id.id_content, mWeiXinFragment); break; case R.id.friend: if (mFriendFragment == null) { mFriendFragment = new FriendFragment(); } transaction.replace(R.id.id_content, mFriendFragment); break; } // transaction.addToBackStack(); // 事務提交 transaction.commit(); } }
從上面的代碼,咱們能夠看出,咱們可使用FragmentManager對Fragment進行動態的加載,這裏使用的replace方法~~~下一節咱們會詳細的介紹FragmentManager的經常使用API。。。。^^
注:若是使用android3.0一下的版本,須要引入v4的包,而後Activity繼承FragmentActivity,而後經過getSupportFragmentManager()得到FragmentManager對象,不過仍是建議把Menifest文件的uses-sdk的minSdkVersion和targetSdkVersion都改成11以上,這樣就沒必要引入v4的包了。
代碼的中間有倆個動態加載進來的Fragment,這個和靜態使用ragment的聲明方式是同樣的,寫一個繼承Fragment的類,而後設置相應的佈局,因爲時間的關係,我這裏只寫了倆個Fragment,如今把這倆個的代碼頁貼出來:
第一個Fragment和他相應的佈局文件:
public class ContentFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_content, container, false); } }
<?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:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:text="weixin" android:textSize="20sp" android:textStyle="bold" /> </LinearLayout>
第二個Fragment和他相應的佈局文件:
public class FriendFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_friend, container, false); } }
<?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:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:text="friend" android:textSize="20sp" android:textStyle="bold" /> </LinearLayout>
好了,如今基本的代碼都有了,咱們把demo的運行圖貼出來給你們分享一下(注:時間緣由,沒注意佈局以及圖片的美化,只是功能的實現),這是分別點擊下面第一個和第二個按鈕的效果圖,從而實現了中間用一個Fragment動態的加載這倆個Fragment的顯示。
ps:爲了代碼的簡潔,就不添加按鈕的點擊變化什麼的了,主要講解功能了~~~
五、Fragment經常使用的API:
Fragment經常使用的三個類:
android.app.Fragment 主要用於定義Fragment
android.app.FragmentManager 主要用於在Activity中操做Fragment
android.app.FragmentTransaction 保證一些列Fragment操做的原子性,熟悉事務這個詞,必定能明白~
a、獲取FragmentManage的方式:
getFragmentManager() // v4中,getSupportFragmentManager
b、主要的操做都是FragmentTransaction的方法
FragmentTransaction transaction = fm.benginTransatcion();//開啓一個事務
transaction.add()
往Activity中添加一個Fragment
transaction.remove()
從Activity中移除一個Fragment,若是被移除的Fragment沒有添加到回退棧(回退棧後面會詳細說),這個Fragment實例將會被銷燬。
transaction.replace()
使用另外一個Fragment替換當前的,實際上就是remove()而後add()的合體~
transaction.hide()
隱藏當前的Fragment,僅僅是設爲不可見,並不會銷燬
transaction.show()
顯示以前隱藏的Fragment
detach()
將此Fragment從Activity中分離,會銷燬其佈局,但不會銷燬該實例
attach()
將從Activity中分離的Fragment,從新關聯到該Activity,從新建立其視圖層次
transatcion.commit()//提交一個事務
注意:經常使用Fragment的哥們,可能會常常遇到這樣Activity狀態不一致:State loss這樣的錯誤。主要是由於:commit方法必定要在Activity.onSaveInstance()以前調用。
上述,基本是操做Fragment的全部的方式了,在一個事務開啓到提交能夠進行多個的添加、移除、替換等操做。
值得注意的是:若是你喜歡使用Fragment,必定要清楚這些方法,哪一個會銷燬視圖,哪一個會銷燬實例,哪一個僅僅只是隱藏,這樣才能更好的使用它們。
a、好比:我在FragmentA中的EditText填了一些數據,當切換到FragmentB時,若是但願會到A還能看到數據,則適合你的就是hide和show;也就是說,但願保留用戶操做的面板,你可使用hide和show,固然了不要使勁在那new實例,進行下非null判斷。
b、再好比:我不但願保留用戶操做,你可使用remove(),而後add();或者使用replace()這個和remove,add是相同的效果。
c、remove和detach有一點細微的區別,在不考慮回退棧的狀況下,remove會銷燬整個Fragment實例,而detach則只是銷燬其視圖結構,實例並不會被銷燬。那麼兩者怎麼取捨使用呢?若是你的當前Activity一直存在,那麼在不但願保留用戶操做的時候,你能夠優先使用detach。