详解Android开发中Fragment的使用
前言
学习Java和Android将近一年的时间了,期间的成果应该就是独立完成了一个Android客户端,并且保证了其在主线版本的稳定性。期间遇到了很多坑,也跟着师兄学到了很多Android知识。但是人总是要拥抱变化,不能让自己太安逸,虽然有不舍,但是我已经证明了自己的学习能力,下一步就是开始做RomPorting了。这里总结一下之前项目中用到最多的Fragment。
Fragment简介
Fragment可以理解成Activity中用户界面的一个行为或者一部分,它必须被嵌套在Activity中。但是一个Fragment有它自己独立的xml布局文件,并且具有良好的封装性,因此特殊情况下Fragment可以很容易用Activity来进行替换。
创建Fragment
创建一个Fragment和创建Activity类似,需要实现XML布局文件和JavaClass。
XML布局文件和其他布局文件都一样,例如如下所示的布局文件(fragment_layout.xml):
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/testview"/> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button"/> </LinearLayout>
Java代码中,一般情况下可以根据需要实现Fragment以下几个生命周期方法:
1.onAttach():当Fragment依附于activity时被调用,可以在该方法中获取activity句柄,从而实现Fragment和activity之间的通信。
2.onCreate():对Fragment做初始化。
3.onCreateView():在第一次为Fragment绘制用户界面时系统会调用此方法。
4.onActivityCreated():在宿主ActivityonCreate函数执行完成之后被调用,可以在这个方法里进行Fragment自己的widget实例化和业务逻辑处理。
5.onDestoryView():当Fragment开始被销毁时调用。
6.onStart():当Fragment可见时被调用。
还有许多其他用以操纵Fragment生命周期中各个阶段的回调函数,大家可自行Google学习。
Fragment生命周期
每一个Fragment都有自己的一套生命周期回调方法和处理自己的用户输入事件。对应的生命周期如下图所示:
在Activity中加入Fragment
首先,需要确保Acitivity支持Fragment,因此Activity通常需要继承自FragmentActivity。在Activity中添加Fragment通常有两种方法:静态的和动态的。
静态方法
直接在Activity的XML布局文件中加入Fragment,如下所示:
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:baselineAligned="false" android:orientation="horizontal"> <fragment android:id="@+id/first" android:name="com.example.FristFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/> <fragment android:id="@+id/second" android:name="com.example.SecondFragment" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1"/> </LinearLayout>
<fragment>中的android:name属性指定了布局中实例化的fragment类
当系统创建Activity布局时,它实例化了布局文件中指定的每一个Fragment,并且为它们调用onCreateView()函数,以获取每一个fragment的布局。系统直接在<fragment>元素位置插入fragment返回的view
注意:每个fragment都需要一个唯一的标识,如果重启activity,系统可用来恢复fragment(并且用来捕捉fragment的事务处理,例如移除)。为了Fragment提供ID有三种方法:
- 用android:id属性提供一个唯一的标识
- 用android:tag属性提供一个唯一的字符串
- 如果上述两个属性都没有,系统会使用其容器视图的ID
动态方法
使用FragmentTranscation。可以使用FragmentTranscation的API来对Activity的Fragment进行操作(例如添加,移除,或者替换Fragment)。参考代码如下:
FragmentManagerfragmentManager=getFragmentManager() FragmentTransactionfragmentTransaction=fragmentManager.beginTransaction(); ExampleFragmentfragment=newExampleFragment(); fragmentTransaction.add(R.id.fragment_container,fragment); fragmentTransaction.commit();
传入add()函数的第一个参数是Fragment被放置的ViewGroup,它由资源ID(resourceID)指定,第二个参数就是要添加的fragment。一旦通过FragmentTranscation做了更改,都应当使用commit()视变化生效。
Fragments通信
Fragments之间不应该直接进行通信,它们之间的交互应该通过宿主Activity进行。有三种Fragment和Acitivity交互的方法:
1.Activity创建带参数的Fragment。
2.Activity中保持了Fragment的对象句柄,可通过句柄直接调用该Fragment的public方法。
3.Fragment可在onAttach函数中获取定义的listener句柄。
创建带参数的Fragment
在某些特定的情况下,Fragment可能需要特定的参数来进行初始化。由于Fragment必须只有一个无参构造函数,因此可以考虑使用静态的newInstance方法来创建带参数的Fragment。示例代码如下:
importandroid.os.Bundle; importandroid.support.v4.app.Fragment; publicclassTestFragmentextendsFragment{ publicstaticTestFragmentnewInstance(intnum,Stringtitle){ TestFragmentfragment=newTestFragment(); Bundleargs=newBundle(); args.putInt("num",num); args.putString("title",title); fragment.setArguments(args); returnfragment; } @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); intnum=getArguments().getInt("num",0); Stringtitle=getArguments().getString("title",""); } }
你可以在Activity里,简单的加载一个带参数的Fragment:
FragmentTransactionft=getSupportFragmentManager().beginTransaction(); TestFragmentfragment=TestFragment.newInstance(5,"fragmenttitle"); ft.replace(R.id.placeholder,fragment); ft.commit();
调用Fragment的方法
因为Activity可以获取嵌入的Fragment的句柄,因此可以直接通过Fragment句柄调用该方法。
publicclassTestFragmentextendsFragment{ publicvoiddoSomething(Stringparam){ //dosomethinginfragment } }
在Activity中,可以直接通过Fragment的对象句柄调用该方法:
publicclassMainActivityextendsFragmentActivity{ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); TestFragmenttestFragment=newTestFragment(); testFragment.doSomething("someparam"); } }
FragmentListener
如果Fragment需要共享事件给Activity,则需要利用这个方法。Fragment中定义一个接口,并且由Activity来实现这个接口。在onAttach()方法中将实现了这个接口的Activity获得到。
在Fragment中定义接口代码如下:
importandroid.support.v4.app.Fragment; publicclassMyListFragmentextendsFragment{ //... //Definethelisteneroftheinterfacetype //listeneristheactivityitself privateOnItemSelectedListenerlistener; //Definetheeventsthatthefragmentwillusetocommunicate publicinterfaceOnItemSelectedListener{ publicvoidonRssItemSelected(Stringlink); } //Storethelistener(activity)thatwillhaveeventsfiredoncethefragmentisattached @Override publicvoidonAttach(Activityactivity){ super.onAttach(activity); if(activityinstanceofOnItemSelectedListener){ listener=(OnItemSelectedListener)activity; }else{ thrownewClassCastException(activity.toString() +"mustimplementMyListFragment.OnItemSelectedListener"); } } //Nowwecanfiretheeventwhentheuserselectssomethinginthefragment publicvoidonSomeClick(Viewv){ listener.onRssItemSelected("somelink"); } }
在Activity中实现这个接口:
importandroid.support.v4.app.FragmentActivity; publicclassRssfeedActivityextendsFragmentActivityimplements MyListFragment.OnItemSelectedListener{ DetailFragmentfragment; @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_rssfeed); fragment=(DetailFragment)getSupportFragmentManager() .findFragmentById(R.id.detailFragment); } //Nowwecandefinetheactiontotakeintheactivitywhenthefragmenteventfires @Override publicvoidonRssItemSelected(Stringlink){ if(fragment!=null&&fragment.isInLayout()){ fragment.setText(link); } } }