编辑
2020-10-18
客户端技术
00
请注意,本文编写于 1079 天前,最后修改于 113 天前,其中某些信息可能已经过时。

目录

Fragment基本概述
静态加载与动态加载
1、静态加载
2、动态加载
3、使用注意点
Activity向Fragment传值
Fragment向Activity传值
Fragment生命周期

为什么需要Fragment?Fragment与Activity又是什么关系?Fragment的生命周期是怎样的?Fragment如何使用呢?其实Fragment是一种可以嵌入在活动中的UI片段,能够让程序更加合理和充分地利用大屏幕的空间,出现的初衷是为了适应大屏幕的平板电脑,可以将其看成一个小型Activity,又称作Activity碎片。下面来看看Fragment到底有哪些神奇之处吧 ~

Fragment基本概述

使用Fragment可以把屏幕划分成几块,然后进行分组,进行一个模块化管理。Fragment不能够单独使用,需要嵌套在Activity中使用,其生命周期也受到宿主Activity的生命周期的影响。

1、一个Activity可以运行多个Fragment 2、Fragment不能脱离Activity而存在 3、Activity是屏幕的主体,而Fragment是Activity的一个组成元素 4、一个Fragment可以被多个Activity重用 5、Fragment有自己的生命周期,并能接收输入事件 6、可以在Activity运行时动态地添加或删除Fragment

Fragment的优势: 1、模块化:我们不必把所有代码全部写在Activity中,而是把代码写在各自的Fragment中。 2、可重用:多个Activity可以重用一个Fragment。 3、可适配:根据硬件的屏幕尺寸、屏幕方向,能够方便地实现不同的布局,这样用户体验更好。

静态加载与动态加载

使用Fragment有两种方式,分别是静态加载和动态加载。

1、静态加载

关于静态加载的流程如下:

  • 定义Fragment的xml布局文件
  • 自定义Fragment类,继承Fragment类或其子类,同时实现onCreate()方法,在方法中,通过inflater.inflate加载布局文件,接着返回其View
  • 在需要加载Fragment的Activity对应布局文件中的name属性设为全限定类名,即包名.fragment
  • 最后在Activity调用setContentView()加载布局文件即可

静态加载一旦添加就不能在运行时删除

比如我现在要先进入到一个专门用于静态加载的Activity

java
// 静态加载 public void toStaticLoadActivity(View view) { startActivity(new Intent(MainActivity.this, StaticLoadActivity.class)); }

StaticLoadActivity的布局文件如下:

xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent"> <fragment android:id="@+id/list_fragment" android:name="com.example.learnfragment.ListFragment" android:layout_width="250dp" android:layout_height="150dp"/> <fragment android:layout_marginTop="20dp" android:id="@+id/list_fragment_two" android:name="com.example.learnfragment.ListFragment" android:layout_width="250dp" android:layout_gravity="center" android:layout_height="150dp"/> </LinearLayout>

其中fragment的name属性都指向了com.example.learnfragment.ListFragment:

java
// 列表 Fragment public class ListFragment extends Fragment { // 创建视图 @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_list, container, false); TextView textView = view.findViewById(R.id.tv_static_load); textView.setText("Hello, Fragment!"); return view; } }

fragment_list.xml 布局文件如下所示:

xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="vertical" android:background="@color/colorPrimary" android:layout_height="match_parent"> <TextView android:id="@+id/tv_static_load" android:layout_width="match_parent" android:layout_height="100dp" android:gravity="center" android:textColor="#ffffff" android:textSize="20sp" android:text="This is a fragment" /> </LinearLayout>

现在只需要点击Main Activity中的TextView即可看到静态加载Fragment的效果:

由此可见,其实Fragment是可复用的,因为StaticLoadActivity的布局文件中写了两个Fragment标签。

2、动态加载

动态加载Fragment的流程如下:

  • 提前准备好Container,即Fragment的容器
  • 获得FragmentManager对象,通过getSupportFragmentManager()
  • 获得FragmentTransaction对象,通过fm.beginTransaction()
  • 调用add()方法或者repalce()方法加载Fragment
  • 最后调用commit()方法提交事务

下面演示一下动态加载Fragment,先在activity_main.xml布局文件中准备好两个Container和按钮:

xml
<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:orientation="vertical" tools:context=".MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="100dp" android:gravity="center" android:onClick="toStaticLoadActivity" android:text="static load fragment" /> <LinearLayout android:layout_width="match_parent" android:orientation="horizontal" android:layout_height="wrap_content"> <Button android:id="@+id/load_left" android:text="加载左边" android:layout_width="0dp" android:layout_weight="1" android:onClick="dynamicLoad" android:layout_height="wrap_content"/> <Button android:id="@+id/load_right" android:text="加载右边" android:layout_width="0dp" android:layout_weight="1" android:onClick="dynamicLoad" android:layout_height="wrap_content"/> <Button android:id="@+id/remove_left" android:text="删除左边" android:layout_width="0dp" android:layout_weight="1" android:onClick="dynamicLoad" android:layout_height="wrap_content"/> <Button android:id="@+id/remove_right" android:text="删除右边" android:layout_width="0dp" android:layout_weight="1" android:onClick="dynamicLoad" android:layout_height="wrap_content"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_margin="1dp" android:id="@+id/list_container" android:layout_width="150dp" android:orientation="horizontal" android:layout_height="400dp"> </LinearLayout> <LinearLayout android:layout_margin="1dp" android:orientation="horizontal" android:id="@+id/detail_container" android:layout_width="200dp" android:layout_height="400dp"> </LinearLayout> </LinearLayout> </LinearLayout>

一个Container是list_container、一个是detail_container,四个按钮的点击事件分别为dynamicLoad:

java
public class MainActivity extends AppCompatActivity { ListFragment leftFragment = null; ListFragment rightFragment = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 静态加载 public void toStaticLoadActivity(View view) { startActivity(new Intent(MainActivity.this, StaticLoadActivity.class)); } // 动态加载 1、container 2、fragment 3、fragment -> container public void dynamicLoad(View view) { int id = view.getId(); switch (id){ case R.id.load_left: leftFragment = new ListFragment(); getSupportFragmentManager() .beginTransaction() .add(R.id.list_container, leftFragment) .commit(); break; case R.id.load_right: rightFragment = new ListFragment(); getSupportFragmentManager() .beginTransaction() .add(R.id.detail_container, rightFragment) .commit(); break; case R.id.remove_left: getSupportFragmentManager() .beginTransaction() .remove(leftFragment) .commit(); break; case R.id.remove_right: getSupportFragmentManager() .beginTransaction() .remove(rightFragment) .commit(); break; } } }

动态加载Fragment中,FragmentTransaction类提供了方法完成增删等操作,完成后调用FragmentTransaction.commit()方法提交修改。

  • transaction.add():往Activity里面添加一个片段

  • transaction.remove():从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁

  • transaction.replace():使用另一个Fragment替换当前的,实际上是remove()然后add()的合体

  • transaction.hide():隐藏当前Fragment,仅不可见,不会销毁

  • transaction.show():显示之前隐藏的Fragment

  • detach():会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护

  • attach():重建view视图,附加到UI上并显示。

FragmentTransaction的commit方法一定要在Activity.onSaveInstance()之前调用,commit()操作是异步的,内部通过mManager.enqueueAction()加入处理队列。对应的同步方法为commitNow(),commit()内部会有checkStateLoss()操作,如果开发人员使用不当(比如commit()操作在onSaveInstanceState()之后),可能会抛出异常,而commitAllowingStateLoss()方法则是不会抛出异常版本的commit()方法,但是尽量使用commit(),而不要使用commitAllowingStateLoss()。

FragmentManager拥有回退栈(BackStack),类似于Activity的任务栈,如果添加了该语句,就把该事务加入回退栈,当用户点击返回按钮,会回退该事务(回退指的是如果事务是add(frag1),那么回退操作就是remove(frag1));如果没添加该语句,用户点击返回按钮会直接销毁Activity。

3、使用注意点

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

2、如果在创建Fragment时要传入参数,必须要通过setArguments(Bundle bundle)方式添加,而不建议通过为Fragment添加带参数的构造函数,因为通过setArguments()方式添加,在由于内存紧张导致Fragment被系统杀掉并恢复(re-instantiate)时能保留这些数据。

3、可以在Fragment的onAttach()中通过getArguments()获得传进来的参数。如果要获取Activity对象,不建议调用getActivity(),而是在onAttach()中将Context对象强转为Activity对象。

Activity向Fragment传值

在ListFragment写一个获取ListFragment的方法:

java
public class ListFragment extends Fragment { private static final String BUNDLE_TITTLE = "bundle_tittle"; private String mTittle; // 传递一个String tittle进来 public static ListFragment getInstance(String tittle){ ListFragment fragment = new ListFragment(); Bundle bundle = new Bundle(); bundle.putString(BUNDLE_TITTLE, tittle); fragment.setArguments(bundle); return fragment; } // .... @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle arguments = getArguments(); if(arguments != null){ mTittle = arguments.getString(BUNDLE_TITTLE); } } // 创建视图 @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_list, container, false); TextView textView = view.findViewById(R.id.tv_static_load); textView.setText(mTittle); return view; } }

这样在创建ListFragment对象的时候,直接ListFragment.getInstance()传参即可:

java
switch (id){ case R.id.load_left: leftFragment = ListFragment.getInstance("Left Fragment!"); getSupportFragmentManager() .beginTransaction() .add(R.id.list_container, leftFragment) .commit(); break; case R.id.load_right: rightFragment = ListFragment.getInstance("Right Fragment!"); getSupportFragmentManager() .beginTransaction() .add(R.id.detail_container, rightFragment) .commit(); break; // ... }

Fragment向Activity传值

还是根据上面的例子,首先在ListFragment中定义

java
public class ListFragment extends Fragment { ...... // 创建视图 @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_list, container, false); TextView textView = view.findViewById(R.id.tv_static_load); textView.setText(tittle); textView.setOnClickListener((v) -> { if(mOnTittleListener != null) { mOnTittleListener.onClick(tittle); } }); return view; } // 1、定义接口 // 当TextView被点击的时候可以把Tittle传出去 public interface OnTittleListener { void onClick(String tittle); } // 2、定义全局变量 private OnTittleListener mOnTittleListener; // 3、设置接口的方法 public void setOnTittleListener(OnTittleListener onTittleListener) { this.mOnTittleListener = onTittleListener; } }

MainActivity中:

java
public class MainActivity extends AppCompatActivity implements ListFragment.OnTittleListener { ListFragment leftFragment = null; boolean leftDisplay = false; ....... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 1、container 2、fragment 3、fragment -> container leftFragment = ListFragment.getInstance("Left Fragment!"); getSupportFragmentManager() .beginTransaction() .add(R.id.list_container, leftFragment) .commit(); leftDisplay = true; leftFragment.setOnTittleListener(this); } @Override public void onClick(String tittle) { // 设置Lable为ListFragment传回来的值 setTitle(tittle); } }

如果在Fragment中需要Context,可以通过getActivity(),如果该Context需要在Activity被销毁后还存在,则使用getActivity.getApplicationContext(); 考虑Fragment的重复使用问题,降低与Activity的耦合,Fragment操作应该由它的管理者Activity决定。

Fragment生命周期

Fragment中常用的生命周期方法

当Fragment从创建到运行时回调的生命周期方法有

1、onAttach():当Fragment依附到Activity时调用的方法 2、onCreate():当Fragment创建时调用的方法 3、onCreateView():给Fragment加载布局时调用的方法 4、onActivityCreated():当该Fragment依附的Activity创建时调用的方法 5、onStart():当Fragment启动时调用的方法 6、onResume():当Fragment正在运行时调用的方法

当Fragment不再使用时调用的生命周期方法

7、onPause():当Fragment不在交互时调用该方法 8、onStop():当Fragment不再可见时调用该方法 9、onDestroyView():销毁Fragment布局时调用的方法 10、onDestroy():当Frament销毁时调用的方法 11、onDetach():当Fragment完全脱离Activity时调用的方法

下面通过代码演示一下Fragment的生命周期:

MainActivity的布局中有两个按钮,一个是用于加载Fragment的,另外一个是切换到另外一个Activity的,MainActivity的界面代码如下所示:

xml
<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:orientation="vertical" tools:context=".MainActivity"> <Button android:onClick="loadFragment" android:text="加载Fragment" android:textAllCaps="false" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="切换到另一个Activity" android:textAllCaps="false" android:onClick="toAnotherActivity" /> <LinearLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="0dp" android:orientation="vertical" android:layout_weight="2" /> </LinearLayout>

MyFragment的界面:

xml
<RelativeLayout 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:gravity="center" android:layout_marginTop="50dp" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:text="Fragment的布局显示" /> </RelativeLayout>

AnotherActivity的界面:

xml
<RelativeLayout 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=".AnotherActivity"> <TextView android:layout_centerInParent="true" android:layout_width="match_parent" android:gravity="center" android:text="这是另一个Activity" android:textAllCaps="false" android:layout_height="match_parent"/> </RelativeLayout>

MainActivity的逻辑,其实主要就是两个点击事件:

java
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } // 加载Fragment public void loadFragment(View view) { MyFragment fragment = MyFragment.newInstance(); getSupportFragmentManager() .beginTransaction() .add(R.id.container, fragment, "MyFragment") .commit(); } // 跳转另一个Activity public void toAnotherActivity(View view) { startActivity(new Intent(this, AnotherActivity.class)); } }

MyFragment代码如下:

java
public class MyFragment extends Fragment { private static final String TAG = "MyFragment"; public static MyFragment newInstance() { return new MyFragment(); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, "onCreate: Fragment创建"); } @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.i(TAG, "onCreateView: Fragment绑定布局"); return inflater.inflate(R.layout.my_fragment, container, false); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Log.i(TAG, "onActivityCreated: 依附的Activity创建"); } @Override public void onStart() { super.onStart(); Log.i(TAG, "onStart: Fragment启动"); } @Override public void onResume() { super.onResume(); Log.i(TAG, "onResume: Fragment正在运行"); } @Override public void onPause() { super.onPause(); Log.i(TAG, "onPause: Fragment不再交互"); } @Override public void onStop() { super.onStop(); Log.i(TAG, "onStop: Fragment停止运行"); } @Override public void onDestroyView() { super.onDestroyView(); Log.i(TAG, "onDestroyView: Fragment视图销毁"); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy: Fragment销毁"); } @Override public void onDetach() { super.onDetach(); Log.i(TAG, "onDetach: Fragment脱离Activity"); } }

通过演示可以看到如下效果:

log
10-18 17:18:08.735 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onCreate: Fragment创建 10-18 17:18:08.736 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onCreateView: Fragment绑定布局 10-18 17:18:08.742 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onActivityCreated: 依附的Activity创建 10-18 17:18:08.742 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onStart: Fragment启动 10-18 17:18:08.742 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onResume: Fragment正在运行 10-18 17:18:11.353 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onPause: Fragment不再交互 10-18 17:18:11.820 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onStop: Fragment停止运行 10-18 17:18:14.072 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onStart: Fragment启动 10-18 17:18:14.072 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onResume: Fragment正在运行 10-18 17:18:16.221 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onPause: Fragment不再交互 10-18 17:18:16.606 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onStop: Fragment停止运行 10-18 17:18:16.607 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onDestroyView: Fragment视图销毁 10-18 17:18:16.619 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onDestroy: Fragment销毁 10-18 17:18:16.619 5107-5107/cn.changlin.fragmentlifecycle I/MyFragment: onDetach: Fragment脱离Activity

本文作者:Tim

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!