最新Activity與Fragment完全理解
阿新 • • 發佈:2019-02-02
Android是通過FragmentManager來管理Fragment,每次對Fragment進行新增和移除時需要開啟事務,通過事務處理這些相應的操作,然後commit事務。
新增Fragment方式一: FragmentManager fm = getSupportFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); tx.add (R.id.content, new Fragment1(),"Fragment1");
tx.commit();
這裡是直接通過add將Fragment1繫結到id為content的View上。
新增Fragment方式二:
FragmentManager fm = getSupportFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); tx.replace(R.id.content, new Fragment1(),"Fragment1"); tx.commit ();
這裡使用replace來新增Fragment,replace的作用相當於是remove() + add() 組合後的作用。即使用replace會先移除掉當前id為content上的Fragment,這個被移除掉的Fragment就會被銷燬掉(如果當前事務),然後通過add再把新的Fragment新增到View上。
(1)使用replace方式,相當於在對應id為content的FrameLayout上只有一層,那就是上面的Fragment1,通過replace這種方式,會把Fragment的生命週期再走一遍,如果我們的Fragment中有獲取資料的操作的話,會頻繁的去拉取資料;使用replace,Fragment繫結的檢視一定會銷燬,Fragment例項不一定會銷燬 ,主要看有沒有新增到回退棧。
(2)而通過add方式,我們可以在id為content的FrameLayout上新增多層,也即可以通過多次add來新增多個Fragment到FrameLayout上。這個時候,我們就可以配合hide()、show()方法來不斷切換不同的Fragment。在我們通過add方式添加了Fragment到FrameLayout 的View上之後,通過hide()、show()來切換Fragment還有一個優勢就是,當一個Fragment重新show展示出來的時候,它原來的資料還保留在該Fragment上,也就是說hide並不會銷燬Fragment,只是單純的隱藏了而已。
推薦方式:
因此,推薦使用add、hide、show的方式管理Fragment。但是這種方式一些情況下也會有一個缺陷就是:可能會造成Fragment重疊。
比如底部有四個Tab:tab1,tab2,tab3,tab4,對應的Fragment引用為tab1Fragment,tab2Fragment,tab3Fragment,tab4Fragment,首先通過add將這四個Fragment新增到FragmentManager後,通過hide和show切換不同TAB都可以處於正常情況,當我們切換到tab1時,假設tab1上的Fragment為
tab1Fragment變數指向的(即tab1Fragment= new Fragment()),這個時候我們按下HOME鍵,如果長時間沒有回到應用或者記憶體不足了,系統回收了該引用,此時tab1Fragment= null;但是,tab1的Fragment例項其實還是存在與記憶體中,只是引用被銷燬了,這個時候,我們切換到tab2,這個步驟中,我們會把tab1的fragment隱藏掉,然後顯示tab2,即如下操作:
tx.hide(tab1Fragment);
tx.show(tab2Fragment);
tx.commit();
但是,因為此時 tab1Fragment = null,引用變數為空,hide操作無法實現隱藏功能,但是又由於tab1上的Fragment例項還處於記憶體中,因此此時會造成tab2與tab1重疊的現象。再切換到tab1時,因為tab1Fragment = null,此時會再去建立一個新的Fragment,放入到tab1上,導致原來的tab1上的Fragment一直存在與記憶體中導致重疊,直至它被回收。
造成上述問題的原因還是因為我們無法找到那個例項物件Fragment,因為引用tab1Fragment已經為空了。這個時候,我們在add的時候可以給Fragment繫結一個tag,用它來標識該Fragment,如果引用為空了,再通過tag來找到該Fragment。如下:
當然,在Fragment裡面也有onSaveInstanceState(Bundle)方法,可以通過這個來儲存資料,然後再onCreate或其他方法裡面獲取到資料來解決資料丟失的問題。
1、新增、移除Fragment的幾種方式
在對Fragment進行管理前,需要開啟一個事務,如下: FragmentManager fm = getSupportFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); FragmentTransaction下管理Fragment的主要方法有add()、remove()、replace()、hide()、show()、detach()、attach()。新增Fragment方式一: FragmentManager fm = getSupportFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); tx.add
FragmentManager fm = getSupportFragmentManager(); FragmentTransaction tx = fm.beginTransaction(); tx.replace(R.id.content, new Fragment1(),"Fragment1"); tx.commit
- //在新增Fragment時
- FragmentManager fm = getSupportFragmentManager();
- FragmentTransaction tx = fm.beginTransaction();
- tab1Fragment = new Fragment1();
- tx.add(R.id.content, tab1Fragment,"fragment1");
- tx.commit();
- //在使用時,比如切換到tab2時
- if(tab1Fragment != null){
- tx.hide(tab1Fragment);
- tx.show(tab2Fragment);
- tx.commit();
- }else{
- tab1Fragment = (Fragment1) fm.findFragmentByTag("fragment1");
- tx.hide(tab1Fragment);
- tx.show(tab2Fragment);
- tx.commit();
- }
2、回退棧
當我們在一個Activity中有多個Fragment進行切換時,我們按下返回鍵時,會直接退出這個Activity,如果我們想在按下返回鍵時,退回到上一個顯示的Fragment,而不是直接返回,我們就需要使用到回退棧了,類似於系統為每個應用建立的Activity棧一樣,每個Activity也維護著一個事務回退棧,在我們通過事務對Fragment進行操作的時候,如果將這個事務新增到回退棧了,這個Fragment的例項就不會被銷燬。按返回鍵時就可以回到這裡。如下: 在MainActivity中,- publicclass MainActivity extends Activity {
- @Override
- protectedvoid onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- FragmentManager fm = getSupportFragmentManager();
- FragmentTransaction tx = fm.beginTransaction();
- tx.add(R.id.content, new Fragment1(),"fragment1");
- tx.commit();
- }
- }
- publicclass Fragment1 extends Fragment implements OnClickListener {
- private Button mBtn;
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_one, container, false);
- mBtn = (Button) view.findViewById(R.id.id_fragment_one_btn);
- mBtn.setOnClickListener(this);
- return view;
- }
- @Override
- publicvoid onClick(View v) {
- Fragment2 fTwo = new Fragment2();
- FragmentManager fm = getFragmentManager();
- FragmentTransaction tx = fm.beginTransaction();
- tx.replace(R.id.content, fTwo, "fragment2");
- tx.addToBackStack(null); //將當前事務新增到回退棧
- tx.commit();
- }
- }
- publicclass Fragment2 extends Fragment implements OnClickListener {
- private Button mBtn ;
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.fragment_two, container, false);
- mBtn = (Button) view.findViewById(R.id.id_fragment_two_btn);
- mBtn.setOnClickListener(this);
- return view ;
- }
- @Override
- publicvoid onClick(View v) { //開啟Fragment3
- Fragment3 fThree = new Fragment3();
- FragmentManager fm = getFragmentManager();
- FragmentTransaction tx = fm.beginTransaction();
- tx.hide(this); //隱藏當前顯示的Fragment2
- tx.add(R.id.content , fThree, "fragment3"); //新增Fragment3
- tx.addToBackStack(null); //將當前事務新增到回退棧
- tx.commit();
- }
- }
當然,在Fragment裡面也有onSaveInstanceState(Bundle)方法,可以通過這個來儲存資料,然後再onCreate或其他方法裡面獲取到資料來解決資料丟失的問題。
3、與Activity通訊
因為Fragment依附於Activity,Activity與Fragment通訊,可以有以下幾種辦法:
(1)如果你Activity中包含自己管理的Fragment的引用,可以通過引用直接訪問所有的Fragment的public方法
(2)如果Activity中沒有儲存任何Fragment的引用,那麼沒關係,每個Fragment都有一個唯一的TAG或者ID,可以通過getSupportFragmentManager().findFragmentByTag()或者findFragmentById()獲得任何Fragment例項,然後進行操作。
(3)在Fragment中可以通過getActivity得到當前繫結的Activity的例項,然後進行操作。