引入Fragment的初衷是为了适应大屏幕的平板电脑 。由于平板电脑很大可以容纳更多的控件。所以使用手机的布局就有些不合适了。所以在Android3.0以前平板电脑往往需要一个单独的版本。但是有了Fragment就可以在一个版本中分别匹配手机和平板。如下图所示
Fragment的生命周期
Fragment必须嵌入到Activity中使用,因此虽然Fragment也拥有自己的生命周期,但Fragment的生命周期会受到它所在的Activity生命周期控制。
与Activity类似的是,Fragment也存在如下的状态。
- 运行状态: 当前Fragment位于前台,用户可见,可以获得焦点。
- 暂停状态: 其他Activity位于前台,该Fragment依然可见,只是不能获得焦点。
- 停止状态: 该Fragment不可见,失去焦点。
- 销毁状态: 该Fragment被完全删除,或该Fragment所在的Activity被结束。
Fragment的生命周期如下图所示
- onAttach():当该Fragment被添加到Activity时被回调。该方法只会被调用一次。
- onCreate(Bundle saveInstanceState):创建Fragment时被回调。该方法只会被调用一次。
- onCreateView():每次创建、绘制该Fragment的View组件时回调该方法,Fragment将会显示该方法返回的View组件。
- onActivityCreated():当Fragment所在的Activity被启动完成后回调该方法。
- onStart():启动Fragment时被回调。
- onResume():恢复Fragment时被回调,在onStart()方法后一定会回调onResume()方法。
- onPause():暂停Fragment时被回调。
- onStop():停止Fragment时被回调。
- onDestroyView():销毁Fragment时被回调。该方法只会被调用一次。
- onDestroy():销毁Fragment时被回调。该方法只会被调用一次。
- onDetach():将该Fragment从Activity中删除、替换完成时回调该方法、在onDestroy()方法后一定会回调onDetach()方法。该方法只会被调用一次。
首先写一个小程序,简单使用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>
然后是fragment2.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffff00" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is fragment 2"
android:textColor="#000000"
android:textSize="25sp" />
</LinearLayout>
这两个布局基本一样只是换了颜色。
然后新建类Fragment1.java 继承Fragment 我们在这个类中验证生命周期
注1 :onCreateView方法,我们在这个方法中加载fragment1.xml。
注2 :还有好多老的资料onAttach(Activity activity)方法已经弃用了,所以使用onAttach(Context context)。
public class Fragment1 extends Fragment {
public static final String TAG = "Fragment1";
@Override
public void onAttach(Context context) {
super.onAttach(activity);
Log.d(TAG, "onAttach");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
return inflater.inflate(R.layout.fragment1, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach");
}
}
然后新建类Fragment2.java 继承Fragment。这个类就比较简单了
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment2, container, false);
}
}
然后打开activity_main.xml作为主Activity的布局文件,在里面加入两个Fragment的引用,使用android: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.example.fragmentdemo.Fragment1"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment2"
android:name="com.example.fragmentdemo.Fragment2"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
最后打开MainActivity:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
最后运行如下所示:
然后打开logcat验证生命周期。
打开程序日志如下:
按home键:
红字是Android6.0的问题不用管它
在进入程序:
最后按back键:
在结合Activity生命周期以及开始的文字说明,就应该懂了。
动态添加Fragment
在上一节代码的基础上修改,打开activity_main.xml,将其中对Fragment的引用都删除,只保留最外层的LinearLayout,并给它添加一个id,因为我们要动态添加Fragment,不用在XML里添加了,删除后代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
</LinearLayou
然后打开MainActivity,修改其中的代码如下所示:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Point size = new Point();
Display display = getWindowManager().getDefaultDisplay();
display.getSize(size);
int width = size.x;
int height = size.y;
if(width > height){
Fragment1 fragment1 = new Fragment1();
getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit();
}else{
Fragment1 fragment2 = new Fragment1();
getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit();
}
}
}
首先,我们要获取屏幕的宽度和高度,然后进行判断,如果屏幕宽度大于高度就添加fragment1,如果高度大于宽度就添加fragment2。动态添加Fragment主要分为4步:
1.获取到FragmentManager,在Activity中可以直接通过getFragmentManager得到。
2.开启一个事务,通过调用beginTransaction方法开启。
3.向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例。
4.提交事务,调用commit方法提交。
运行程序, 竖屏就显示fragment1。横屏就显示fragment2。
Fragment的通信
Fragment之间进行通信
通常情况下,Activity都会包含多个Fragment,这时多个Fragment之间如何进行通信就是个非常重要的问题了。我们通过一个例子来看一下,如何在一个Fragment中去访问另一个Fragment的视图。
还是在上一节代码的基础上修改,首先打开fragment2.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:background="#ffff00" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is fragment 2"
android:textColor="#000000"
android:textSize="25sp" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get fragment1 text"
/>
</LinearLayout>
然后打开fragment1.xml,为TextView添加一个id:
<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:id="@+id/fragment1_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is fragment 1"
android:textColor="#000000"
android:textSize="25sp" />
</LinearLayout>
接着打开Fragment2.java,添加onActivityCreated方法,并处理按钮的点击事件:
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment2, container, false);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Button button = (Button) getActivity().findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TextView textView = (TextView) getActivity().findViewById(R.id.fragment1_text);
Toast.makeText(getActivity(), textView.getText(), Toast.LENGTH_LONG).show();
}
});
}
}
可以看到,在fragment2中成功获取到了fragment1中的视图,并弹出Toast。这是怎么实现的呢?主要都是通过getActivity这个方法实现的。getActivity方法可以让Fragment获取到关联的Activity,然后再调用Activity的findViewById方法,就可以获取到和这个Activity关联的其它Fragment的视图了。
Fragment与Activity通信
Fragment与Activity通信的方式有两种:
第一种:通过setArgument()传递Bundle。
在动态添加Fragment时我们通过Fragment.setArgument(Bundle bundle)的方法为Fragment传递数据。
在onAttach()方法中通过getArgument()得到一个Bundle数据。
在上一节修改程序:
修改MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Fragment1 fragment1 = new Fragment1();
Bundle arguments = new Bundle();
arguments.putString("FragmentText", "这是新的Fragment1");
fragment1.setArguments(arguments);
getFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit();
}
之后修改Fragment1.java
public class Fragment1 extends Fragment {
private TextView textView;
private String data;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment1, container, false);
}
@Override
public void onResume(){
super.onResume();
textView = (TextView) getActivity().findViewById(R.id.fragment1_text);
data = getArguments().getString("FragmentText");
textView.setText(data);
}
}
之后就可以运行就可得到
第二种: 在Fragment内部定义一个内部回调接口,在让包含该Fragment的Activity实现该回调接口,这样Fragment就可以调用该回调方法将数据传给Activity了。
示例程序:
新建left_fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#00ff00"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/first_button"
android:text="first button"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/second_button"
android:text="second button"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/third_button"
android:text="third button"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
新建right_fragment.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ff0000"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/right_text"
android:text="haha"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
新建LeftFragment.java继承Fragment
public class LeftFragment extends Fragment implements View.OnClickListener {
private MyListener myListener;
private Button firstButton;
private Button secondButton;
private Button thirdButton;
@Override
public void onAttach(Context context){
super.onAttach(context);
myListener = (MyListener) context;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState){
return inflater.inflate(R.layout.left_fragment, container, false);
}
@Override
public void onResume(){
super.onResume();
firstButton = (Button) getActivity().findViewById(R.id.first_button);
secondButton = (Button) getActivity().findViewById(R.id.second_button);
thirdButton= (Button) getActivity().findViewById(R.id.third_button);
firstButton.setOnClickListener(this);
secondButton.setOnClickListener(this);
thirdButton.setOnClickListener(this);
}
public interface MyListener{
void showMessage(int index);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.first_button:
myListener.showMessage(1);
break;
case R.id.second_button:
myListener.showMessage(2);
break;
case R.id.third_button:
myListener.showMessage(3);
break;
}
}
}
新建RightFragment.java
public class RightFragment extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) {
return inflater.inflate(R.layout.right_fragment, container, false);
}
}
修改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="horizontal">
<LinearLayout
android:orientation="vertical"
android:id="@+id/left_layout"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="match_parent">
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:id="@+id/right_layout"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="match_parent">
</LinearLayout>
</LinearLayout>
最后MainActivity.java
public class MainActivity extends Activity implements LeftFragment.MyListener {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
android.app.FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
LeftFragment leftFragment = new LeftFragment();
RightFragment rightFragment = new RightFragment();
transaction.add(R.id.left_layout, leftFragment, "leftLayout");
transaction.add(R.id.right_layout, rightFragment, "RightLayout");
transaction.commit();
}
@Override
public void onResume(){
super.onResume();
textView = (TextView)findViewById(R.id.right_text);
}
public void showMessage(int index){
switch (index){
case 1:
textView.setText("这是第一段文本");
break;
case 2:
textView.setText("这是第二段文本");
break;
case 3:
textView.setText("这是第三段文本");
break;
}
}
}
最后运行可得到结果。
参考书籍博客
《疯狂Android讲义》
Android Fragment完全解析,关于碎片你所需知道的一切
Android手机平板两不误,使用Fragment实现兼容手机和平板的程序
Android Fragment应用实战,使用碎片向ActivityGroup说再见
android中fragment与activity之间通信原理以及例子
【Android开发】之Fragment与Acitvity通信
博客内容是我阅读以上资料加上自己的理解所得,有部分摘抄自以上资料,侵删。