Fragment學習及簡單實踐

本文主要介紹下Android Fragment的生命周期,相關的API以及和Activity通信的簡單實踐

一、生命周期:

fragment_lifecycle.png

Fragment創建銷毀時,fragment和所依賴的activity生命周期的執行順序,注意看log的TAG

1.創建時

01-12 17:57:24.823 28028-28028/? I/ActivityLife: onCreate...
01-12 17:57:24.843 28028-28028/? I/FragmentLife: onAttach...
01-12 17:57:24.843 28028-28028/? I/FragmentLife: onCreate...
01-12 17:57:24.843 28028-28028/? I/FragmentLife: onCreateView...
01-12 17:57:24.843 28028-28028/? I/FragmentLife: onActivityCreated...
01-12 17:57:24.843 28028-28028/? I/ActivityLife: onStart...
01-12 17:57:24.843 28028-28028/? I/FragmentLife: onStart...
01-12 17:57:24.843 28028-28028/? I/ActivityLife: onResume...
01-12 17:57:24.843 28028-28028/? I/FragmentLife: onResume...

2.銷毀時

01-12 17:57:40.583 28028-28028/? I/FragmentLife: onPause...
01-12 17:57:40.583 28028-28028/? I/ActivityLife: onPause...
01-12 17:57:40.883 28028-28028/? I/FragmentLife: onStop...
01-12 17:57:40.883 28028-28028/? I/ActivityLife: onStop...
01-12 17:57:40.883 28028-28028/? I/FragmentLife: onDestroyView...
01-12 17:57:40.883 28028-28028/? I/FragmentLife: onDestroy...
01-12 17:57:40.883 28028-28028/? I/FragmentLife: onDetach...
01-12 17:57:40.883 28028-28028/? I/ActivityLife: onDestroy...

二、Activity中,Fragment的2種加載方法

1.靜態加載

(1)創建Fragment類和布局文件(fragment1.xml)

public class Fragment1 extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
       return inflater.inflate(R.layout.fragment1, container, false);
    }
}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#00ff00" >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is fragment 1"
        android:textColor="#000000"
        android:textSize="25sp" />
</LinearLayout> 

(2)在activity布局文件(activity_main.xml)中添加fragment布局

...
<fragment
android:id="@+id/fragment1"
android:name="com.example.sven.fragementdemo.Fragment1"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
...

2.動態加載

(1) 在activity布局文件中添加FragmentLayout節點

...
  <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>
...

(2) java代碼中動態加載

...
  1.獲取fragmentManager
FragmentManager fragmentManager = getFragmentManager();
  2.獲取FragmentTransaction
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
  3.創建需要的Fragment
Fragment fragment = new Fragment1();
  4.動態添加fragment
    將創建的fragment添加到Activity布局文件中定義的占位符中(FrameLayout)
fragmentTransaction.add(R.id.fragment_container,fragment).commit();
...

三、FragmentTransaction方法解析結合Fragment的生命周期

對Fragment的操作主要是通過調用FragmentTransaction類的方法進行的,FragmentTransaction的對象通常是通過getFragmentManager().beginTransaction()獲取的

1.add & remove / replace

(1) add 往Activity中添加一個Fragment,對應的fragment的生命周期如下:

06-13 12:01:43.406 1986-1986/? I/Fragment3: onAttach
06-13 12:01:43.406 1986-1986/? I/Fragment3: onCreate
06-13 12:01:43.406 1986-1986/? I/Fragment3: onCreateView
06-13 12:01:43.406 1986-1986/? I/Fragment3: onActivityCreated
06-13 12:01:43.406 1986-1986/? I/Fragment3: onStart
06-13 12:01:43.406 1986-1986/? I/Fragment3: onResume  

(2) remove 從Activity中移除一個Fragment,和add配對使用,通常是直接用replace

06-13 12:07:54.046 8765-8765/com.example.sven.fragementdemo I/Fragment3: onPause
06-13 12:07:54.046 8765-8765/com.example.sven.fragementdemo I/Fragment3: onStop
06-13 12:07:54.046 8765-8765/com.example.sven.fragementdemo I/Fragment3: onDestroyView
06-13 12:07:54.046 8765-8765/com.example.sven.fragementdemo I/Fragment3: onDestroy
06-13 12:07:54.046 8765-8765/com.example.sven.fragementdemo I/Fragment3: onDetach

(3) replace 使用另一個fragment替換當前的,先remove掉當前的再add新的,參考add和remove的過程

2.attach & detach

(1) attach 重建view視圖,添加到UI上并顯示,和detach配合使用,顯示一個被detach的fragment

06-13 11:57:46.376 29992-29992/com.example.sven.fragementdemo I/Fragment3: onCreateView
06-13 11:57:46.376 29992-29992/com.example.sven.fragementdemo I/Fragment3: onActivityCreated
06-13 11:57:46.376 29992-29992/com.example.sven.fragementdemo I/Fragment3: onStart
06-13 11:57:46.376 29992-29992/com.example.sven.fragementdemo I/Fragment3: onResume

(2) detach 會將view從UI中移除,和remove()不同,此時fragment的狀態依然由FragmentManager維護,fragment的實例還存在,但是視圖被銷毀了

06-13 11:58:13.486 29992-29992/com.example.sven.fragementdemo I/Fragment3: onPause
06-13 11:58:13.486 29992-29992/com.example.sven.fragementdemo I/Fragment3: onStop
06-13 11:58:13.486 29992-29992/com.example.sven.fragementdemo I/Fragment3: onDestroyView

3.hide & show

不涉及fragment生命周期,調試過程中沒有看到想象中onPause、onStop、onResume函數的調用過程,hide時fragment的視圖和實例都不會被銷毀,只是視圖可見與不可見的變化。賬號登陸界面應該用的比較多,能夠保存用戶的輸入,和detach區別就是detach不能夠的保存界面的信息(例如EditText的輸入),每次detach,attch時界面都會重繪

4.commit

提交對fragment的一系列操作。注意每一次對fragment的操作都要開一次事務,commit一次。commit和FragmentManager.beginTransaction()要配對使用。(很像數據庫,暫時沒深入研究)

5.addToBackStack(String)

把當前事務的變化情況添加到回退棧,下節詳細講下

tips:

1.以上這些方法通常是在Activity中使用,依附于同一個Activity的多個fragment的各種切換
2.fragment真正的實例其實是要通過FragmentTransaction.add,只有在add時才會執行onCreate,new的時候拿到只是一個引用 并沒有執行生命周期函數
3.使用attach/detach 或者 hide/show 都需要new出對象,并且執行add
4.add/remove/replace/hide/show后都要commit其效果才會在屏幕上顯示出來

四、Fragment的回退棧

Fragment的會退棧是用來保存每一次Fragment事務發生的變化,如果你將Fragment任務添加到回退棧,當點擊back鍵時,將看到上一次的保存的Fragment,一旦Fragment完全從后退棧中彈出,用戶再次點擊后退鍵,則退出當前Activity,如果被移除的Fragment沒有添加到回退棧,例如執行remove或者replace時,這個Fragment實例將會被銷毀

理解以下3種情況 在同一個Actity中,fragment1 跳轉到fragment2,然后按back退出時的不同情況

public void jump2fragment2(){
    FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    fragment之間跳轉時
    1.只采用replace 實例會被銷毀 back鍵一次
    fragmentTransaction.replace(R.id.fragment_container,new Fragment2());
    fragmentTransaction.addToBackStack(null);

    2.把當前事務的變化情況添加到回退棧,視圖會被銷毀但是實例還在(back 2次)
    fragmentTransaction.replace(R.id.fragment_container,new Fragment2());
    fragmentTransaction.addToBackStack(null);

    3.采用hide方式 實例不會被銷毀,視圖也不會被銷毀(back 2次)
    fragmentTransaction.hide(this);
    fragmentTransaction.add(R.id.fragment_container, new Fragment2());
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}

五、Fragment與Activity之間的通信

1.一般Fragment依附于Activity存在,因此與Activity之間的通信可以歸納為以下3點:

(1)Fragment中可以通過getActivity得到當前綁定的Activity的實例,然后進行操作

例如你在想在某個fragment中通過activity拿到fragment1的數據可以這樣做

public void getfragment1text() {
    getActivity().findViewById(R.id.fragment1_text)
}

(2) 如果你Activity中包含自己管理的Fragment的引用,可以通過引用直接訪問所有的Fragment的public方法

(3) 如果Activity中未保存任何Fragment的引用,那么沒關系,每個Fragment都有一個唯一的TAG或者ID,可以通過getFragmentManager.findFragmentByTag()或者findFragmentById()獲得任何Fragment實例,然后進行操作 fragment的id其實是無法設置的,add或replace時設置的id是其實要插入fragment所在ViewGroup的id,tag可以在add或replace時設置。https://stackoverflow.com/questions/9363072/android-set-fragment-id

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "ActivityLife";
    private Fragment1 fragment = new Fragment1();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        printLog("onCreate...");
        setContentView(R.layout.activity_main);

        // 1.獲取fragmentManager
        FragmentManager fragmentManager = getFragmentManager();
        // 2.獲取FragmentTransaction
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        // 3.創建需要的Fragment
        // Fragment fragment = new Fragment1();
        // 4.動態添加fragment
        // 將創建的fragment添加到Activity布局文件中定義的占位符中(FrameLayout)
        fragmentTransaction.add(R.id.fragment_container, fragment, "fragment1").commit();
    }

    public void getFragmentMessage(View view){
        //1.針對第2種情況
        fragment.printSth();
        //2.針對第3種情況
        Fragment fragment = getFragmentManager().findFragmentByTag("fragment1");
        printLog(fragment.toString());
        fragment.onResume();
    }
}

public class Fragment1 extends Fragment {
    ...
    public void printSth() {
        Log.i(TAG,"fragment3 printSth");
    }
}

(4)更復雜的用法參考:

http://blog.csdn.net/lmj623565791/article/details/37992017

2.應用場景主要就是通過Activity去管理多個Fragment的狀態

思路如下:

(1) 讓各個Fragment注冊一個監聽接口,讓Activity去implements這個監聽接口

(2) 在Activity中new出各個Fragment的對象,獲取引用

(3) 將Fragment中的事件通過listener傳遞到Activity中,在Fragment中通過getActivity去調用監聽方法

tips:

Fragment中的按鈕的事件響應時不能通過在配置文件中加onClick屬性找到的,必須注冊onClickListenr(有2種方式)

原因參考:http://blog.csdn.net/Zafir6453/article/details/51383915

3.以上這些自己寫了個Demo,方便大家調試和學習

http://download.csdn.net/detail/time_traveller14/9870932
1.生命周期,回退棧,參考fragment1,fragment2,MainActivity
2.FragmentTransaction及和Activity通信(fragment中不同的按鈕事件響應) 參考fragment3,fragment4, Main2Activity

六.還未理解問題:

1.回退棧的使用場景,有了hide和detach為何還要加這個,僅僅是為了多按一次back鍵?
2.hide和show為何沒有onPause和onResume的過程,hide為何不執行onPause,onStop呢?
3.remove 的真正含義銷毀還是出棧,看許多資料解釋是銷毀掉,但為何remove掉,commit完fragment!=null,還能夠通過tag獲取到,但是去尋找fragment的視圖元素會報錯?
4.屏幕旋轉時,已經判斷了onSavedInstance==null,為何還會繼續,log中 onCreate onCreateView會執行,要結合activity?

參考資料:

https://developer.android.com/guide/components/fragments.html
郭霖大神 http://blog.csdn.net/guolin_blog/article/details/8881711
鴻洋大神 http://blog.csdn.net/lmj623565791/article/details/37970961

自己的第一篇技術博客,請大家多多指教,最后不理解的問題,歡迎討論!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容