Fragment初识
概述
官方API
Fragment是什么
Android 在 Android 3.0(API 11 級)中引入了Fragment,主要是為了給大屏幕(如平板電腦)上更加動(dòng)態(tài)和靈活的 UI 設(shè)計(jì)提供支持。由于平板電腦的屏幕比手機(jī)屏幕大得多,因此可用于組合和交換 UI 組件的空間更大。利用片段實(shí)現(xiàn)此類設(shè)計(jì)時(shí),您無需管理對視圖層次結(jié)構(gòu)的復(fù)雜更改。 通過將 Activity 布局分成片段,您可以在運(yùn)行時(shí)修改 Activity 的外觀,并在由 Activity 管理的返回棧中保留這些更改。
當(dāng)然了我們普通手機(jī)開發(fā)也會加入這個(gè)Fragment, 我們可以把它看成一個(gè)小型的Activity,又稱Activity片段!
例如:新聞應(yīng)用可以使用一個(gè)片段在左側(cè)顯示文章列表,使用另一個(gè)片段在右側(cè)顯示文章—兩個(gè)片段并排顯示在一個(gè) Activity 中,每個(gè)片段都具有自己的一套生命周期回調(diào)方法,并各自處理自己的用戶輸入事件。 因此,用戶不需要使用一個(gè) Activity 來選擇文章,然后使用另一個(gè) Activity 來閱讀文章,而是可以在同一個(gè) Activity 內(nèi)選擇文章并進(jìn)行閱讀,如下圖中的左側(cè)平板電腦布局所示。
我們應(yīng)該將每個(gè)片段都設(shè)計(jì)為可重復(fù)使用的模塊化 Activity 組件。也就是說,由于每個(gè)片段都會通過各自的生命周期回調(diào)來定義其自己的布局和行為,您可以將一個(gè)片段加入多個(gè) Activity,因此,您應(yīng)該采用可復(fù)用式設(shè)計(jì),避免直接從某個(gè)片段直接操縱另一個(gè)片段。 這特別重要,因?yàn)槟K化片段讓您可以通過更改片段的組合方式來適應(yīng)不同的屏幕尺寸。 在設(shè)計(jì)可同時(shí)支持平板電腦和手機(jī)的應(yīng)用時(shí),您可以在不同的布局配置中重復(fù)使用您的片段,以根據(jù)可用的屏幕空間優(yōu)化用戶體驗(yàn)。 例如,在手機(jī)上,如果不能在同一 Activity 內(nèi)儲存多個(gè)片段,可能必須利用單獨(dú)片段來實(shí)現(xiàn)單窗格 UI。
下圖是文檔中給出的一個(gè)Fragment分別對應(yīng)手機(jī)與平板間不同情況的處理圖:
例如:仍然以新聞應(yīng)用為例—在平板電腦尺寸的設(shè)備上運(yùn)行時(shí),該應(yīng)用可以在Activity A 中嵌入兩個(gè)片段。不過,在手機(jī)尺寸的屏幕上,沒有足以儲存兩個(gè)片段的空間,因此Activity A 只包括用于顯示文章列表的片段,當(dāng)用戶選擇文章時(shí),它會啟動(dòng)Activity B,其中包括用于閱讀文章的第二個(gè)片段。因此,應(yīng)用可通過重復(fù)使用不同組合的片段來同時(shí)支持平板電腦和手機(jī),如上圖右側(cè)。
如需了解有關(guān)通過利用不同片段組合來適應(yīng)不同屏幕配置這種方法設(shè)計(jì)應(yīng)用的詳細(xì)信息,請參閱支持平板電腦和手機(jī)指南。
Fragment的生命周期圖
- ①Activity加載Fragment的時(shí)候,依次調(diào)用下面的方法: onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart ->onResume
- ②當(dāng)我們弄出一個(gè)懸浮的對話框風(fēng)格的Activity,或者其他,就是讓Fragment所在的Activity可見,但不獲得焦點(diǎn) onPause
- ③當(dāng)對話框關(guān)閉,Activity又獲得了焦點(diǎn): onResume
- ④當(dāng)我們替換Fragment,并調(diào)用addToBackStack()將他添加到Back棧中 onPause -> onStop -> onDestoryView !!注意,此時(shí)的Fragment還沒有被銷毀哦!!!
- ⑤當(dāng)我們按下鍵盤的回退鍵,Fragment會再次顯示出來: onCreateView -> onActivityCreated -> onStart -> onResume
- ⑥如果我們替換后,在事務(wù)commit之前沒有調(diào)用addToBackStack()方法將 Fragment添加到back棧中的話;又或者退出了Activity的話,那么Fragment將會被完全結(jié)束, Fragment會進(jìn)入銷毀狀態(tài) onPause -> onStop -> onDestoryView -> onDestory -> onDetach
核心要點(diǎn)
- 3.0版本后引入,即minSdk要大于11,使用兼容包v4,可以向下兼容
- Fragment需要嵌套在Activity中使用,當(dāng)然也可以嵌套到另外一個(gè)Fragment中,但這個(gè)被嵌套 的Fragment也是需要嵌套在Activity中的,間接地說,Fragment還是需要嵌套在Activity中!! 受寄主Activity的生命周期影響,當(dāng)然他也有自己的生命周期!另外不建議在Fragment里面 嵌套Fragment因?yàn)榍短自诶锩娴腇ragment生命周期不可控!!!
- 官方文檔說創(chuàng)建Fragment時(shí)至少需要實(shí)現(xiàn)三個(gè)方法:onCreate( ),onCreateView( ),OnPause( ); 不過貌似只寫一個(gè)onCreateView也是可以的…
- Fragment的生命周期和Activity有點(diǎn)類似:
三種狀態(tài):
Resumed:在允許中的Fragment可見
Paused:所在Activity可見,但是得不到焦點(diǎn)
Stoped: ①調(diào)用addToBackStack(),Fragment被添加到Bcak棧 ②該Activity轉(zhuǎn)向后臺,或者該Fragment被替換/刪除
ps:停止?fàn)顟B(tài)的fragment仍然活著(所有狀態(tài)和成員信息被系統(tǒng)保持著),然而,它對用戶 不再可見,并且如果activity被干掉,他也會被干掉.
Fragment的幾個(gè)子類
很多時(shí)候我們都是直接重寫Fragment,inflate加載布局完成相應(yīng)業(yè)務(wù)了,子類用的不多,等需要的 時(shí)候在深入研究!
- 對話框:DialogFragment
- 列表:ListFragment
- 選項(xiàng)設(shè)置:PreferenceFragment
- WebView界面:WebViewFragment
是用App包下的Fragment還是v4包下的
問題概述:
再引入Fragment聲明時(shí),
我們到底是使用android.app下的Fragment還是用的android.support.v4.app包下 的Fragment呢?
其實(shí)都可以,前面說過Fragment是Android 3.0(API 11)后引入的,那么如果開發(fā)的app需要 在3.0以下的版本運(yùn)行呢?比如還有一點(diǎn)點(diǎn)市場份額的2.3!于是乎,v4包就這樣應(yīng)運(yùn)而生了, 而最低可以兼容到1.6版本!至于使用哪個(gè)包看你的需求了,現(xiàn)在3.0下手機(jī)市場份額其實(shí)已經(jīng)不多了,隨街都是4.0以上的,7.0都出了,你說呢…所以這個(gè)時(shí)候,你可以直接使用app包下的Fragment 然后調(diào)用相關(guān)的方法,通常都是不會有什么問題的;如果你Fragment用了app包的, FragmentManager和FragmentTransaction都需要是app包的!要么用全部用app,要么全部用v4, 不然可是會報(bào)錯(cuò)的哦!當(dāng)然如果你要自己的app對于低版本的手機(jī)也兼容的話,那么就可以選擇用v4包!
使用v4包下Fragment要注意的地方:
- ①如果你使用了v4包下的Fragment,那么所在的那個(gè)Activity就要繼承FragmentActivity或者其子類如AppCompatActivity
案例:今天在xml文件中靜態(tài)地載入fragment,然后重寫了Fragment,但是在加載Activity的時(shí)候就報(bào)錯(cuò)了, 大概的提示就是Fragment錯(cuò)誤還是找不到什么的,name屬性改了幾次還是錯(cuò)!最后才發(fā)現(xiàn)是用了 v4的包的緣故,只需讓自己的Activity改成FragmentActivity即可! - 如果引用的是V4包中的類,getFragmentManager( )不能使用,需要改成getSupportFragmentManager( )
創(chuàng)建一個(gè)Fragment
靜態(tài)加載Fragment
操作步驟
- Step 1:定義Fragment的布局
- Step 2:自定義一個(gè)Fragment類,需要繼承Fragment或者他的子類,重寫onCreateView()方法 在該方法中調(diào)用:inflater.inflate()方法加載Fragment的布局文件,接著返回加載的view對象
- Step 3:在需要加載Fragment的Activity對應(yīng)的布局文件中添加fragment的標(biāo)簽, 記住,name屬性是全限定類名,就是要包含F(xiàn)ragment的包名,另外 fragment必須用id或tag作為唯一標(biāo)識
- Step 4: Activity在onCreate( )方法中調(diào)用setContentView()加載布局文件即可!
Code
Fragment的UI布局fragment_static_load.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical" android:layout_width="match_parent"android:layout_height="match_parent"><TextView android:id="@+id/textview"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="我是靜態(tài)加載的Fragment"/></LinearLayout>創(chuàng)建類FragmentOne.java,繼承Fragment或者其之類
package com.turing.base.activity.fragment;import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;import com.turing.base.R;public class FragmentOne extends Fragment {@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fragment_static_load,container,false);return view;} }在Activity的布局文件activity_fragment_static_load.xml中聲明fragment標(biāo)簽
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><fragment android:id="@+id/fragmentStaticLoad"android:name="com.turing.base.activity.fragment.FragmentOne"android:layout_width="wrap_content"android:layout_height="wrap_content" /></RelativeLayout>加載布局文件,操作UI
package com.turing.base.activity.fragment;import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.TextView;import com.turing.base.R;/*** 靜態(tài)加載Fragment的Activity*/ public class FragmentStaticLoadAct extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_fragment_static_load);//靜態(tài)加載時(shí)可以直接獲取到 Fragment中的UI控件TextView tv = (TextView) findViewById(R.id.textview);tv.setText("我在Act中獲取到了Fragment中的UI控件");} }效果圖
操作步驟
動(dòng)態(tài)加載Fragment
實(shí)現(xiàn)動(dòng)態(tài)加載,我們需要先了解Fragment事務(wù)。
Fragment事務(wù):對Fragment進(jìn)行添加、移除、替換或執(zhí)行其它動(dòng)作,提交給Activity的每一個(gè)變化。
Fragment是UI模塊,自然在一個(gè)Activity中可以不只有一個(gè)模塊,所以Android提供了FragmentManage類來管理Fragment,FragmentTransaction類來管理事務(wù)。
我們對Fragment的動(dòng)態(tài)加載就是先將添加、移除等操作提交到事務(wù),然后通過FragmentManage完成的。
通過FragmentManager.beginTransaction()我們可以開始一個(gè)事務(wù)。在事務(wù)中,我們可以對Fragment進(jìn)行的操作以及對應(yīng)的方法如下:
- 添加:add()
- 移除:remove()
- 替換:replace()
- 提交事務(wù):commit()
- 上面幾個(gè)是比較常用的,還有attach()、detach()、hide()、addToBackStack()等方法。
如果允許用戶通過back鍵退回到前一個(gè)Fragment狀態(tài),調(diào)用commit()之前可以加入addToBackStack()方法
我們需要注意的是,Fragment以ID或Tag作為唯一標(biāo)識,所以remove和replace的參數(shù)是Fragment,這個(gè)Fragment目標(biāo)Fragment一致
注意:Activity動(dòng)態(tài)的添加Fragment必需有一個(gè)容器View來容納Fragment的layout布局
操作步驟
Code
activity_fragment_dynamic_load
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><!--Activity動(dòng)態(tài)的添加Fragment必需有一個(gè)容器View來容納Fragment的layout布局--><LinearLayout android:id="@+id/fragmentDynamicLoad"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"/> </RelativeLayout>FragmentDynamicLoadAct.java
package com.turing.base.activity.fragment.dynamicload;import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.DisplayMetrics; import android.widget.TextView;import com.turing.base.R;public class FragmentDynamicLoadAct extends AppCompatActivity {// 屏幕寬高int width, height;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_fragment_dynamic_load);// 獲取屏幕的寬高DisplayMetrics displayMetrics = new DisplayMetrics();getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);width = displayMetrics.widthPixels;height = displayMetrics.heightPixels;// 根據(jù)寬高,加載不同的Fragmentif (width < height) {FragementFirst fragementFirst = new FragementFirst();// 使用V4的包就使用getSupportFragmentManager ,使用app下的,就使用getFragmentManagergetSupportFragmentManager().beginTransaction().add(R.id.fragmentDynamicLoad, fragementFirst).commit();} else {FragmentSecond fragmentSecond = new FragmentSecond();getSupportFragmentManager().beginTransaction().add(R.id.fragmentDynamicLoad, fragmentSecond).commit();}//當(dāng)fragment被提交之后,【fragmentTransaction.commit()提交fragment是異步處理的,所以獲取fragment時(shí)要注意】// 可通過以下兩種方法獲取fragment:findFragmentByTag()、findFragmentById()}/*** 重寫onStart()方法,* 因?yàn)閺膄ragment的生命周期可以知道當(dāng)Activity的onCreate(Bundle savedInstanceState)中* 還無法獲取fragment的布局的組件*/@Overrideprotected void onStart() {super.onStart();/*** 可以直接通過findViewById()獲取fragment的組件,* 因?yàn)閒ragment本身就是Activity的一部分(“碎片”/“片段”);* 因?yàn)锳ctivity和fragment要從fragment的onActivityCreate()生命周期方法之后* 才能相互獲取對方布局中的組件,* 所以在fragment中獲取Activity的組件最早只能在onActivityCreate()中獲取,* 而Activity最早只能在onStart()中獲取;*/// 獲取Fragment中的UI組件if (width < height) {TextView textView = (TextView) findViewById(R.id.fragmentFirst);textView.setText("~~~~~First");} else {TextView textView = (TextView) findViewById(R.id.fragmentSecond);textView.setText("~~~~~Second");}} }FragementFirst.java
package com.turing.base.activity.fragment.dynamicload;import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;import com.turing.base.R;/*** A simple {@link Fragment} subclass.*/ public class FragementFirst extends Fragment {public FragementFirst() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentreturn inflater.inflate(R.layout.fragment_fragement_first, container, false);}}fragment_fragement_first.xml
<FrameLayout 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.turing.base.activity.fragment.dynamicload.FragementFirst"><!-- TODO: Update blank fragment layout --><TextView android:id="@+id/fragmentFirst"android:layout_width="match_parent"android:layout_height="match_parent"android:text="fragment first" /></FrameLayout>FragmentSecond.java
package com.turing.base.activity.fragment.dynamicload;import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup;import com.turing.base.R;/*** A simple {@link Fragment} subclass.*/ public class FragmentSecond extends Fragment {public FragmentSecond() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentreturn inflater.inflate(R.layout.fragment_fragment_second, container, false);}}fragment_fragment_second.xml
<FrameLayout 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.turing.base.activity.fragment.dynamicload.FragmentSecond"><!-- TODO: Update blank fragment layout --><TextView android:id="@+id/fragmentSecond"android:layout_width="match_parent"android:layout_height="match_parent"android:text="fragment second" /></FrameLayout>效果圖
Fragment管理與Fragment事務(wù)
Fragment與Activity的交互
組件獲取
Activity中獲取Fragment,以及Fragment中的組件
獲取Fragment
當(dāng)fragment被提交之后,【fragmentTransaction.commit()提交fragment是異步處理的,所以獲取fragment時(shí)要注意】
可通過以下兩種方法獲取fragment:findFragmentByTag()、findFragmentById()
Fragment中的組件
/*** 重寫onStart()方法,* 因?yàn)閺膄ragment的生命周期可以知道當(dāng)Activity的onCreate(Bundle savedInstanceState)中* 還無法獲取fragment的布局的組件*/@Overrideprotected void onStart() {super.onStart();/*** 可以直接通過findViewById()獲取fragment的組件,* 因?yàn)閒ragment本身就是Activity的一部分(“碎片”/“片段”);* 因?yàn)锳ctivity和fragment要從fragment的onActivityCreate()生命周期方法之后* 才能相互獲取對方布局中的組件,* 所以在fragment中獲取Activity的組件最早只能在onActivityCreate()中獲取,* 而Activity最早只能在onStart()中獲取;*/// 獲取Fragment中的UI組件if (width < height) {TextView textView = (TextView) findViewById(R.id.fragmentFirst);textView.setText("~~~~~First");} else {TextView textView = (TextView) findViewById(R.id.fragmentSecond);textView.setText("~~~~~Second");}}Fragment中獲取Activity中的組件
public class FragementFirst extends Fragment {public FragementFirst() {// Required empty public constructor}@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {// Inflate the layout for this fragmentreturn inflater.inflate(R.layout.fragment_fragement_first, container, false);}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 在Fragment中獲取Activity的組件TextView textView = (TextView) getActivity().findViewById(R.id.id_tv_actUI);textView.setText("FFFF");} }數(shù)據(jù)傳遞
①Activit傳遞數(shù)據(jù)給Fragment:
在Activity中創(chuàng)建Bundle數(shù)據(jù)包,調(diào)用Fragment實(shí)例的setArguments(bundle) 從而將Bundle數(shù)據(jù)包傳給Fragment,然后Fragment中調(diào)用getArguments獲得 Bundle對象,然后進(jìn)行解析就可以了
fragementFirst = new FragementFirst();// 使用V4的包就使用getSupportFragmentManager ,如果使用app下的Fragment,就使用getFragmentManagergetSupportFragmentManager().beginTransaction().add(R.id.fragmentDynamicLoad, fragementFirst).commit();// Activity傳遞數(shù)據(jù)給FragmentBundle bundle = new Bundle();bundle.putString("key", "這是Activity傳遞給Fragment的數(shù)據(jù)");// setArgumentsfragementFirst.setArguments(bundle);在Fragment中接收解析數(shù)據(jù)
// 接收Activity傳遞過來的數(shù)據(jù)Bundle bundle = getArguments();Toast.makeText(getActivity(), bundle.getString("key"), Toast.LENGTH_SHORT).show();②Fragment傳遞數(shù)據(jù)給Activity:
在Fragment中定義一個(gè)內(nèi)部回調(diào)接口,再讓包含該Fragment的Activity實(shí)現(xiàn)該回調(diào)接口, Fragment就可以通過回調(diào)接口傳數(shù)據(jù)了。
Step 1:定義一個(gè)回調(diào)接口:(Fragment中)
FragementFirst.java
/*** 定義一個(gè)回調(diào)接口:(Fragment中)*/public interface FragmentCallBack {//定義一個(gè)接口方法void getResult(String result);}Step 2:接口回調(diào)(Fragment中)
FragementFirst.java
getData改方法持有接口對象
Step 3:使用接口回調(diào)方法讀數(shù)據(jù)(Activity中)
//使用接口回調(diào)方法讀數(shù)據(jù)(Activity中)fragementFirst.getData(new FragementFirst.FragmentCallBack() {@Overridepublic void getResult(String result) {Toast.makeText(FragmentDynamicLoadAct.this, result, Toast.LENGTH_SHORT).show();}});總結(jié)
->在Fragment定義一個(gè)接口,接口中定義抽象方法,你要傳什么類型的數(shù)據(jù)參數(shù)就設(shè)置為什么類型;
->接著還有寫一個(gè)調(diào)用接口中的抽象方法,把要傳遞的數(shù)據(jù)傳過去
->再接著就是Activity了,調(diào)用Fragment提供的那個(gè)方法,然后重寫抽象方法的時(shí)候進(jìn)行數(shù)據(jù) 的讀取就可以了~
運(yùn)行圖
③Fragment與Fragment之間的數(shù)據(jù)互傳
找到要接受數(shù)據(jù)的fragment對象,直接調(diào)用setArguments傳數(shù)據(jù)進(jìn)去就可以了
通常的話是replace時(shí),即fragment跳轉(zhuǎn)的時(shí)候傳數(shù)據(jù)的,那么只需要在初始化要跳轉(zhuǎn)的Fragment
后調(diào)用他的setArguments方法傳入數(shù)據(jù)即可!
如果是兩個(gè)Fragment需要即時(shí)傳數(shù)據(jù),而非跳轉(zhuǎn)的話,就需要先在Activity獲得f1傳過來的數(shù)據(jù),
再傳到f2了,就是以Activity為媒介~
還有一片簡書上的文章 可以學(xué)習(xí)下~
總結(jié)
以上是生活随笔為你收集整理的Fragment初识的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Intent传递数据全解
- 下一篇: interface declaratio