1.基本概念
1)Fragment是什么,有什么用?
答:Fragment是可以让你的app纵享丝滑的设计,如果你的app想在现在基础上性能大幅度提高,并且占用内存降低,同样的界面Activity占用内存比Fragment要多,响应速度Fragment比Activty在中低端手机上快了很多,甚至能达到好几倍!如果你的app当前或以后有移植平板等平台时,可以让你节省大量时间和精力。
下图是文档中给出的一个Fragment分别对应手机与平板间不同情况的处理图:
PS:简单的新闻浏览页面,使用两个Fragment分别显示新闻列表与新闻内容;
2)Fragmeng优点
- Fragment可以使你能够将activity分离成多个可重用的组件,每个都有它自己的生命周期和UI。
- Fragment可以轻松得创建动态灵活的UI设计,可以适应于不同的屏幕尺寸。从手机到平板电脑。
- Fragment是一个独立的模块,紧紧地与activity绑定在一起。可以运行中动态地移除、加入、交换等。
- Fragment提供一个新的方式让你在不同的安卓设备上统一你的UI。
- Fragment 解决Activity间的切换不流畅,轻量切换。
- Fragment 替代TabActivity做导航,性能更好。
- Fragment 在4.2.版本中新增嵌套fragmeng使用方法,能够生成更好的界面效果。
- Fragment做局部内容更新更方便,原来为了到达这一点要把多个布局放到一个activity里面,现在可以用多Fragment来代替,只有在需要的时候才加载Fragment,提高性能
3)Fragment的生命周期图
可以看到Fragment比Activity多了几个额外的生命周期回调方法:
onAttach:onAttach()在fragment与Activity关联之后调调查用。需要注意的是,初始化fragment参数可以从getArguments()获得,但是,当Fragment附加到Activity之后,就无法再调用setArguments()。所以除了在最开始时,其它时间都无法向初始化参数添加内容。
onCreate:fragment初次创建时调用。尽管它看起来像是Activity的OnCreate()函数,但这个只是用来创建Fragment的。此时的Activity还没有创建完成,因为我们的Fragment也是Activity创建的一部分。所以如果你想在这里使用Activity中的一些资源,将会获取不到。比如:获取同一个Activity中其它Frament的控件实例。(代码如下:),如果想要获得Activity相关联的资源,必须在onActivityCreated中获取。
onCreateView:在这个fragment构造它的用户接口视图(即布局)时调用。
onActivityCreated:在Activity的OnCreate()结束后,会调用此方法。所以到这里的时候,Activity已经创建完成!在这个函数中才可以使用Activity的所有资源。如果把下面的代码放在这里,获取到的btn_Try的值将不会再是空的!
onStart:当到OnStart()时,Fragment对用户就是可见的了。但用户还未开始与Fragment交互。在生命周期中也可以看到Fragment的OnStart()过程与Activity的OnStart()过程是绑定的。意义即是一样的。以前你写在Activity的OnStart()中来处理的代码,用Fragment来实现时,依然可以放在OnStart()中来处理。
onResume:当这个fragment对用户可见并且正在运行时调用。这是Fragment与用户交互之前的最后一个回调。从生命周期对比中,可以看到,Fragment的OnResume与Activity的OnResume是相互绑定的,意义是一样的。它依赖于包含它的activity的Activity.onResume。当OnResume()结束后,就可以正式与用户交互了。
onPause:此回调与Activity的OnPause()相绑定,与Activity的OnPause()意义一样。
onStop:这个回调与Activity的OnStop()相绑定,意义一样。已停止的Fragment可以直接返回到OnStart()回调,然后调用OnResume()。
onDestroyView:如果Fragment即将被结束或保存,那么撤销方向上的下一个回调将是onDestoryView()。会将在onCreateView创建的视图与这个fragment分离。下次这个fragment若要显示,那么将会创建新视图。这会在onStop之后和onDestroy之前调用。这个方法的调用同onCreateView是否返回非null视图无关。它会潜在的在这个视图状态被保存之后以及它被它的父视图回收之前调用。
onDestroy:当这个fragment不再使用时调用。需要注意的是,它即使经过了onDestroy()阶段,但仍然能从Activity中找到,因为它还没有Detach。
onDetach:Fragment生命周期中最后一个回调是onDetach()。调用它以后,Fragment就不再与Activity相绑定,它也不再拥有视图层次结构,它的所有资源都将被释放。
2.创建一个Fragment
1)静态加载Fragment
实现流程:
示例代码:
Step 1:定义两个Fragment的布局,就是fragment显示内容的(两个都一样,所以这里就贴一个)
fragment1.xml:
<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>
Step 2:自定义一个Fragment类,需要继承Fragment或者他的子类,重写onCreateView()方法 在该方法中调用:inflater.inflate()方法加载Fragment的布局文件,接着返回加载的view对象,我们分别创建两个fragment的布局,分别是Fragmentone,Fragmenttwo,因为都一样所以就贴一个
public class Fragmentone extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container,false);
return view;
}
}
注意,使用的V4包中的Fragment!这里的代码主要就是加载了我们刚刚写好的fragment1.xml布局文件并返回。同样的方法,我们再写好Fragment2
Step 3:在需要加载Fragment的Activity对应的布局文件中添加fragment的标签, 记住,name属性是全限定类名哦,就是要包含Fragment的包名,如:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
<fragment
android:id="@+id/fragment1"
android:name="com.harvic.com.harvicblog2.Fragment1"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment2"
android:name="com.harvic.com.harvicblog2.Fragment2"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
Step 4: Activity在onCreate( )方法中调用setContentView()加载布局文件即可!
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
2)动态加载Fragment
实现流程:
打开activity_main.xml,将其中代码全部删除,改成下面的样子:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:baselineAligned="false" >
<Button
android:id="@+id/btn_show_fragment1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="显示Fragment1"/>
<Button
android:id="@+id/btn_show_fragment2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="显示Fragment2"/>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
主界面上有两个按钮和一个FrameLayout布局。这两个按钮分别用来在这个FrameLayout加载Fragment1和Fragment2的实例。效果如下:
(由于在录GIF时,绿色会出问题,所以把fragment1的背景改成了紫色)
其它代码都没有动,主要的是在MainActivity里,点击这两个按钮时做的处理:
MainActivity
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnLoadFrag1 = (Button)findViewById(R.id.btn_show_fragment1);
btnLoadFrag1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment1 fragment1 = new Fragment1();
transaction.add(R.id.fragment_container, fragment1);
transaction.commit();
}
});
Button btnLoagFrag2 = (Button)findViewById(R.id.btn_show_fragment2);
btnLoagFrag2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment2 fragment2 = new Fragment2();
transaction.add(R.id.fragment_container, fragment2);
transaction.commit();
}
});
}
}
看上面的代码很容易明白,在点击按钮时都做了类似的操作:
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
Fragment1 fragment1 = new Fragment1();
transaction.add(R.id.fragment_container, fragment1);
transaction.commit();
动态添加Fragment主要分为4步:
1.获取到FragmentManager,在V4包中通过getSupportFragmentManager,在系统中原生的Fragment是通过getFragmentManager获得的。
2.开启一个事务,通过调用beginTransaction方法开启。
3.向容器内加入Fragment,一般使用add或者replace方法实现,需要传入容器的id和Fragment的实例。
4.提交事务,调用commit方法提交。
这部分有关fragment的操作看不大懂也没关系,下节我们会具体讲有关Fragment的管理!
3.Fragment管理与Fragment事务
4.Fragment与Activity的交互
1)组件获取
Fragment获得Activity中的组件 | getActivity().findViewById(R.id.list); |
Activity获得Fragment中的组件(根据id和tag都可以) | getFragmentManager.findFragmentByid(R.id.fragment1); |
2)数据传递
①Activit传递数据给Fragment:
在Activity中创建Bundle数据包,调用Fragment实例的setArguments(bundle) 从而将Bundle数据包传给Fragment,然后Fragment中调用getArguments获得 Bundle对象,然后进行解析就可以了
②Fragment传递数据给Activity
在Fragment中定义一个内部回调接口,再让包含该Fragment的Activity实现该回调接口, Fragment就可以通过回调接口传数据了,回调,相信很多人都知道是什么玩意,但是 写不出来啊,网上的一百度"fragment传数据给Activity",全是李刚老师的那个代码,真心无语 算了,这里就写下局部代码吧,相信读者一看就懂的了:
Step 1:定义一个回调接口:(Fragment中)
/*接口*/
public interface CallBack{
/*定义一个获取信息的方法*/
public void getResult(String result);
}
Step 2:接口回调(Fragment中)
/*接口回调*/
public void getData(CallBack callBack){
/*获取文本框的信息,当然你也可以传其他类型的参数,看需求咯*/
String msg = editText.getText().toString();
callBack.getResult(msg);
}
Step 3:使用接口回调方法读数据(Activity中)
/* 使用接口回调的方法获取数据 */
leftFragment.getData(new CallBack() {
@Override
public void getResult(String result) { /*打印信息*/
Toast.makeText(MainActivity.this, "-->>" + result, 1).show();
}
});
③Fragment与Fragment之间的数据互传
其实这很简单,找到要接受数据的fragment对象,直接调用setArguments传数据进去就可以了 通常的话是replace时,即fragment跳转的时候传数据的,那么只需要在初始化要跳转的Fragment 后调用他的setArguments方法传入数据即可!
如果是两个Fragment需要即时传数据,而非跳转的话,就需要先在Activity获得f1传过来的数据, 再传到f2了,就是以Activity为媒介~
MainActivity
//[1]获取fragment管理者
FragmentManager fragmentManager = getFragmentManager();
//[2]开启一个事物
FragmentTransaction beginTransaction = fragmentManager.beginTransaction();
//[2.1]替换fragment
beginTransaction.replace(R.id.ll1, new Fragment1(),"f1");
beginTransaction.replace(R.id.ll2, new Fragment2(),"f2");
//[3]开启事物
beginTransaction.commit();
Fragment1()和Fragment2()一样,只写个Fragment1()
public class Fragment1 extends Fragment {
//当系统第一次画ui的时候调用 通过这个方法方法可以让Fragment显示自己的布局内容
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//通过打气筒把一个布局转换成一个View对象
View view = inflater.inflate(R.layout.fragment1, null);
//[1]找到按钮 设置点击事件
view.findViewById(R.id.btn_update).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//[2]修改fragment2里面的内容 通过fragment的公共桥梁 --->activity
Fragment2 fragment2 = (Fragment2) getActivity().getFragmentManager().findFragmentByTag("f2");
fragment2.updateText("哈哈哈呵呵");
}
});
return view;
}
}