由於 Fragment 確實是一個大坑,所以在學習的過程中,記錄一下 Fragment 的使用,以及摳一些細節的東西來強化自己。
1 - 兼顧平板與手機的編程。
推薦郭霖大神的博文:Android手機平板兩不誤,使用Fragment實現兼容手機和平板的程序
簡單講就是平板的屏幕相對手機來說是比較大的,在手機上很好看的頁面也許到了平板就會因爲過度拉伸而變得很奇怪,這時我們的想法就是將佈局改變,可以是將頁面擴展一下,可以將原本兩頁的內容合併到一起,這就是博文展示一種解決思路。
2 - 需要對某個頁面進行反覆替換——動態添加碎片
2.1 第一種方案:使用replace()
2.1.1 具體操作
- 主佈局中嵌入 FrameLayout 作爲容器,用於將承載 Fragment 。
- 使用 FragmentManager 開啓事務。
- 使用事務的
replace()
方法替換 FrameLayout 。 - 提交事務。
2.1.2 優、缺點
replace()
被替換的 Fragment 將會被銷燬,執行 onDestroy()
,故而內存開銷會減少。相對應的如果頻繁切換需要重複構建 Fragment 實例。
2.1.3 應用示例
將替換流程封裝成一個方法,調用其替換指定的 FrameLayout。
- activity_main.xml 添加容器 FrameLayout
<!-- 省略 -->
<FrameLayout
android:id="@+id/frame_layout"
...>
<!-- 省略 -->
- MainActivity.java
...
private Fragment currentFragment = null; //記錄當前Fragment
...
/*
* 調用該方法替換當前Fragment
* @param fragment 替換的Fragment
*/
public void replaceFragment(Fragment fragment){
if( (currentFragment!=null) && (fragment == currentFragment) )
return;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transcation.replace(R.id.frame_layout, fragment); //替換容器中的Fragment
transcation.commit(); //提交事務
currentFragment = fragment; //更換當前Fragment標記
}
...
2.2 第二種方案(常用方案):使用add()
、 hide()
、show()
2.2.1 具體流程
- 主佈局中嵌入 FrameLayout 作爲容器,用於將承載 Fragment 。
- 使用 FragmentManager 開啓事務。
- 使用
add()
向指定容器添加新的Fragment。 - 隱藏其他Fragment。
- 提交事務。
2.2.2 注意事項
- 不能重複
add()
同一個 Fragment 實例,否則會報錯。 - Fragment 不會被銷燬,會一直保存在內存中,相互之間的切換使用
hide()
、show()
不會調用到 Fragment 生命週期的方法。 - 因爲我們一般會選擇FrameLayout 作爲容器,所以新添加的 Fragment 會在上層。默認所有的 Fragment 都是可見的,所以會配合
hide()
來隱藏其他 Fragment。
2.2.3 優、缺點
不會回收 Fragment ,適用於頻繁切換頁面的場景。
2.2.4 代碼
xml佈局和第一種方案一樣,只修改我們的 MainActivity.java
...
private Fragment currentFragment = null; //記錄當前顯示的Fragment
...
public void replaceFragment(Fragment fragment){
if( (currentFragment!=null) && (fragment == currentFragment) ) //該頁面已經顯示,直接return
return;
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
if(fragment.isAdded()){ //已經添加就只需要show
transaction.show(fragment);
}else{
transaction.add(R.id.frame_layout,fragment); //沒有添加就需要add
}
if(currentFragment!=null)
transaction.hide(currentFragment); //隱藏之前的 Fragment
transcation.commit(); //提交事務
currentFragment = fragment; //更換當前Fragment標記
}
2.2.5 補充
由於 Activity 有可能被意外重啓,如手機內存不足,不可見的 Activity 有可能會被暫時銷燬,再打開時,Fragment 將重新構建實例並添加到容器內,這樣會造成內存泄漏。相關資料參考 Fragment 常用寫法 ,裏面提到 MainActivity的非正常退出的重啓會造成 Fragment 重複添加到容器的漏洞以及如何解決的辦法。
我這裏的解決寫法如下,使用 tag
標誌,保證了始終只有 tag
關聯的頁面在容器裏,即使意外重啓也只能使用 tag
關聯的 Fragment,不怕重複添加 :
...
private Fragment currentFragment = null;
...
public void replaceFragment(Fragment fragment, String tag){
if( (currentFragment!=null) && (fragment == currentFragment) )
return;
FragmentManager manager = getSupportFragmentManager();
if(manager.findFragmentByTag(tag)!=null){
fragment = manager.findFragmentByTag(tag); //通過tag獲取實例
}
FragmentTransaction transaction = manager.beginTransaction();
if(fragment.isAdded()){
transaction.show(fragment);
}else{
transaction.add(R.id.frame_layout, fragment, tag); //第三個參數就是標誌
}
if(currentFragment!=null)
transaction.hide(currentFragment);
transaction.commit();
currentFragment = fragment;
}
最後強調:Fragment 一般只會有一個實例,爲了開發得順心,大家千萬不要偷懶,不然出現 bug 難受的是自己哦。即使重用也要考慮仔細,能不能只用一個 Fragment 的實例完成重用。
3 - 結合 ViewPager 作導航欄,實現導航切換功能
具體可以參考我的文章:底部導航欄的實現
結合 ViewPager 之後我們就要額外關注 Fragment 的生命週期了。
正文結束,歡迎留言。