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