简介
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