该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!


前言

上一篇博客我们主要总结了之前博客的知识,那么本篇我们来分析一下Fragment。Fragment又被称为“碎片”,可把它看做是一个轻量的Activity,它可以像Activity一样包含布局。Fragment通常是嵌套在Activity中使用的。

Fragment初探#

Fragment设计之初是也许是为了适配平板等大屏幕设备,在这些设备上有足够的空间同时显示两个“Activity”,使用Fragment可以让我们更加充分地利用平板的屏幕空间。面我们一起来探究下如何使用Fragment。

首先需要注意,Fragment是在3.0版本引入的,如果你使用的是3.0之前的系统,需要先加入android-support-v4支持才能使用Fragment功能。在Android Studio中这是很容易的,另请注意尽量不要用app包中的fragment,因为这个是在3.0之后才有的,支持的版本太高,在低版本中是是用不了的。

[app/build.gradle]

compile 'com.android.support:support-v4:25.3.1'

注:后面的25.3.1是android-support-v4的版本号,25表示android-support-v4的主版本,这个主版本尽量要与compileSdkVersion 25保持一致。

我们在TestApplication中新建一个包fragment专门用来测试与Fragment相关知识。

新建布局文件fragment_good.xml

<FrameLayout
android:background="@color/light_green"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Goods"
android:textStyle="bold"
android:textSize="30sp"
android:layout_gravity="center"/>
</FrameLayout>

这个布局文件十分简单,仅仅是在FrameLayout布局中间位置放置了一个TextView,注意这里的FrameLayout是一个布局,称为“帧布局”,,当我们往里面添加控件的时候,会默认把他们放到这块区域的左上角,而这种布局方式却没有任何的定位方式,所以它应用的场景并不多;帧布局的大小由控件中最大的子控件决定,如果控件的大小一样大的话,那么同一时刻就只能看到最上面的那个组件!后续添加的控件会覆盖前一个!虽然默认会将控件放置在左上角,但是我们也可以通过layout_gravity属性,指定到其他的位置!

再新建布局文件fragment_goodcar.xml

<FrameLayout
android:background="@color/light_green"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GoodCar"
android:textStyle="bold"
android:textSize="30sp"
android:layout_gravity="center"/>
</FrameLayout>

这个布局文件与上面的几乎完全一致。

那么我们的布局文件有了,我们该新建使用该布局文件的类了,这一点与Activity可以说是完全一致

新建GoodsFragment,该类继承于Fragment

public class GoodsFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//加载上面的布局文件
View view = inflater.inflate(R.layout.fragment_goods, null); return view;
}
}

可以看到Fragment的使用几乎与Activity如出一辙。下面如法炮制新建GoodCarFragment

public class GoodCarFragment extends Fragment {

    @Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//加载上面的布局文件
View view = inflater.inflate(R.layout.fragment_goodcar, null); return view;
}
}

注:其实在AndroidStudio中有更简单的办法

Fragment有了,那么怎么使用呢,Fragment一般是嵌套在Activity中使用,那么我们新建一个EasyFragmentActivity

在其布局文件中activity_easy_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false" >
<!--在这里使用fragment 标签 并指定name为上面定义的两个Fragment的全类名--> <fragment
android:id="@+id/fragment1"
android:name="com.mafeibiao.testapplication.fragment.GoodCarFragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
tools:layout="@layout/fragment_goodcar" /> <fragment
android:id="@+id/fragment2"
android:name="com.mafeibiao.testapplication.fragment.GoodsFragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"
tools:layout="@layout/fragment_goods" /> </LinearLayout>

EasyFragmentActivity的代码也极其简单,使用默认生成的就可以了

public class EasyFragmentActivity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_easy_fragment);
}
}

到这里我们运行程序就可得到如下结果

到这里我们已经会使用Fragment了,神奇不神奇?不过上面的只是最简单的,有的文章称它是静态的使用Fragment,因为我们只是在XML中引用了一下。既然有静态,那就有动态了,下面来看动态

动态添加Fragment#

稍微修改一下上面的布局文件activity_easy_fragment

<?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:baselineAligned="false"
android:orientation="vertical"> <!--删除了上面两个fragment标签,加入了一个id为main_layout的FrameLayout布局-->
<FrameLayout
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </LinearLayout>

我们的Activity代码也做了相应改变

public class EasyFragmentActivity extends AppCompatActivity {

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_easy_fragment);
Display display = getWindowManager().getDefaultDisplay();
if (display.getWidth() > display.getHeight()) {
GoodCarFragment fragment1 = new GoodCarFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1).commit();
} else {
GoodsFragment fragment2 = new GoodsFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2).commit();
}
}
}

首先,我们要获取屏幕的宽度和高度,然后进行判断,如果屏幕宽度大于高度就添加fragment1,如果高度大于宽度就添加fragment2。动态添加Fragment主要分为4步:

  1. 获取到FragmentManager,在Activity中可以直接通过getSupportFragmentManager得到。在这里不推荐使用getFragmentManager得到FragmentManager,而使用v4包下getSupportFragmentManager。

  2. 开启一个事务,通过调用beginTransaction方法开启。

  3. 向容器内加入Fragment,一般使用replace方法实现,需要传入容器的id和Fragment的实例。replace方法有几个重载方法,在这里使用的是

     FragmentTransaction replace(@IdRes int containerViewId, Fragment fragment);

    该方法第1个参数是容器ID,即容纳Fragment的容器ID,这里传入的是在activity_easy_fragment声明的id为main_layout的FrameLayout布局,第2个参数即自定义的Fragment对象。整个函数的意思就是第2个参数中指定的Fragment嵌入到第一个参数指定的布局容器中。这一点我们可以通过Hierarchy View验证。

  4. 提交事务,调用commit方法提交。

Fragment生命周期#

和Activity一样,Fragment 也有自己的生命周期,理解Fragment的生命周期非常重要,我们通过代码的方式来瞧一瞧Fragment的生命周期是什么样的:

public class GoodsFragment extends Fragment {
private static String TAG= GoodsFragment.class.getSimpleName();
@Override
public void onAttach(Context context) {
super.onAttach(context);
Log.d(TAG,"onAttach");
} @Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG,"onCreateView");
View view = inflater.inflate(R.layout.fragment_goods, null); return view;
} @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG,"onViewCreated");
} @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG,"onActivityCreated");
} @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
} @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");
}
}

我的神呀,Fragment的生命周期函数真是多呀。Fragment的生命周期函数比Activity的生命周期函数要多不少。不过要理解Fragment的生命周期并不难。因为我们前面的文章中已经分析了Activity的生命周期。把EasyFragmentActivity也稍微修改一下,加入生命周期函数

public class EasyFragmentActivity extends AppCompatActivity {
private static String TAG= EasyFragmentActivity.class.getSimpleName(); @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
setContentView(R.layout.activity_easy_fragment);
Display display = getWindowManager().getDefaultDisplay();
if (display.getWidth() > display.getHeight()) {
GoodCarFragment fragment1 = new GoodCarFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment1,Constant.GOODCAR_FRAGMENT_FLAG).commit();
} else {
GoodsFragment fragment2 = new GoodsFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.main_layout, fragment2,Constant.GOODS_FRAGMENT_FLAG).commit();
}
} @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
protected void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy");
}
}

运行一下



我们在EasyFragmentActivity中onCreate函数中动态加载了Fragment,看Fragment的生命周期的回调顺序,我们来解释一下

  • onAttach方法:Fragment和Activity建立关联的时候调用。
  • onCreate方法:与Activity一样,Fragment被创建的的时候调用。
  • onCreateView方法:为Fragment加载布局时调用。
  • onActivityCreated方法:当Activity中的onCreate方法执行完后调用。
  • onstart方法: 与Activity一样,Fragment开始可见但不可交互的时候调用。
  • onResume方法:与Activity一样,是当该Fragment与用户能进行交互时被执行,用户可以获得Fragment的焦点,能够与用户交互。

按下home键之后

  • onPause方法 与Activity一样,通常是当前的Fragment被暂停了。
  • onStop方法 与Activity一样,一般是用户按下了home键,Fragment变为后台后,之后用户再切换回这个Fragment就会调用onRestart()而后调用onStart()

按下back键之后

  • onDestroyView方法:Fragment中的布局被移除时调用。
  • onDetach方法:Fragment和Activity解除关联的时候调用。

Fragment的生命周期与Activity的生命周期很类似,Fragment通常也是嵌套在Activity中使用,所以的Fragment的生命周期跟当前Activity的状态息息相关。

Fragment通信#

一个宿主Activity可能包含2个以上的Fragment,那么Fragment之间以及Fragment与宿主Activity之间的通信问题是我们不得不解决的问题。

总结来说,基本有以下几种方法:

Fragment直接调用Activity中的public方法##

我们可以直接在Fragment中调用Activity中的公开方法,如下:

public class GoodsFragment extends Fragment {

    @Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG,"onCreateView");
View view = inflater.inflate(R.layout.fragment_goods, null);
//通过getActivity()获取与该Fragment相关联的Activity
((EasyFragmentActivity)getActivity()).show();
return view;
}
}

show方法是定义在EasyFragmentActivity中的public方法

public class EasyFragmentActivity extends AppCompatActivity {

public void show(){

Log.d(TAG,"show");

}

}

直接在一个Fragment中调用另外一个Fragment中的方法##

我们可以直接在一个Fragment中调用另外一个Fragment的公开方法,前提是要先拿到另外一个Fragment的实例,我们先来看看怎样拿到实例:

public class GoodsFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG,"onCreateView");
View view = inflater.inflate(R.layout.fragment_goods, null);
// ((EasyFragmentActivity)getActivity()).show();
GoodCarFragment goodCarFragment = (GoodCarFragment) getActivity().getSupportFragmentManager().findFragmentByTag("GOODCAR_FRAGMENT_FLAG");
goodCarFragment.show();
return view;
}
}

上面的findFragmentByTag是FragmentManager类的方法,是根据Tag找到对应的Fragment,这个Tag是在EasyFragmentActivity中添加Fragment时指定的。

使用接口(推荐使用)##

先定义一个接口,我们在接口中定义我们需要的功能

public interface IShow {
public void show();
}

然后让宿主Activity实现该接口

public class EasyFragmentActivity extends AppCompatActivity implements IShow {
@Override
public void show(){
Log.d(TAG,"show");
}
}

然后在Fragment中定义一个该接口的成员变量,并在Fragment onAttach方法中实例化该接口

public class GoodsFragment extends Fragment {

	private IShow mCallback;

    @Override
public void onAttach(Context context) {
super.onAttach(context);
if (context != null) {
mCallback = (IShow) context;
}
Log.d(TAG,"onAttach");
} @Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG,"onCreateView");
View view = inflater.inflate(R.layout.fragment_goods, null);
//然后在这里就可以使用了
mCallback.show();
return view;
}
}

这种方案应该是既能达到Fragment复用,又能达到很好的可维护性,并且性能也是杠杠的,所以说推荐使用。

其他方案##

在上面的方案之外,还存在Hanler、广播等通用的通信解决方案,我这里就不进行具体介绍了。因为前面我们已经详细分析了Handler的消息机制,至于广播,我会在以后的文章中分析。


本篇总结

我们本篇详细分析了Android Fragment相关知识,灵活运用Fragment可以让你得到意向不到的效果,我们使用Fragment降低耦合度,但是可能也增加了Fragment之间以及Fragment与宿主Activity之间的通信成本,不过这点成本对于程序的易扩展性来说是十分值得的。

参考文章:

http://blog.csdn.net/guolin_blog/article/details/8881711


下篇预告

下一篇文章是呢是Fragment的最佳实践,读者敬请期待哦。


此致,敬礼

Android开发之漫漫长途 XII——Fragment详解的更多相关文章

  1. Android开发之漫漫长途 XIII——Fragment最佳实践

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  2. Android开发之漫漫长途 Fragment番外篇——TabLayout+ViewPager+Fragment

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  3. Android开发之漫漫长途 IX——彻底掌握Binder

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  4. Android开发之漫漫长途 XI——从I到X的小结

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  5. Android开发之漫漫长途 XVI——ListView与RecyclerView项目实战

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  6. Android开发之漫漫长途 XIX——HTTP

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  7. Android开发之漫漫长途 Ⅰ——Android系统的创世之初以及Activity的生命周期

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>中的相关知识,再次表示该书 ...

  8. Android开发之漫漫长途 Ⅱ——Activity的显示之Window和View(2)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  9. Android开发之漫漫长途 Ⅱ——Activity的显示之Window和View(1)

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

随机推荐

  1. shell 踩坑记

    变量赋值时,等号两边不能有空格: 在判断表达式中,不论是 [ -n "$1" ] 还是 [ -f  "$1" ] 都要在变量两侧加上双引号: 在使用与或非判断式 ...

  2. 关于《Web接口开发与自动化测试--基于Python语言》

    关于封面logo 首先,你会被书封上面logo吸引,这么炫酷?双蛇杖?嗯,这是Requests的新logo. 旧的logo是一只乌龟. 新logo是双蛇杖: 看到新logo我首先想到的是 火爆全网页游 ...

  3. ogg12c_静默安装

    1.上传压缩包:123010_fbo_ggs_Linux_x64_shiphome.zip 2.解压: unzip 123010_fbo_ggs_Linux_x64_shiphome.zip 3.配置 ...

  4. css中使用if条件在各大浏览器(IE6\IE7\IE8)中hack方法解决教程

    一个滚动代码,其他浏览器都滚的好好的,就IE出现错误,DIV+CSS if条件hack,这里DIVCSS5为大家介绍针对各大浏览器(IE6\IE7\IE8)中使用if条件hack方法教程,DIV CS ...

  5. Linux文件的复制、删除和移动命令

    cp命令  功能:将给出的文件或目录拷贝到另一文件或目录中,就如同DOS下的copy命令一样,功能非常强大.  语法:cp [选项] 源文件或目录 目标文件或目录  说明:该命令把指定的源文件复制到目 ...

  6. unittest单元测试框架详解

    unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动化测试用例的开发与执行,该测试框架可组织执行测试用例,并且提供了丰富的断言方法,判断测试用例是否通过,最终生成测试结果.今天笔者 ...

  7. webstorm方向键与电脑快捷键冲突

    1.桌面上,单击鼠标右键,选择"图形属性" 2.单击"选项和支持" 3.在图示框框中,修改电脑上的快捷键  

  8. Nginx 解决WebApi跨域二次请求以及Vue单页面问题

    一.前言 由于项目是前后端分离,API接口与Web前端 部署在不同站点当中,因此在前文当中WebApi Ajax 跨域请求解决方法(CORS实现)使用跨域处理方式处理而不用Jsonp的方式. 但是在一 ...

  9. 在阿里云 ECS 搭建 nginx https nodejs 环境(三、nodejs)

    在阿里云 ECS 搭建 nginx https nodejs 环境(三.nodejs) 在阿里云 ECS 搭建 nginx https nodejs 环境(三.nodejs) 第一步 指定版本源 执行 ...

  10. Vue + iView + vuex + vee-validate 完整项目总结

    build/*.js config/*.js src/旧代码文件夹 部门最近的一个新项目启动,很幸运由我来主导整个前端部分的技术选型和整体架构,项目工作量很大,但是却没有足够的人手,只有三个连CSS都 ...