Android"第五大组件"之Fragment

一、简介


Fragment
Fragment

Android 在 Android 3.0(API 级别 11)中引入了Fragment,主要是为了给大屏幕(如平板电脑)上更加动态和灵活的 UI 设计提供支持。

Fragment 表示 Activity 中的行为或用户界面部分。您可以将多个Fragment组合在一个 Activity 中来构建多窗格 UI,以及在多个 Activity 中重复使用某个Fragment。您可以将Fragment视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且您可以在 Activity 运行时添加或移除Fragment(有点像您可以在不同 Activity 中重复使用的“子 Activity”)。

Fragment必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。

您可以通过在 Activity 的布局文件中声明片段,将其作为 <fragment> 元素插入您的 Activity 布局中,或者通过将其添加到某个现有 ViewGroup,利用应用代码进行插入

因为Fragment的使用频率仅次于Activity,有的项目中Fragment使用频率比Activity还要高,并且它有自己的生命周期,所以,我称它为“Android第五大组件”

二、使用


1. 创建Fragment

/**
 * 创建Fragment
 */
public class RightFragment extends Fragment {

    public static final String TAG = "RightFragment";

    /**
     * 生命周期方法:与Activity建立关联时调用
     */
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.d(TAG, "onAttach");
    }

    /**
     * 生命周期方法:初始化创建时回调
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
    }

    /**
     * 生命周期方法:为Fragment创建视图时调用
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        Log.d(TAG, "onCreateView");
        // 加载视图文件
        View view = inflater.inflate(R.layout.right_fragment, container, false);
        return view;
    }

    /**
     * 生命周期方法:在确保Activity创建完成并且Fragment的视图结构创建完成后调用
     */
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Log.d(TAG, "onActivityCreated");
    }

    /**
     * 生命周期方法:与Activity类似,对用户可见时,在Activity的onStart之后回调
     */
    @Override
    public void onStart() {
        super.onStart();
        Log.d(TAG, "onStart");
    }

    /**
     * 生命周期方法:与Activity类似,用户可见且可交互时,在Activity的onResume之后回调
     */
    @Override
    public void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
    }

    /**
     * 生命周期方法:与Activity类似,Activity被遮挡可见不可交互时,在Activity的onPause之后回调
     */
    @Override
    public void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
    }

    /**
     * 生命周期方法:与Activity类似,Activity被其他非dialog Activity覆盖不可见时,在Activity的onStop之后回调
     */
    @Override
    public void onStop() {
        super.onStop();
        Log.d(TAG, "onStop");
    }

    /**
     * 生命周期方法:Activity回调onDestroy之后,销毁Fragment的视图时调用
     */
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        Log.d(TAG, "onDestroyView");
    }

    /**
     * 生命周期方法:在onDestroyView之后,不再使用Fragment,销毁Fragment实例时调用
     */
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }

    /**
     * 生命周期方法:与Activity解除关联时调用
     */
    @Override
    public void onDetach() {
        super.onDetach();
        Log.d(TAG, "onDetach");
    }

}

2. 编写xml布局

right_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#00ff00"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="This is right fragment"
        />

</LinearLayout>

3. 使用Fragment

  • 静态引用

MainActivity中引用:activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- 引用全类名 -->
    <fragment
        android:name="com.example.fragmenttest.RightFragment"
        android:id="@+id/left_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>
  • 动态引用
    activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/right_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

</FrameLayout>
// MainActivity中代码引用
// 获取fm
FragmentManager fragmentManager = getSupportFragmentManager();
// 开启事务
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 指定容器添加Fragment
transaction.add(R.id.right_layout, fragment);
// 提交事务
transaction.commit();

三、Fragment生命周期


Fragment生命周期
Fragment生命周期

Activity与Fragment的生命周期交替:

四、FragmentManager


顾名思义,FragmentManager,是管理Fragment的一个类

  • 分类:

    • android.app.FragmentManager
      在3.0以上版本使用,通过getFragmentManager()获取

    • android.support.v4.app.FragmentManager
      兼容版本,可以让Fragment在Android各个版本功能保持一致,比如Fragment嵌套Fragment就是在Android4.2才支持的,如果用上面的FragmentManager就会崩溃。
      必须在FragmentActivity的子类中才能使用,通过getSupportFragmentManager()获取

  • 常用操作:

    • findFragmentById()(对于在 Activity 布局中提供 UI 的片段)或 findFragmentByTag()(对于提供或不提供 UI 的片段)获取 Activity 中存在的片段。
    • popBackStack()(模拟用户发出的返回命令)将片段从返回栈中弹出。
    • addOnBackStackChangedListener() 注册一个侦听返回栈变化的侦听器。
    • beginTransaction() 打开一个 FragmentTransaction,通过它来执行某些事务,如添加和移除片段。

五、FragmentTransaction


FragmentTransaction可以对Fragment执行添加、移除、替换以及其他操作。 您提交给 Activity 的每组更改都称为事务。

// 通过FragmentManager开启事务
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

常用操作:

  • add() 添加
  • remove()移除
  • hide()隐藏
  • show()展示
  • replace()替换
  • commit()提交 ,一个FragmentTransaction只能提交一次,最后提交
  • addToBackStack(null)添加到回退栈,用户通过按返回按钮返回上一个Fragment。
  • 向 FragmentTransaction 添加更改的顺序无关紧要,不过:
    • 您必须最后调用 commit()
    • 如果您要向同一容器添加多个片段,则您添加片段的顺序将决定它们在视图层次结构中的出现顺序
  • 调用 commit() 不会立即执行事务,而是在 Activity 的 UI 线程(“主”线程)可以执行该操作时再安排其在线程上运行。不过,如有必要,您也可以从 UI 线程调用 executePendingTransactions() 以立即执行 commit() 提交的事务。通常不必这样做,除非其他线程中的作业依赖该事务。

六、Fragment通信


1. Fragment和Activity通信

片段可以通过 getActivity() 访问 Activity 实例,并轻松地执行在 Activity 布局中查找视图、调用其中的方法等任务。

View listView = getActivity().findViewById(R.id.list);

同样地,您的 Activity 也可以使用 findFragmentById()findFragmentByTag(),通过从 FragmentManager 获取对 Fragment 的引用来调用片段中的方法。例如:

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
fragment.updateView();

2. Fragment之间通信

实际和上面差不多,一个Fragment获取相关的Activity,然后由Activity再获取另一个Fragment,从而实现这两个Fragment之间的通信

3. Fragment传值

通过setArguments()给Fragment传递值

RightFragment fragment = new RightFragment();
// bundle,键值对映射
Bundle bundle = new Bundle();
bundle.putString("test","传递的值");
fragment.setArguments(bundle);

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.add(R.id.right_layout, fragment);
transaction.addToBackStack(null);
transaction.commit();

通过getArguments()获取值

public class RightFragment extends Fragment {

    public static final String TAG = "RightFragment";
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 获取bundle
        Bundle bundle = getArguments();
        if(bundle != null){
            String test = bundle.getString("test");
            Log.e(TAG,test); // 将会输出“传递的值”
        }

    }

3. Fragment通过startActivityForResult()获取回传值

如果我们在Fragment中跳转Activity想要获取回传值,我们可以通过Fragment.startActivityForResult()来实现,切记,是调用Fragment的该方法,而不是Activity的,否则onActivityResult()回调将会在Fragment所在的Activity中被回调。

不贴代码了,和Activity的基本一致,在Fragment中通过startActivityForResult()打开Activity,在Activity中通过setResult()回传值,在Fragment中通过重写onActivityResult()获取值。

七、Fragment转场动画


1. 自带动画效果

使用FragmentTransaction.setTransition()设置,设置后,添加和移除时就会有动画效果;在操作Fragment之前调用才有效

FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 1. FragmentTransaction.TRANSIT_NONE:无动画,默认
// 2. FragmentTransaction.TRANSIT_FRAGMENT_OPEN:打开样式动画效果
// 3. FragmentTransaction.TRANSIT_FRAGMENT_CLOSE:关闭样式动画效果
// 4. FragmentTransaction.TRANSIT_FRAGMENT_FADE:淡入淡出动画效果
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);

transaction.add(R.id.right_layout, fragment);
transaction.addToBackStack(null);
transaction.commit();

2. 自定义动画

使用FragmentTransaction.setCustomAnimations()设置,需要在操作Fragment之前调用

  • android.app.Fragment
android.app.FragmentTransaction
public abstract FragmentTransaction setCustomAnimations(@AnimatorRes int var1,@AnimatorRes int var2,@AnimatorRes int var3,@AnimatorRes int var4)

由上面的文档可看出,使用该Fragment,需要使用属性动画,在res下新建animator文件夹,res/animator,在其中创建以<set><objectAnimator><valueAnimator>为根布局的动画文件

  • android.support.v4.app.Fragment
android.support.v4.app.FragmentTransaction
public abstract FragmentTransaction setCustomAnimations(@AnimRes int enter,@AnimRes int exit,@AnimRes int popEnter,@AnimRes int popExit)

由上面的文档可看出,使用该Fragment,需要使用补间动画,在res下新建anim文件夹,res/anim,在其中创建以<set><rotate><scale>等几个补间动画的标签为根布局的动画文件

八、Fragment结合Viewpager使用


关于Fragment结合Viewpager使用,可以看我的ViewPager 全面总结

最后推荐三篇Fragment好文:
Fragment全解析系列(一):那些年踩过的坑
Fragment全解析系列(二):正确的使用姿势
Fragment之我的解决方案:Fragmentation


个人总结,水平有限,如果有错误,希望大家能给留言指正!如果对您有所帮助,可以帮忙点个赞!如果转载,希望可以留言告知并在显著位置保留草帽团长的署名和标明文章出处!最后,非常感谢您的阅读!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容