Fragment API文檔
(需要翻墻)
本文內容
- Fragment生命周期
- Fragment創建步驟
- Fragment靜態加載
- Fragment動態加載
1. Fragment生命周期
(下圖為Steve Pomeroy制作的完整的Fragment生命周期圖)
注意點:
Fragment以XML的方式靜態加載時,最先會調用onInflate的方法(調用時機:Fragment所關聯的Activity在執行setContentView時)。
-
onInflate有兩個重載的方法:
onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState)(推薦使用)
onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState)(已廢棄,不推薦被外部類調用)
-
onAttach也有兩個重載的方法:
onAttach(Context context)(推薦使用)
onAttach(Activity activity)(已廢棄,不推薦被外部類調用)
2. Fragment的創建步驟(最簡單的方式)
1.創建XML視圖(供Fragment進行管理)
2.創建Fragment
通過這兩步我們的Fragment已經創建完畢了,接下來就是使用了。
3. Fragment靜態加載
靜態加載是指以XML的方式進行加載。(Activity和對應布局,如下圖所示)
在fragment標簽中需要添加name屬性來指定你想添加的Fragment。
接下來我們跑一下程序,界面就加載出來了。
接下來,給各位踩一下“靜態加載Fragment”可能出現的坑。
我們的fragment在xml中是這樣寫的,運行沒有問題。
<fragment
android:id="@+id/content_fragment"
android:name="com.sina.example.fragmentdemo.fragment.ContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
但如果android:id這個屬性忘記寫的話就要遭殃了(事例如下)...
<fragment
android:name="com.sina.example.fragmentdemo.fragment.ContentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
我們去掉android:id屬性跑下程序,結果程序崩了...,檢查崩潰日志后得到了兩條關鍵日志信息:
......
Caused by: android.view.InflateException: Binary XML file line #6: Error inflating class fragment
......
Caused by: java.lang.IllegalArgumentException: Binary XML file line #6: Must specify unique android:id, android:tag, or have a parent with an id for com.sina.example.fragmentdemo.fragment.ContentFragment
第1條信息:告訴我們在Fragment填充布局的時候出錯了。
第2條信息:告訴我們解決這個問題的辦法:必須指定android:id或android:tag或給我們的fragment的父容器一個id(解決辦法如下圖所示)。
緊接著,我們用Hierarchy Viewer分析一下圖層,說明一種現象。
(1). 打開視圖后,我們最先看到的是視圖樹的根View - DecorView(PhoneWindow的內部類)
(2). 順著視圖樹向后看,我們看到一個id/content的視圖容器.
我們在Activity中setContentView就是向這個視圖容器(android.R.id.content)中添加布局的。
下方分支是ActionBar的相關布局,如果將標題欄去掉,你會發現下方的分支消失了。
(3). 最后,可以看到我們自己寫的布局文件
id/rl_main:是我們賦給Acitivity布局文件根布局的id值。
id/content_fragment:是我們賦給fragment的id值(但視圖中卻變成了RelativeLayout)
AppCompatImageView:我們的ImageView,未賦Id值。
細心觀察,我們會發現Fragment的id值賦給的了它所管理視圖的最外層布局(RelativeLayout)。經過測試,發現不止id值,其他屬性也賦值給了最外層布局(賦值是指對RelativeLayout沒有的屬性進行添加,已有的屬性進行刷新)。
結論:靜態加載Fragment時,Fragment在XML設置的屬性將賦值給它所管理視圖的最外層布局。
最后,看下如何在Activity中找到我們的Fragment。
FragmentManager里有提供了“兩種方法”查找我們的Fragment:
findFragmentById(@IdRes int id) : 通過id查找Fragment。(查找過程:第一次查找會從FragmentManager所管理的所有Fragment里查找,如果找不到,則會從關聯過這個id的回退棧的所有Fragment里查找。如過找到則返回這個指定id的Fragment,否則返回null。)
findFragmentByTag(String tag):通過Tag查找Fragment。(查找過程:類比id的查找過程)
看下上一步,我們Fragment的id值已經賦給了它所管理視圖的最外層布局了,我們試下還能不能找到這個Fragment。
我們通過查找id的方式找到了我們的Fragment(ContentFragment)
-
同一個id(R.id.content_fragment)以不同的方式查找得到了兩個不同的對象
- findViewById的方式我們得到的是Fragment的視圖容器RelativeLayout。
- findFragmentById的方式我們得到的是Fragment的實例ContentFragment。
4. Fragment動態加載
步驟:
- 在Activity的XML布局文件中添加一個FrameLayout視圖容器(用于存放Fragment被添加后Fragment所管理的視圖)。
- 在Activity中調用一個鏈式方法完成Fragment的動態添加。
- getSupportFragmentManager() : 獲取Fragment管理器(需要support.v4包)
- beginTransaction() : 使Fragment管理器開啟一個事務
- add(R.id.fl-main, new ContentFragment(), null) : 在事務中進行一個添加操作,將目標Fragment添加到我們在步驟1中提供好的視圖容器(R.id.fl_main)中 ,該Fragment的tag設為null。
- commit() : 提交事務到主線程執行添加操作。
PS:使用getSupportFragmentManager() 需要導入support.v4包,用來對Android3.0以下的版本進行兼容,不需要向下兼容可以考慮用 getFragmentManager()
最后,跑一下程序,我們的界面就顯示出來了。
接著,我們用Hierarchy Viewer查看一下圖層,看下是不是我們所想象的。
果然,Fragment中的布局完全裝進了fl-main這個視圖容器里,說明實例也已經被添加了。
這里僅僅是介紹Fragment最簡單的動態加載,后面小編會在FragmentTransaction里進行更詳細的介紹。