1. 程式人生 > >讓多個Fragment 切換時不重新例項化

讓多個Fragment 切換時不重新例項化

在專案中需要進行Fragment的切換,一直都是用replace()方法來替換Fragment:

1

2

3

4

5

6

7

8

9

public void switchContent(Fragment fragment) {

if(mContent != fragment) {

mContent = fragment;

mFragmentMan.beginTransaction()

.setCustomAnimations(android.R.anim.fade_in, R.anim.slide_out)

.replace(R.id.content_frame, fragment) // 替換Fragment,實現切換

.commit();

}

}


但是,這樣會有一個問題:
每次切換的時候,Fragment都會重新例項化,重新載入一邊資料,這樣非常消耗效能和使用者的資料流量。

就想,如何讓多個Fragment彼此切換時不重新例項化?

翻看了Android官方Doc,和一些元件的原始碼,發現,replace()這個方法只是在上一個Fragment不再需要時採用的簡便方法。

正確的切換方式是add(),切換時hide()add()另一個Fragment;再次切換時,只需hide()當前,show()另一個。
這樣就能做到多個Fragment切換不重新例項化:

1

2

3

4

5

6

7

8

9

10

11

12

public void switchContent(Fragment from, Fragment to) {

if (mContent != to) {

mContent = to;

FragmentTransaction transaction = mFragmentMan.beginTransaction().setCustomAnimations(

android.R.anim.fade_in, R.anim.slide_out);

if (!to.isAdded()) { // 先判斷是否被add過

transaction.hide(from).add(R.id.content_frame, to).commit(); // 隱藏當前的fragment,add下一個到Activity中

} else {

transaction.hide(from).show(to).commit(); // 隱藏當前的fragment,顯示下一個

}

}

}

————Edited 2015.2.7————-

問題一:儲存UI與資料的記憶體消耗

上面所述為避免重新例項化而帶來的“重新載入一邊資料”、“消耗資料流量”,其實是這個Fragment不夠“純粹”。

Fragment應該分為UI FragmentHeadless Fragment

前者是指一般的定義了UI的Fragment,後者則是無UI的Fragment,即在onCreateView()中返回的是null。將與UI處理無關的非同步任務都可以放到後者中,而且一般地都會在onCreate()中加上setRetainInstance(true),故而可以在橫豎屏切換時不被重新建立和重複執行非同步任務。

這樣做了之後,便可以不用管UI Fragment的重新建立與否了,因為資料和非同步任務都在無UI的Fragment中,再通過Activity 的 FragmentManager 互動即可。

只需記得在Headless Fragment銷燬時將持有的資料清空、停止非同步任務。

UIFragment.java

1

2

3

4

5

6

7

8

9

10

11

public class UIFragment extends Fragment{

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment,

container, false);

return view;

}

...

}

HeadlessFragment.java

1

2

3

4

5

6

7

8

9

10

11

12

13

public class HeadlessFragment extends Fragment{

@Override

public void onCreate(Bundle savedInstanceState) {

setRetainInstance(true);

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

return null;

}

...

}

具體例項程式碼如下:

問題二:Fragment重疊

其實是由Activity被回收後重啟所導致的Fragment重複建立和重疊的問題。

在Activity onCreate()中新增Fragment的時候一定不要忘了檢查一下savedInstanceState

1

2

3

4

if (savedInstanceState == null) {

getFragmentManager().beginTransaction().add(android.R.id.content,

new UIFragment()).commit();

}

多個Fragment重疊則可以這樣處理:通過FragmentManager找到所有的UI Fragment,按需要show()某一個Fragment,hide()其他即可!

為了能準確找出所需的Fragment,所以在add()或者replace() Fragment的時候記得要帶上tag引數,因為一個ViewGroup 容器可以依附add()多個Fragment,它們的id自然是相同的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

if (savedInstanceState == null) {

// getFragmentManager().beginTransaction()...commit()

}else{

//先通過id或者tag找到“復活”的所有UI-Fragment

UIFragment fragment1 = getFragmentManager().findFragmentById(R.id.fragment1);

UIFragment fragment2 = getFragmentManager().findFragmentByTag("tag");

UIFragment fragment3 = ...

...

//show()一個即可

getFragmentManager().beginTransaction()

.show(fragment1)

.hide(fragment2)

.hide(fragment3)

.hide(...)

.commit();

}