為了在Activity布局文件中使用Fragment我們需要四個步驟。
1.定義一個Activity,他繼承android.support.v4.app.FragmentActivity,下面是關鍵代碼
import android.support.v4.app.FragmentActivity; public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
2.定義Activity的布局文件activity_main.xml,注意fragment是小寫的,且name屬性的值必須是完全限定包名
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="fill_parent" 3 android:layout_height="fill_parent" 4 android:orientation="vertical" > 5 6 <fragment 7 android:name="com.example.v4fragmenttest.MyFragment" 8 android:id="@+id/myfragmentid" 9 android:layout_width="400px" 10 android:layout_height="500px" 11 android:layout_gravity="center" 12 /> 13 14 15 </LinearLayout>
3.自定義一個Fragment,他繼承android.support.v4.app.Fragment,關鍵代碼如下
import android.support.v4.app.Fragment; public class MyFragment extends Fragment { @Override @Nullable public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //System.out.println("Fragment onCreateView"); //System.out.println(container == null); return inflater.inflate(R.layout.fragment, container, false); } }
4.定義Fragment對應的布局文件fragment.xml(名字任意),fragment.xml和普通的布局文件是完全一樣的
1 <TextView xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="wrap_content" 3 android:layout_height="wrap_content" 4 android:layout_gravity="center" 5 android:text="這是Fragment的TextView,他是水平居中的" />
完整的源碼下載:點我下載
可以發現在布局文件中使用自定義view和和fragment是有些類似的,但是有些區別
1.自定義view在布局文件中定義是<com.xxnote.myview />,Fragment則是<fragment android:name="com.example.v4fragmenttest.MyFragment" />的形式
2.自定義view要繼承view,而自定義Fragment需要繼承android.support.v4.app.Fragment
同時也可以發現,自定義的Fragment本質就是一個繼承了android.support.v4.app.Fragment的類,在他的onCreateView里面返回一個View,這個View會替換掉Activity布局文件里面的fragment標簽
另外SDK文檔中有這么一句話
Note: Each fragment requires a unique identifier that the system can use to restore the fragment if the activity is restarted (and which you can use to capture the fragment to perform transactions, such as remove it). There are three ways to provide an ID for a fragment:
- Supply the
android:id
attribute with a unique ID. - Supply the
android:tag
attribute with a unique string. - If you provide neither of the previous two, the system uses the ID of the container view.
可見每一個Fragment都需要一個unique identifier,有三種方式可以為他提供unique identifier,
第一種是使用Fragment的android:id屬性作為unique identifier,如下:
1 <fragment 2 android:name="com.example.v4fragmenttest.MyFragment" 3 android:id="@+id/myfragmentid" 4 android:layout_width="400px" 5 android:layout_height="500px" 6 android:layout_gravity="center" 7 />
第二種是使用Fragment的android:tag屬性作為unique identifier
<fragment android:name="com.example.v4fragmenttest.MyFragment" android:tag="myfragmenttag" android:layout_width="400px" android:layout_height="500px" android:layout_gravity="center" />
第三種是如果上面兩種都沒提供,系統會使用父容器的id作為unique identifier,如下
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:id="@+id/myactivitylayout" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <fragment 8 android:name="com.example.v4fragmenttest.MyFragment" 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 /> 12 13 </LinearLayout>
上面的Fragment的unique identifier就是父容器的id,可以使用getSupportFragmentManager().findFragmentById(R.id.layoutfragment)來獲取這個Fragment。
但是試了下發現上面三種方式都沒提供的話也沒什么影響,這個應該是用代碼添加Fragment的時候才會用到,在布局文件中使用Fragment沒啥用,以后再好好研究下
下午研究了下,終於搞清楚unique identifier了,unique identifier的作用就是用來傳給FragmentManager的findFragmentByTag或者findFragmentById這兩個方法的,這兩個方法根據唯一的unique identifier來確定到底是要查找那個Fragment,那么問題來了,如果這些Fragment的unique identifier相同會出現什么情況呢?這時通過這個unique identifier查找到的Fragment是用這個unique identifier設置的最后一個Fragment,即使這個Fragment被remove了,再用這個unique identifier來查找Fragment也無法查找到其他的Fragment,只會返回null。
什么情況下會出現unique identifier相同的情形呢?一種情況是同一個布局中有多個使用父容器的id作為unique identifier的Fragment,即上面的為Fragment分配unique identifier的第三種方式,第二種情況就是通過代碼添加Fragment,如下:
1 fragmentTransaction.add(R.id.myactivitylayout, new MyFragment(), "000");
第三種情況就是第一種和第二種情況的結合了。
通過代碼添加Fragment的時候第一個參數必須是父容器的id,即上面的R.id.myactivitylayout,所以這些添加的Fragment都會有相同的unique identifier,因此我們會無法查找所有的Fragment,這時第三個參數派上了用場, fragmentTransaction.add(R.id.myactivitylayout, new MyFragment(), "000"); 這句里面的第三個參數"000"也會作為Fragment的unique identifier,查找的時候使用FragmentManage的findFragmentByTag方法就可以了。因此最好不要使用FragmentManager的findFragmentById方法來查找Fragment,而是使用FragmentManage的findFragmentByTag方法來查找Fragment。
還有一些要注意的地方就是在布局xml里面的fragment是無法刪除和replace的;對於一個fragmentTransaction只能調用一次commit()方法,再進行add、remove、replace后必須commit才生效,如果一個fragmentTransaction已經commit過一次了,那么再要進行add、remove、replace的時候就調用getSupportFragmentManager().beginTransaction()方法重新獲得一個fragmentTransaction,然后使用這個fragmentTransaction來add、remove、replace我們的Fragment,最后了commit一下就生效了;commit是異步的,因此當我們commit之后立刻查找Fragment很可能會返回null。