android中的第五大組件->Fragment

簡介

fragment在android中是以碎片的形式依附在activity中,常被成爲android控件中的第五大組件,現在更多的形式都是以單activity+多fragment的app結構。在相同界面中,用fragment比activity所佔用內存要小的多,如果我們考慮到性能優化這方面,我們也可以優先考慮使用fragment。我們可以從以下幾個方面進行解析。

  1. fragment爲何成爲第五大組件?
  2. fragment的加載方式。
  3. fragment的生命週期。
  4. fragment的常用方式。
  5. fragment與activity進行的通信。

fragment爲何成爲android第五大組件?

我們都知道android有主要的四大組件:activity,service,broadcastReceiver,Contentprovider.所以引發我們的思考fragment爲什麼稱爲android第五大組件呢?
1.隨着android越來越成熟,各方面的性能優化,如內存優化,界面性能優化方面,使用fragment所佔用的內存少,界面比較順滑。
2.在使用頻率上,fragment是不輸與其他四大組件的,它具有自己的生命週期,同時可以靈活的動態,靜態的加載activity的內容,所以成爲第五大組件。

fragment的加載方式

fragment分爲動態加載和靜態加載。
動態加載:

主佈局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.trip.fragmenttest.MainActivity">

    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="切換fragment"/>

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

    </FrameLayout>
</LinearLayout>

fragment1的佈局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/lblFragment1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="fragment1"
        android:textColor="#000000"
        android:textSize="25sp" />
</LinearLayout>

MainActivity代碼:

public class MainActivity extends AppCompatActivity {

    private Button btn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn = findViewById(R.id.btn);
        //獲取fragmentManager管理者 如果繼承爲FragmentActivity則:
    FragmentManager fragmentManager = getSupportFragmentManager();
    final FragmentManager fragmentManager = getFragmentManager();
        //開啓FragmentTransaction事務
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        //通過事務向Activity的佈局中添加MyFragment
        fragmentTransaction.add(R.id.fragment_content, new Fragment1());
        fragmentTransaction.commit();

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //開啓事務
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                fragmentTransaction.replace(R.id.fragment_content,new Fragment2());
                fragmentTransaction.commit();
            }
        });
    }
}

注:
1.如果mainActivity繼承Activity則Fragment爲 android.app.Fragment,如果繼承FragmentActivity則是android.support.v4.app.Fragment;
2.fragment01和fragment02需動態的繼承import android.app.Fragment或import android.support.v4.app.Fragment;
3.fragmentTransaction需要再次進行add,replace,remove操作的時候,需重新beginTransaction(),然後進行commit提交操作。如果直接進行commit會報commit already called異常.

靜態加載:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.trip.fragmenttest.MainActivity">

    <fragment
        android:id="@+id/lblFragment1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.trip.fragmenttest.Fragment1"/>
</LinearLayout>

關於靜態加載可直接在主佈局加入fragment,android:name的屬性是該fragment的佈局。

fragment的生命週期

這裏寫圖片描述

當fragment啓動的時候:

12-18 13:14:29.395 32566-32566/com.trip.fragmenttest E/fragment1: onAttach
12-18 13:14:29.395 32566-32566/com.trip.fragmenttest E/fragment1: onCreate
12-18 13:14:29.398 32566-32566/com.trip.fragmenttest E/fragment1: onCreateView
12-18 13:14:29.399 32566-32566/com.trip.fragmenttest E/MainActivity: onCreate
12-18 13:14:29.401 32566-32566/com.trip.fragmenttest E/fragment1onActivityCreated
12-18 13:14:29.402 32566-32566/com.trip.fragmenttest E/fragment1: onStart
12-18 13:14:29.402 32566-32566/com.trip.fragmenttest E/MainActivity: onStart
12-18 13:14:29.406 32566-32566/com.trip.fragmenttest E/MainActivity: onResume
12-18 13:14:29.407 32566-32566/com.trip.fragmenttest E/fragment1: onResume

按home鍵回到桌面的時候:

12-18 13:16:28.855 32566-32566/com.trip.fragmenttest E/fragment1: onPause
12-18 13:16:28.855 32566-32566/com.trip.fragmenttest E/MainActivity: onPause
12-18 13:16:29.211 32566-32566/com.trip.fragmenttest E/fragment1: onStop
12-18 13:16:29.211 32566-32566/com.trip.fragmenttest E/MainActivity: onStop

回到應用的時候:

12-18 13:17:11.243 32566-32566/com.trip.fragmenttest E/MainActivity: onRestart
12-18 13:17:11.243 32566-32566/com.trip.fragmenttest E/fragment1: onStart
12-18 13:17:11.243 32566-32566/com.trip.fragmenttest E/MainActivity: onStart
12-18 13:17:11.244 32566-32566/com.trip.fragmenttest E/MainActivity: onResume
12-18 13:17:11.244 32566-32566/com.trip.fragmenttest E/fragment1: onResume

跳轉頁面的時候:

12-18 13:21:16.707 3659-3659/com.trip.fragmenttest E/fragment1: onPause
12-18 13:21:16.707 3659-3659/com.trip.fragmenttest E/MainActivity: onPause
12-18 13:21:17.069 3659-3659/com.trip.fragmenttest E/fragment1: onStop
12-18 13:21:17.069 3659-3659/com.trip.fragmenttest E/MainActivity: onStop

退出應用:

12-18 13:22:29.086 3659-3659/com.trip.fragmenttest E/fragment1: onPause
12-18 13:22:29.086 3659-3659/com.trip.fragmenttest E/MainActivity: onPause
12-18 13:22:29.462 3659-3659/com.trip.fragmenttest E/fragment1: onStop
12-18 13:22:29.462 3659-3659/com.trip.fragmenttest E/MainActivity: onStop
12-18 13:22:29.463 3659-3659/com.trip.fragmenttest E/fragment1: onDestroyView
12-18 13:22:29.463 3659-3659/com.trip.fragmenttest E/fragment1: onDestroy
12-18 13:22:29.463 3659-3659/com.trip.fragmenttest E/fragment1: onDetach
12-18 13:22:29.463 3659-3659/com.trip.fragmenttest E/MainActivity: onDestroy

注意:
以上爲fragment的生命週期各個執行過程,fragment只能依附在activity中存在,並不能單獨的界面存在。

4.fragment的常用方式

fragment常常與viewpager進行搭配使用.可參考:
http://blog.csdn.net/zhang31jian/article/details/29867951

可涉及到FragmentPagerAdapter和FragmentStatePagerAdapter的區別:

當ViewPager使用的是FragmentPagerAdapter的時候:

This version of the pager is best for use when there are a handful of
 * typically more static fragments to be paged through, such as a set of tabs.
 * The fragment of each page the user visits will be kept in memory, though its
 * view hierarchy may be destroyed when not visible.  This can result in using
 * a significant amount of memory since fragment instances can hold on to an
 * arbitrary amount of state.  For larger sets of pages, consider

上面描述解釋的意思是: 這個pager最好的使用是用於一些靜態界面,像一些tabs欄,這些fragment會一直保存在內存當中,即使fragment中的view被destroyed。

fragment被移除所執行的方法爲:onPouse()->onStop()->onDestroyView()(不會執行onDestroy()方法和onDetach())。

當ViewPager使用FragmentStatePagerAdapter的時候:

This version of the pager is more useful when there are a large number
 * of pages, working more like a list view.  When pages are not visible to
 * the user, their entire fragment may be destroyed, only keeping the saved
 * state of that fragment.  This allows the pager to hold on to much less
 * memory associated with each visited page as compared to
 * {@link FragmentPagerAdapter} at the cost of potentially more overhead when
 * switching between pages.

由官方解釋的意思是:這個pager比較適合一些比較多的fragment的界面,當界面不可見的時候,所用到的fragment也會被destroyed,因爲它只緩存保存狀態的Fragment。

fragment被移除所執行的方法爲:onPouse()->onStop()->onDestroyView()-_onDestroy()->onDetach()。

總結:當page數量少,用FragmentPagerAdapter;反之則用FragmentStatePagerAdapter;它們兩的Fragment生命週期在ViewPage的切換過程中都會重複執行多次,所以它都不適用於App主頁Tab。

5. fragment與activity進行的通信。

activity傳值給fragment方式:

1.使用Bundle傳值:

        Fragment1  fragment = new Fragment1();
        Bundle bundle = new Bundle();
        bundle.putString("id", "123");
        fragment.setArguments(bundle);

  在fragment接收值:      String type = (String) getArguments().get("id");

2.使用方法獲取值:

public String getTitles(){
    return "hello";
}
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    titles = ((MainActivity) activity).getTitles();//通過強轉成宿主activity,就可以獲取到傳遞過來的數據
}

3.創建Fragment和傳遞數值

Fragment1 fragment1 = MyFragment.newInstance("這是第一個fragment");
在fragment中接收:
static MyFragment newInstance(String s){
        MyFragment myFragment = new MyFragment();
        Bundle bundle = new Bundle();
        bundle.putString("DATA",s);
        myFragment.setArguments(bundle);
        return myFragment;
    }

fragment傳值給activity:
使用回調函數傳值:

public class MenuFragment extends Fragment implements View.OnClickListener {
    // 2.1 定義用來與外部activity交互,獲取到宿主activity
    private FragmentInteraction listterner;
    // 1 定義了所有activity必須實現的接口方法
    public interface FragmentInteraction {
        void process(String str);
    }
    // 當FRagmen被加載到activity的時候會被回調
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if(activity instanceof FragmentInteraction) {
            listterner = (FragmentInteraction)activity; // 2.2 獲取到宿主activity並賦值
        } else{
            throw new IllegalArgumentException("activity must implements FragmentInteraction");
        }
    }
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_menu, container, false);
        View btn = view.findViewById(R.id.tv_button);
        View btn_m = view.findViewById(R.id.movie_button);
        if (btn != null||btn_m!=null) {
            btn.setOnClickListener(this);
            btn_m.setOnClickListener(this);
        }
        return view;
    }
    @Override
    public void onClick(View v) {
        int id  = v.getId();
        switch (id) {
            case R.id.tv_button:
                listterner.process("我是電視劇"); // 3.1 執行回調
                break;
            case R.id.movie_button:
                listterner.process("我是電影");
                break;
        }
    }
    //把傳遞進來的activity對象釋放掉
    @Override
    public void onDetach() {
        super.onDetach();
        listterner = null;
    }
}

然後在mainActivity中實現該回調函數:

public class MainActivity extends Activity implements MenuFragment.FragmentInteraction{

    private TextView textView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        textView = (TextView) findViewById(R.id.content_text);
    }

    // 3.2 +實現接口,實現回調
    @Override
    public void process(String str) {
        if (str != null) {
            textView.setText(str);
        }
    }
}

注:可使用第三方開源框架(EventBus)來實現fragment與activity進行通信。可自行百度搜索。

總結

fragment在android中使用的範圍越來越廣泛,當前fragment中也有很多坑,官方Fragment庫的那些自身的BUG,並給出解決方案。但是真真切切也給程序帶來了優化,已經讓android界面更加的流暢。
擴展內容:
http://www.jianshu.com/p/d9143a92ad94

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章