该文章是一个系列文章,是本人在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. 移动端的一些常用meta标签

    <!DOCTYPE html> <!-- 使用 HTML5 doctype,不区分大小写 --> <html lang="zh-cmn-Hans"&g ...

  2. pc端常用导航

    应为经常要写网站,导航部分基本一样,没必要每次都写一遍,下次直接来复制: HTML: <nav> <div class="clearfix container"& ...

  3. 记录优雅的pythonic代码

    记录平时学习中接触到的和网上看到的一些pythonic的方法,只为日后查询时候方便. 1.列表推导式: seq_list=[1,2,3,4,5] new_list=[i *2 for i in seq ...

  4. Tomcat 到底依赖JRE还是JDK

    Tomcat 6.0 以上可以不再依赖JDK运行,直接使用JRE即可,但Tomcat 5.5以下,是必须安装JDK的. 这主要是由于,Tomcat 5.5及以下版本主要是依赖JDK去编译JSP文件生成 ...

  5. Hibernate学习笔记(4)---hibernate的核心接口

    Configuration类 该类主要是读取配置文件,启动hibernate,并负责管理hibernate的配置信息,一个程序只创建一个Configuration对象. Configuration类操 ...

  6. Mysql:执行source sql脚本时,出现:error 2

    Centos下部署mysql: 1.yum -y install mysql*; 2.service mysqld start; 3.chkconfig mysqld on; 4.设置用户名和密码:m ...

  7. js 数组的常用方法

    pop,push,reverse,shift,sort,splice,unshift 会改变原数组 join,concat,indexOf,lastIndexOf,slice,toString 不会改 ...

  8. Java架构师学习路线

    Java架构师,首先要是一个高级java攻城狮,熟练使用各种框架,并知道它们实现的原理.jvm虚拟机原理.调优,懂得jvm能让你写出性能更好的代码;池技术,什么对象池,连接池,线程池--   Java ...

  9. 详说 Navicat for MySQL 快捷键

    详说 Navicat for MySQL 快捷键: Navicat 主窗口 Navicat 主窗口快捷键 常规 Navicat 常规快捷键 表设计器 Navicat 表设计器快捷键 表查看器 Navi ...

  10. mvc中传入字典的模型项的类型问题

    刚项目一直报这个错,找了一会发现忘了给他模型项了,我把这个小问题纪录下来,希望你们别犯这个小错