1. 程式人生 > >Activity與Fragment易混點歸納

Activity與Fragment易混點歸納

Android是通過FragmentManager來管理Fragment,每次對Fragment進行新增和移除時需要開啟事務,通過事務處理這些相應的操作,然後commit事務。

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
(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時
        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中,
public class MainActivity extends Activity  {  
    @Override  
    protected void 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();  
    }  
}  
進入Activity時初始化載入第一個Fragment1,這裡我們並沒有把它加入到回退棧中,這是因為當目前顯示的是Fragment1時,按下返回鍵我們就是需要直接退出,因為這是最初的那個Fragment,如果我們加入到了回退棧,那按下返回鍵後將是一片空白,再按一次返回鍵後才會退出這個Activity,這是因為第一次按下返回鍵時,相當於是將Fragment1從棧中彈出,此時被銷燬了,Activity的FrameLayout也就沒有了Fragment依附,因此一片空白。 在Fragment1中,有了一個按鈕,點選後開啟第二個Fragment2,
public class 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  
        public void 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();  
        }  
    }  
在Fragment2中,
public class 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  
        public void 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();  
        }  
    }  
在Fragment3中我們只是列印了一些訊息,就不再寫了。 噹噹前顯示到Fragment3時,我們按下返回鍵,將會顯示出Fragment2出來,繼續按下返回鍵,顯示出Fragment1出來,再按下後,直接退出Activity。 因為我們在Fragment1和Fragment2中,在事務提交之前,即tx.commit()之前,我們把當前的事務(用新的Fragment替換當前顯示Fragment或者hide當前Fragment)加入到了回退棧,即tx.addToBackStack(null),點選返回鍵後,就從回退棧中退出棧頂元素,即上一個加入的事務。 上面我們使用了前面介紹的兩種新增Fragment的方式,即replace方式和hide()、add()方式,replace方式,Fragment繫結的檢視一定會銷燬,如果該事務加入到了回退棧,Fragment例項就不會被銷燬,只是檢視銷燬了;而hide()、add()方式隱藏當前Fragment,加入新的Fragment,隱藏的Fragment繫結的檢視也不會被銷燬。 這裡的檢視銷不銷燬,指的是我們的資料有沒有被儲存下來,如果檢視被銷燬了,說明重新回到這個Fragment後,會重新進入onCreate及之後的週期方法區建立一個新的檢視,這個時候資料肯定就不在了如果檢視沒有被銷燬,在重新回到這個Fragment時,原來的輸入資料還在,沒有丟失
當然,在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的例項,然後進行操作。