前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android中最最常用—Fragment基础篇最详解

Android中最最常用—Fragment基础篇最详解

作者头像
下码看花
发布2019-09-18 16:45:17
1.9K0
发布2019-09-18 16:45:17
举报
文章被收录于专栏:AndroidStudio初识

前言

各位花粉好久不见,本人还没有从假期综合征中恢复状态,但是想到还有你们在,所以我的动力就立刻被充满啦!一直跟着我们学习的花粉们肯定会好奇, Activity虽然已经学会了,但是还是无法实现像微信或者某东、某宝一样做到切换展示的样式,或者有的小伙伴是在点击时手动去显示和隐藏不同的布局页面,可是根本无法实现所想要达到的交互效果,本期我们为大家重点介绍一下如何实现类似效果。

我们本期的主角— Fragment作为 Android最基本也最重要的基础概念之一,在我们日常的 Android开发中经常会用到。本文就从 Fragment的诞生开始,详细的介绍下 Fragment的各个方面,包括其定义、使用、通信、生命周期等!

概念

Fragment被称为碎片,是 Android3.0(API 11)开始引入的组件,其初衷是便于大屏UI、平板电脑的设计和实现,现已广泛用于移动设备的开发中。

为了兼容低版本, support-v4库中也开发了一套 FragmentAPI,最低兼容Android 1.6,这也是我们在开发中建议使用的。

Fragment官方定义如下:

代码语言:javascript
复制
A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove while the activity is running.

由以上定义可以看出:

  • Fragment是依赖于 Activity的,不能独立存在。
  • 一个 Activity里可以有多个 Fragment
  • 一个 Fragment可以被多个 Activity重用。
  • Fragment有自己的生命周期,并能接收输入事件。
  • 我们能在 Activity运行时动态地添加或删除 Fragment

基本使用

介绍 Fragment使用前,先介绍下 Fragment一些相关的核心类:

  • Fragment:基类,任何创建的 Fragment都需要继承该类。
  • FragmentManager:管理和维护 Fragment。它是一个抽象类,具体的实现类是 FragmentManagerImpl
  • FragmentTransaction:对 Fragment的添加、删除等操作都需要通过事务方式进行。他是抽象类,具体的实现类是 BackStackRecord

了解了以上概念后,先看下怎么创建一个 Fragment

代码语言:javascript
复制
public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_my, container, false);
    }
}

布局文件 R.layout.fragment_my代码如下:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
    tools:context=".MyFragment">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="我的Fragment"
        android:textSize="20sp" />
</FrameLayout>

首先继承 Fragment,重写 onCreateView()方法,该方法返回 Fragment的UI布局。需要注意的是, inflate()的第三个参数需要设置为false,因为在 Fragment内部实现中,会把该布局添加到 container中,如果设为true,那么就会重复做两次添加,则会抛如下异常:

代码语言:javascript
复制
Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

因为 Fragment不能独立存在,需要依附于 Activity。把 Fragment添加到 Activity中的方式分为两种:

  • 静态添加:通过 xml的方式添加,缺点是一旦添加就不能在运行时删除。
  • 动态添加:运行时添加,这种方式比较灵活,因此建议使用这种方式。
1.静态添加

在需要加载 FragmentActivity对应的布局文件中添加 fragment的标签,需指定 name属性,为了限定类名。

需要注意的是,必须给 fragment的标签添加id属性,否则运行会报错。

代码语言:javascript
复制
<fragment
    android:id="@+id/my_fragment"
    android:name="com.phjt.fragmentdemo.MyFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
2.动态添加

首先 Activity需要有一个容器存放 Fragment,一般是 FrameLayout,因此在 Activity的布局文件中加入 FrameLayoutActivity的布局文件如下:

代码语言:javascript
复制
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    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"
    tools:context=".MyFragmentActivity">

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

然后直接在 Activity代码中添加:

代码语言:javascript
复制
//自定义的Fragment类
MyFragment myFragment = new MyFragment();
//要先获取FragmentManager对象
FragmentManager fragmentManager = getSupportFragmentManager();
//开启一个FragmentTransaction事务
FragmentTransaction transaction = fragmentManager.beginTransaction();
//添加Fragment到布局中
transaction.add(R.id.container, myFragment, "myFragment").commit();

这里我们需要注意几点:

  • 因为我们使用了 support库的 Fragment,因此需要使用 getSupportFragmentManager()获取 FragmentManager
  • add()是对 Fragment众多操作中的一种,还有 remove(), replace()等,第一个参数是根容器的id( FrameLayout的id,即”@+id/container”),第二个参数是 Fragment对象,第三个参数是 Fragment的tag名,指定tag的好处是后续我们可以通过 FragmentmFragment=getSupportFragmentManager().findFragmentByTag("myFragment")FragmentManager中查找 Fragment对象。
  • 在一次事务中,可以做多个操作,比如同时做 add().remove().replace()
  • commit()操作是异步的,内部通过 mManager.enqueueAction()加入处理队列。
  • addToBackStack("fname")是可选的。 FragmentManager拥有回退栈(BackStack),类似于 Activity的任务栈,如果添加了该语句,就把该事务加入回退栈,当用户点击返回按钮,会回退该事务(回退指的是如果事务是 add(fragment),那么回退操作就是 remove(fragment));如果没添加该语句,用户点击返回按钮会直接销毁 Activity

常用方法

FragmentManager相关

1. getFragmentManager()

获取 Fragment父容器的管理器,现在该方法在 Activity中已经被标记过时,不推荐使用

2. getSupportFragmentManager()

V4包下的这个方法,与上一个效果一样,是 Android推荐使用的方法(可以兼容 Android所有版本)。

3. getChildFragmentManager()

Fragment可以添加到 Activity中,那么 Fragment是否也可以添加到 Fragment中呢?当然可以。当我们在 Fragment中继续添加 Fragment,怎么在父 Fragment中获取子 Fragment的管理器?就需要使用 getChildFragmentManager()来获取。

FragmentTransaction相关

可以看出 FragmentTransaction中的方法很多,下面介绍一些常用方法:

1. attach/detach()

  • detach(Fragmentfragment) :分离指定 Fragment的UI视图。
  • attach(Fragmentfragment) :重新关联一个 Fragment(当这个 Fragmentdetach执行之后)。

Fragmentdetach后, Fragment的生命周期执行完 onDestroyView就终止了,这意味着 Fragment的实例并没有被销毁,只是UI界面被移除了(注意和 remove是有区别的)。

Fragmentdetach后,执行 attach操作,会让Fragment从 onCreateView开始执行,一直执行到 onResume

attach无法像 add一样单独使用,单独使用会抛异常。方法存在的意义是对 detach后的 Fragment进行界面恢复。

2. add/remove()

这两个方法,应该是 Fragment中使用频率最高的两个方法了。add()remove()是将 Fragment添加和移除。remove()detach()要彻底一些,如果不加入到回退栈中, remove()的时候 Fragment的生命周期会一直走到 onDetach();如果加入了回退栈,则会只执行到 onDestoryView()Fragment对象还是存在的。

add一个 Fragment,如果加到的是同一个id的话,有点像我们的 Activity栈,启动多个 Activity时候, Activity一个个叠在上面, Fragment也是类似,一个个 Fragment叠在上面。

3. replace()

可以理解为先把相同id下的 Fragment移除掉,然后再加入这个当前的 Fragment。相当于 remove+add

4. hide/show()

如字面意思,让 Fragment隐藏和显示,可以类比 View的显示和隐藏。常常配合有多个 Fragment及有TAB等切换方式的时候,如APP的底部导航,选中某个按钮,让对应的 Fragment显示,其他 Fragment隐藏。

5. commit()

提交 Fragment,提交事务。额外补充:commit()方法并不立即执行 transaction中包含的动作,而是把它加入到UI线程队列中。如果想要立即执行,可以在 commit之后立即调用 FragmentManagerexecutePendingTransactions()方法。

6. addToBackStack()

FragmentTransaction中有加入回退栈的方法,但是没有退出的方法,因为这个方法在 FragmentManager中。

从图中可以看出, popBackStackFragmentTransaction是一个层级,所以 popBackStack操作的其实也是回退栈中 Fragment的事务( FragmentTransaction)。

生命周期

Fragment的生命周期和 Activity类似,但比 Activity的生命周期复杂一些,基本的生命周期方法如下图:

详细解释如下:

  • onAttach()FragmentActivity相关联时调用。可以通过该方法获取 Activity引用,还可以通过 getArguments()获取参数。
  • onCreate()Fragment被创建时调用。
  • onCreateView():创建 Fragment的布局。
  • onActivityCreated():当 Activity完成 onCreate()时调用。
  • onStart():当 Fragment可见时调用。
  • onResume():当 Fragment可见且可交互时调用。
  • onPause():当 Fragment不可交互但可见时调用。
  • onStop():当 Fragment不可见时调用。
  • onDestroyView():当 Fragment的UI从视图结构中移除时调用。
  • onDestroy():销毁 Fragment时调用。
  • onDetach():当 FragmentActivity解除关联时调用。

类比 Activity的生命周期和上图,可以在 ActivityFragment中分别重写各个生命周期的方法,通过打印日志的方式,更好的去理解生命周期的调用,这里就不再进行阐述。

获取Context

Fragment中,我们可以通过 getActivity()getContext()方法直接获取 Context

但有时候可能会获取为空,所以推荐使用以下方法获取 Context对象:

代码语言:javascript
复制
public class MyFragment extends Fragment {

    private Context mContext;

    //高版本,回调此方法
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mContext = context;
    }

    //API低于23的时候,回调这个方法
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mContext = activity;
    }
}

构造函数及数据传递

在项目中,最常见的数据传递就是通过 ActivityFragment传递参数。在上面动态添加 Fragment的时候,可以看出, Fragment就是一个普通的对象,可以通过 new的方式,所以可以通过构造函数或 set方法去给 Fragment传递参数,但并不推荐

推荐使用 Bundle来向 Fragment传递数据,在 Fragment通过 getArguments()获取参数。

代码语言:javascript
复制
public class MyFragment extends Fragment {

    private String mName;

    //不推荐使用
    public MyFragment(String name) {
        this.mName = name;
    }

    //推荐使用,bundle传递数据
    public static MyFragment newInstance(String name) {
        Bundle args = new Bundle();
        args.putString("name", name);

        MyFragment fragment = new MyFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        //通过getArguments方法,获取传递的bundle数据
        Bundle bundle = getArguments();
        mName = bundle.getString("name");

        return inflater.inflate(R.layout.fragment_my, container, false);
    }
}

之所以使用 Bundle传递数据, Activity重新创建时,会重新构建它所管理的 Fragment,原先的 Fragment的字段值将会全部丢失。比如当横竖屏切换时, Fragment会调用自己的无参构造函数,在构造函数传参就会失效。若通过 Fragment.setArguments(bundle)方法设置的 Bundle数据就会保留下来,用于数据恢复,所以应尽量使用这个方式。

对于 Fragment之间的通信,由于 Fragment之间是没有任何依赖关系的,如果要进行 Fragment之间的通信,建议通过 Activity作为中介,不要 Fragment之间直接通信。

结语

以上就是今天 Fragment的基础内容介绍,内容还是挺多的,需要好好消化下。在接下来的文章里,将通过 RadioButtonFragment结合、 ViewPagerFragment相结合的例子,来详细介绍 Fragment的实际项目使用示例。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 下码看花 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 概念
  • 基本使用
    • 1.静态添加
      • 2.动态添加
      • 常用方法
        • FragmentManager相关
          • FragmentTransaction相关
          • 生命周期
          • 获取Context
          • 构造函数及数据传递
          • 结语
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档