Android Fragment(动态,静态)碎片详解及总结
android fragment(动态,静态)碎片详解
一.fragment的相关概念(一)fragment的基础知识
fragment是android3.0新增的概念,中文意思是碎片,它与activity十分相似,用来在一个 activity中描述一些行为或一部分用户界面.使用多个fragment可以在一个单独的activity中建 立多个ui面板,也可以在多个activity中使用fragment。
fragment拥有自己的生命 周期和接收、处理用户的事件,这样就不必在activity写一堆控件的事件处理的代码了。更为 重要的是,你可以动态的添加、替换和移除某个fragment。
一个fragment必须总是被嵌入到一个activity中,它的生命周期直接被其所属的宿主activity生 命周期影响,它的状态会随宿主的状态变化而变化。 要创建一个fragment 必须创建一个fragment的子类,或者继承自另一个已经存在的 fragment的子类.并重写 oncreateview()方法加载ui。
(二)fragment生命周期
因为fragment必须嵌入在acitivity中使用,所以fragment的生命周期和它所在的activity是 密切相关的。 如果activity是暂停状态,其中所有的fragment都是暂停状态;如果activity是stopped状 态,这个activity中所有的fragment都不能被启动;如果activity被销毁,那么它其中的所有 fragment都会被销毁。 但是,当activity在活动状态,可以独立控制fragment的状态,比如加上或者移除 fragment。 当这样进行fragment transaction(转换)的时候,可以把fragment放入activity的back stack中,这样用户就可以进行返回操作。
下面是activity对象和fragment对象的全部生命周期的对比图:
可以看到activity有七个生命周期,fragment有十一个生命周期。
(三)fragment中几个重要的回调方法说明:1.onattach(activity)
当fragment与activity发生关联时调用。
2.oncreateview(layoutinflater, viewgroup,bundle) 创建该fragment的视图3.onactivitycreated(bundle)
当activity的oncreate方法返回时调用
4.ondestoryview()
与oncreateview相对应,当该fragment的视图被移除时调用
5.ondetach()与onattach相对应
当fragment与activity关联被取消时调用
注意:除了oncreateview,其他的所有方法如果你重写了,必须调用父类对于该方法的实现,就是super。。不能去掉。
(四)fragment家族常用的api1.
fragment常用的三个类:
(1)android.app.fragment主要用于定义
(2)fragment android.app.fragmentmanager 主要用于在activity中操作
(3)fragment android.app.fragmenttransaction 保证一些列fragment操作的原子性,熟悉事务这个词, 一定能明白。
2.获取fragmentmanage的方式:
(1)getfragmentmanager()
(2)getsupportfragmentmanager //v4包中fragmentactivity
3.fragmenttransaction的操作和方法(1)开启一个事务
fragmenttransaction transaction = fm.bengintransatcion();
(2)往activity中添加一个fragment
transaction.add()
(3)从activity中移除一个fragment,如果被移除的fragment没有添加到回退栈(回退栈后面会详细说),这个fragment实例将会被销毁。
transaction.remove()
(4)使用另一个fragment替换当前的,实际上就是remove()然后add()的合体
transaction.replace()
(5)当你的fragment数量固定很少时隐藏当前的fragment,仅仅是设为不可见,并不会销毁,多的时候可能出现oom异常
transaction.hide()
(6)显示之前隐藏的fragment
transaction.show()
(7)detach()会将view从ui中移除,和remove()不同,此时fragment的状态依然由fragmentma nager维护。(8)attach()重建view视图,附加到ui上并显示。(9)transatcion.commit()//提交一个事务
上述,基本是操作fragment的所有的方式了,在一个事务开启到提交可以进行多个的添加、 移除、替换等操作。
值得注意的是:如果你喜欢使用fragment,一定要清楚这些方法,哪个会销毁视图,哪个会销毁实例,哪个仅仅只是隐藏,这样才能更好的使用它们。
比如:我在fragmenta中的edittext填了一些数据,当切换到fragmentb时,如果希望回到a还能看到数据,则适合你的就是hide和show;也就是说,希望保留用户操作的面板,你可以使用hide和show,当然了不要使劲在那new实例,最好进行下非null判断。
再比如:我不希望保留用户操作,你可以使用remove(),然后add();或者使用replace() 这个和remove,add是相同的效果。
remove和detach有一点细微的区别,在不考虑回退栈的情况下,remove会销毁整个 fragment实例,而detach则只是销毁其视图结构,实例并不会被销毁。那么二者怎么取舍使用呢?如果你的当前activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用 detach。
二.activity中添加fragment
(一)添加fragment的两种方法1.方法一(activity的布局文件中加入标签) 在xml中配置更加简单一点,但是灵活性不够,不能在同一个位置去切换多个不同的 fragment
<fragment android:id="@+id/myfragment" android:name="包名.fragment类名" android:layout_width="match_parent" android:layout_height="match_parent" />
注意:fragment必须设置id或者tag,并且需要指定name为类名。
这样使用就相当于把fragment当作一块布局来使用了!
2.方法二(使用fragmenttransaction的add()方法加入fragment)(1)获取到fragmentmanager,在activity中可以直接通过getfragmentmanager得到。
fragmentmanager fragmentmanager = getfragmentmanager();//这里要注意是否是v4包的
(2)开启一个事务,通过调用begintransaction方法开启。
fragmenttransaction fragmenttransaction = fragmentmanager.begintransaction();
(3)向容器内加入fragment,一般使用add或者replace方法实现,需要传入容器的id和 fragment的实例。
fragmenttransaction.replace(activity设置的布局中的viewgroup组件id,需要替换的fragment实例);
//也可以使用三参的方法,传递一个tag
(4)提交事务,调用commit方法提交。
fragmenttransaction.commit();
(二)fragment回退栈
类似与android系统为activity维护一个任务栈,我们也可以通过activity维护一个回退栈来保 存每次fragment事务发生的变化。如果你将fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的fragment。一旦fragment完全从后退栈中弹出,用户再次点击后退键,则退出当前activity。 假设现在我们有两个fragment:fragment01和fragment02,我们现在从fragment01的界面跳到fragment02,然后按back键,发现程序是直接退出了,而不是返回到fragment01。 如果现在想实现以下功能:从fragment01的界面跳到fragment02,然后按back键,会返回到fragment01,就需要加入回退栈了,fragmenttransaction中提供了一个 addtobackstack()方法,可以将一个事务添加到返回栈中。
1. transaction.add(r.id.right, rightfragment);
2. transaction.addtobackstack(null);
我们在事务提交之前调用了fragmenttransaction的addtobackstack()方法,它可以接受一个名字用于描述返回栈的状态,一般传入null即可。
下面是fragment程序的示例
三.静态显示fragment碎片页面,并进行两碎片页面的数据通信
这里左边四个按钮是一个碎片布局,右边的碎片布局是一个listview显示数据。
程序运行后效果:
数据交换的后的情况:
设计代码:
布局文件比较简单,所以先展示布局文件。再展示java代码设计。
(一)布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <fragment android:id="@+id/main_frag1" android:name="com.example.xmlfragment.fragmenta" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> <fragment android:id="@+id/main_frag2" android:name="com.example.xmlfragment.fragmentb" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" /> </linearlayout>
这里设计两个静态的碎片文件,使用的是小写的fragment标签,里面必须要有name和id(或tag)属性。
(二)第一个碎片页面包含的布局文件fragment_a.xml
<?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" android:orientation="vertical"> <button android:id="@+id/music" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="音乐" /> <button android:id="@+id/sports" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="体育" /> <button android:id="@+id/arts" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="美术" /> <button android:id="@+id/dance" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="舞蹈" /> </linearlayout>
(三)第二个碎片页面包含的布局文件fragment_b.xml
<?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" android:orientation="vertical"> <listview android:id="@+id/frag2_lv" android:layout_width="match_parent" android:layout_height="match_parent" /> </linearlayout>
(四)第一个碎片页面的java代码设计:
package com.example.xmlfragment; import android.os.bundle; import android.support.annotation.nullable; import android.support.v4.app.fragment; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.button; import android.widget.toast; /** * 碎片页面a的显示,这里显示四个按钮 */ public class fragmenta extends fragment implements view.onclicklistener { //布局中的控件 button music; button sports; button arts; button dance; /** * oncreate执行之后执行的方法,一般用于显示视图 */ @override public view oncreateview(layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { //不要super实现的方法,它是返回空值的 return view.inflate(getactivity(), r.layout.fragment_a, null); } //这个方法不在fragment的生命周期里面 //但是它会在oncreateview后面执行,里面的参数view就是上面传入的view对象 @override public void onviewcreated(view view, @nullable bundle savedinstancestate) { super.onviewcreated(view, savedinstancestate); //实例化布局上的控件 music = (button) view.findviewbyid(r.id.music); sports = (button) view.findviewbyid(r.id.sports); arts = (button) view.findviewbyid(r.id.arts); dance = (button) view.findviewbyid(r.id.dance); //给控件添加监听事件 music.setonclicklistener(this); sports.setonclicklistener(this); arts.setonclicklistener(this); dance.setonclicklistener(this); } //实现监听的方法 @override public void onclick(view v) { //获取页面碎片b的对象,来对页面碎片b进行操作,这里不能使用new的方法来创建对象 fragmentb fragmentb = (fragmentb) getactivity().getsupportfragmentmanager().findfragmentbyid(r.id.main_frag2); switch (v.getid()) { case r.id.music: fragmentb.list.add(0, "音乐");//给页面b的集合中添加数据 toast.maketext(getactivity(), "music", toast.length_short).show(); break; case r.id.sports: fragmentb.list.add(0, "运动");//给页面b的集合中添加数据 toast.maketext(getactivity(), "sports", toast.length_short).show(); break; case r.id.arts: fragmentb.list.add(0, "艺术");//给页面b的集合中添加数据 toast.maketext(getactivity(), "arts", toast.length_short).show(); break; case r.id.dance: fragmentb.list.add(0, "跳舞");//给页面b的集合中添加数据 toast.maketext(getactivity(), "dance", toast.length_short).show(); break; } fragmentb.adapter.notifydatasetchanged();//刷新页面b的数据 } }
(五)第二个碎片页面的java代码设计
package com.example.xmlfragment; import android.os.bundle; import android.support.annotation.nullable; import android.support.v4.app.fragment; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.adapterview; import android.widget.arrayadapter; import android.widget.listview; import java.util.arraylist; import java.util.list; /** * 碎片页面b的显示,这里显示一个listview数据 */ public class fragmentb extends fragment implements adapterview.onitemclicklistener { //定义集合、适配器、listview list<string> list; arrayadapter<string> adapter; listview listview; //数据源的其中一个字符串变量 string name = "music"; /** * 最先执行,一般是处理于界面无关的数据 */ @override public void oncreate(@nullable bundle savedinstancestate) { super.oncreate(savedinstancestate); //实例化list集合 list = new arraylist<>(); //实例化listview //添加数据源 for (int i = 1; i <= 100; i++) { list.add(name + i); } //实例化adapter对象 adapter = new arrayadapter<string>(getactivity(), android.r.layout.simple_list_item_activated_1, list); } /** * oncreate执行之后执行的方法,一般用于显示视图 */ @override public view oncreateview(layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { //不要super实现的方法,它是返回空值的 return view.inflate(getactivity(), r.layout.fragment_b, null); } /*** * fragment创建前最后执行的方法 * 加载视图上面显示的数据和默认设置 */ @override public void onviewcreated(view view, @nullable bundle savedinstancestate) { super.onviewcreated(view, savedinstancestate); //这里的view上面加载的listview listview = (listview) view.findviewbyid(r.id.frag2_lv); //给listview添加适配器 listview.setadapter(adapter); //给listview添加点击的监听事件 listview.setonitemclicklistener(this); //获取碎片a的对象 fragmenta= (fragmenta) getactivity().getsupportfragmentmanager().findfragmentbyid(r.id.main_frag1); } /** * listview中点击对应的条目的回调方法 */ fragmenta fragmenta; @override public void onitemclick(adapterview<?> parent, view view, int position, long id) { //这里修改页面a中的第一个按钮的文本 //点击listview哪个条目,这个条目的文本都会显示在第一个按钮上(实现fragment之间的通信) fragmenta.music.settext(list.get(position)); } }
(六)主方法类的java代码:
package com.example.xmlfragment; import android.support.v7.app.appcompatactivity; import android.os.bundle; public class mainactivity extends appcompatactivity { @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); } }
可以看到主方法类里面什么都不用设计,只需要把布局显示出来就可以了,这里全部的处理都是在fragment碎片中做好了,这就是碎片的好处。
四.动态创建碎片的示例
程序运行后的界面:
点击某个按钮,选择显示对应的碎片页面
代码设计:
(一)布局文件设计activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <relativelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.fragment.mainactivity"> <radiogroup android:background="@drawable/tab_bg" android:id="@+id/main_rg" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignparentbottom="true" android:orientation="horizontal"> <radiobutton android:id="@+id/main_rb1" style="@style/buttonstyle" android:drawabletop="@drawable/contact" android:textcolor="@color/mytextcolors" android:text="联系人" /> <radiobutton android:id="@+id/main_rb2" style="@style/buttonstyle" android:drawabletop="@drawable/message" android:textcolor="@color/mytextcolors" android:text="消息" /> <radiobutton android:id="@+id/main_rb3" style="@style/buttonstyle" android:drawabletop="@drawable/news" android:textcolor="@color/mytextcolors" android:text="动态" /> <radiobutton android:id="@+id/main_rb4" style="@style/buttonstyle" android:drawabletop="@drawable/setting" android:textcolor="@color/mytextcolors" android:text="设置" /> </radiogroup> <framelayout android:id="@+id/main_fl" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@id/main_rg"/> </relativelayout>
这里动态显示的碎片页面在先设置一个framelayout布局容器来存放碎片页面。
下面简单设计碎片页面。
(二)联系人页面碎片
package com.example.fragment; import android.os.bundle; import android.support.annotation.nullable; import android.support.v4.app.fragment; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.textview; /** *这是联系人碎片页面 */ public class contactfragment extends fragment { @override public view oncreateview(layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { textview textview = new textview(getactivity()); textview.settext("这是联系人页面"); textview.settextsize(30); return textview; } }
(三)消息页面碎片
package com.example.fragment; import android.os.bundle; import android.support.annotation.nullable; import android.support.v4.app.fragment; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.textview; /** * 这是消息页面的碎片 */ public class messagefragment extends fragment { @override public view oncreateview(layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { textview textview = new textview(getactivity()); textview.settext("这是消息页面"); textview.settextsize(30); return textview; } }
(四)动态页面碎片
package com.example.fragment; import android.os.bundle; import android.support.annotation.nullable; import android.support.v4.app.fragment; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.textview; /** *这是动态页面的碎片 */ public class newsfragment extends fragment { @override public view oncreateview(layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { textview textview = new textview(getactivity()); textview.settext("这是动态页面"); textview.settextsize(30); return textview; } }
(五)动态页面碎片
package com.example.fragment; import android.os.bundle; import android.support.annotation.nullable; import android.support.v4.app.fragment; import android.view.layoutinflater; import android.view.view; import android.view.viewgroup; import android.widget.textview; /** *这是动态页面的碎片 */ public class newsfragment extends fragment { @override public view oncreateview(layoutinflater inflater, @nullable viewgroup container, @nullable bundle savedinstancestate) { textview textview = new textview(getactivity()); textview.settext("这是动态页面"); textview.settextsize(30); return textview; } }
上面就是四个碎片页面的设计,都是比较简单的页面设计,其实也是可以像activity一样设计成一个很复杂页面的显示。
(六)主方法类,重点理解
package com.example.fragment; import android.os.bundle; import android.support.v4.app.fragment; import android.support.v4.app.fragmenttransaction; import android.support.v7.app.appcompatactivity; import android.widget.framelayout; import android.widget.radiogroup; public class mainactivity extends appcompatactivity implements radiogroup.oncheckedchangelistener { //定义布局内的控件 radiogroup radiogroup; framelayout framelayout; //定义四个存放碎片的数组 fragment[] fragment = new fragment[4]; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); initview(); radiogroup.check(r.id.main_rb1); //显示第一个碎片页面 showfragment(0); } /** * 显示碎片页面的方法 * 这里是要重点理解的地方 * 这里要添加和移除的操作都有,而且还有进行一定的判断 */ //定义一个当前点击的游标值,默认是-1,说明还没有点 int currentindex = -1; private void showfragment(int i) { //如果点击的页面是刚才显示的页面,就什么都不做 if (i == currentindex) { return; } //处理碎片,显示、移除等等 //这里要用碎片的事务来完成 fragmenttransaction transaction = getsupportfragmentmanager().begintransaction(); //如果用户打开已经打开过一个fragment页面,再打开其他页面后,要先把原来的页面移除 if (currentindex != -1) { //移除碎片 transaction.hide(fragment[currentindex]); } //显示新的碎片 if (fragment[i] == null) { //创建碎片 createfragment(i); //使用事务显示碎片 //第一个参数是碎片要显示的布局的位置的id号 //第二个参数是显示的碎片的对象 transaction.add(r.id.main_fl, fragment[i]); } else { //如果碎片曾经显示过就显示出来就可以了 transaction.show(fragment[i]); // transaction.addtobackstack(null); } //保存用户点击的游标值 currentindex = i; //最后提交事务,把碎片放置到位 transaction.commit(); } //初始化数据 private void initview() { //实例化数据 radiogroup = (radiogroup) findviewbyid(r.id.main_rg); framelayout = (framelayout) findviewbyid(r.id.main_fl); //给groupbutton设置监听事件 radiogroup.setoncheckedchangelistener(this); } /** * 按钮选择后触发的方法 */ @override public void oncheckedchanged(radiogroup group, int checkedid) { //点击哪一个按钮就显示哪一个碎片 //这里的checkedid不是0、1、2、3这种数值,而是布局里面对应的控件的id值 switch (checkedid) { case r.id.main_rb1: showfragment(0); break; case r.id.main_rb2: showfragment(1); break; case r.id.main_rb3: showfragment(2); break; case r.id.main_rb4: showfragment(3); break; } } /** * 创建碎片页面对象的方法 */ private void createfragment(int i) { //如果碎片是第一次点开,就要创建碎片 switch (i) { case 0: fragment[i] = new contactfragment(); break; case 1: fragment[i] = new messagefragment(); break; case 2: fragment[i] = new newsfragment(); break; case 3: fragment[i] = new settingfragment(); break; } } }
程序运行后就可以显示界面了。这就是动态创建碎片的示例。
五.动态创建碎片和静态创建碎片的对比:
很多人都会有疑问:动态创建和静态创建碎片有什么区别?
其实主要是应用场合的区别,并且我们也要知道它们的创建的区别。
(一)碎片静态创建和动态创建的区别1.静态创建碎片
在布局内创建fragment标签,并且标签内有属性:name和id(或tag),其中id或tag是一个唯一标识,是用来找到这个碎片对象用的;而name是继承了fragment的自定义类,使用包名+类名设置。
静态创建的碎片一旦创建它就在这个activity页面的固定位置了。
2.动态创建碎片
要在布局文件内先创建层布局容器标签framelayout,动态创建的碎片页面都是显示在这个容器里面的。这里控制碎片改变是在所依赖的activity页面的代码当中来控制。都是通过事务来显示或隐藏碎片,达到碎片切换的效果。
(二)静态碎片和动态碎片的应用场合1.静态碎片的应用场合
静态碎片的应用场合是多个页面都会出现这种布局,并且它的事件处理是一样的。
比如下面两个页面:
上面两不同的页面中,最下方的显示都是一样的,并且点击某个按钮跳转到的页面都是一样的,这时就需要用静态的碎片。
使用方法:只要把这个静态标签的fragment放到这两页面的底部就可以了。
对于上面两个页面但是如果你不用使用碎片,就需要在两个页面都设置这几个控件,并且它们的点击事件的处理,都要在两个页面的activity中重新做。这就降低了代码的复用性。
静态碎片是固定的,但是它的事件处理都是已经写好了的,都在自定义的fragment类中,你使用的使用只要复制静态的xml代码就可以了。
在开发中,如果是多个页面有相同的一些小布局,这时使用静态碎片就非常必要了。
2.动态碎片的应用场合
动态碎片的应用场合应该是比较容易就看出来的,就是某一个页面,通过几个按钮实现页面中局部画面的切换。
最常见的应用场合:
这里在同一个activity中可以显示多个页面,并且页面之间可以实现简单的切换,这就是动态创建碎片的应用。
在实际开发中,上面的页面还要实现左右滑动来切换页面,这就需要viewpager,使用viewpager能非常方便的实现页面的切换,并且不需要事务处理。这个知识点后面总结。
最后简单说一下:
其实碎片就像一个activity,它也是可以显示很多页面数据,并且可以实现页面的跳转,数据的传递。但是它是不用在androidmanifest中注册的。
碎片页面的跳转到其他activity页面也是使用startactivity或startactivityforresult。
数据传递也是可以通过intent来携带数据。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
下一篇: 一个Java配置文件加密解密工具类分享