【Android】小白进阶之GUI设计Fragment基础浅析

举报
产品人卫朋 发表于 2021/10/30 23:32:41 2021/10/30
【摘要】 作为一个底层硬件驱动开发者,边学边总结是最佳的学习方式,以项目驱动为核心。Fragment 说的直白点就是指片段,目的是与 Activity 去耦合,方便扩展开发。 1、基础简介 Fragment 需要依赖于 Activity,不能独立存在,一个 Activity 里可以有多个 Fragment,同时一个 Fragment 可以被多...

作为一个底层硬件驱动开发者,边学边总结是最佳的学习方式,以项目驱动为核心。Fragment 说的直白点就是指片段,目的是与 Activity 去耦合,方便扩展开发。

1、基础简介

Fragment 需要依赖于 Activity,不能独立存在,一个 Activity 里可以有多个 Fragment,同时一个 Fragment 可以被多个 Activity 重用。

Fragment有自己的生命周期,并能接收输入事件,可以在 Activity 运行时动态地添加或删除 Fragment。

Fragment 具有以下优势:

模块化(Modularity):可以把部分代码写在 Fragment 中
可重用(Reusability):多个 Activity 可以重用一个 Fragment
可适配(Adaptability):根据硬件的屏幕尺寸、屏幕方向,能够方便地实现不同的布局,减少与 Activity 的耦合

2、Fragment使用示例

2.1、创建Fragment


  
  1. public class FragmentTest extends Fragment {  
  2.   private static String ARG_PARAM = "param_key"
  3.      private String mParam; 
  4.      private Activity mActivity; 
  5.      
  6.      public void onAttach(Context context) {
  7.         mActivity = (Activity) context;
  8.         mParam = getArguments().getString(ARG_PARAM);
  9.     }
  10.     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  11.         View root = inflater.inflate(R.layout.fragment_1, container, false);
  12.         TextView view = root.findViewById(R.id.text);
  13.         view.setText(mParam);
  14.         return root;
  15.     }
  16.     
  17.     public static FragmentTest newInstance(String str) {
  18.         FragmentTest frag = new FragmentTest();
  19.         Bundle bundle = new Bundle();
  20.         bundle.putString(ARG_PARAM, str);
  21.         fragment.setArguments(bundle);
  22.         return fragment;
  23.     }
  24. }

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

2.2、添加Fragment到Activity中

在 Activity 中添加 Fragment 有两种方式:

静态添加:在xml中添加,缺点是一旦添加就不能在运行时删除
动态添加:运行时添加,这种方式比较灵活

动态添加示例:

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


  
  1. <FrameLayout
  2.     android:id="@+id/container"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"/>

在 Activity 的 onCreate()中,通过如下方式添加 Fragment:


  
  1. if (bundle == null) {
  2.     getSupportFragmentManager().beginTransaction()
  3.         .add(R.id.container, FragmentTest.newInstance("hello world"), "f1")
  4.         .commit();
  5. }

3、Fragment生命周期

Fragment 的生命周期和 Activity 类似,但比 Activity 的生命周期复杂一些:

图1:


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

上面的方法中,只有 onCreateView() 在重写时不用写 super 方法

因为 Fragment 是依赖 Activity 的,因此为了讲解 Fragment 的生命周期,需要和 Activity 的生命周期方法一起讲:

图2:


举个例子来理解 Fragment 生命周期方法,功能如下:

共有两个 Fragment:F1 和 F2,F1 在初始化时就加入 Activity,点击 F1 中的按钮调用 replace 替换为 F2

当 F1 在 Activity 的 onCreate() 中被添加时,日志如下:


  
  1. BasicActivity: [onCreate] BEGIN
  2. BasicActivity: [onCreate] END
  3. BasicActivity: [onStart] BEGIN
  4. Fragment1:     [onAttach] BEGIN 
  5. Fragment1:     [onAttach] END
  6. BasicActivity: [onAttachFragment] BEGIN
  7. BasicActivity: [onAttachFragment] END
  8. Fragment1:     [onCreate] BEGIN
  9. Fragment1:     [onCreate] END
  10. Fragment1:     [onCreateView]
  11. Fragment1:     [onViewCreated] BEGIN
  12. Fragment1:     [onViewCreated] END
  13. Fragment1:     [onActivityCreated] BEGIN
  14. Fragment1:     [onActivityCreated] END
  15. Fragment1:     [onStart] BEGIN
  16. Fragment1:     [onStart] END
  17. BasicActivity: [onStart] END
  18. BasicActivity: [onPostCreate] BEGIN
  19. BasicActivity: [onPostCreate] END
  20. BasicActivity: [onResume] BEGIN
  21. BasicActivity: [onResume] END
  22. BasicActivity: [onPostResume] BEGIN
  23. Fragment1:     [onResume] BEGIN
  24. Fragment1:     [onResume] END
  25. BasicActivity: [onPostResume] END
  26. BasicActivity: [onAttachedToWindow] BEGIN
  27. BasicActivity: [onAttachedToWindow] END

可以看出:

Fragment 的 onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart() 都是在 Activity 的 onStart() 中调用的
Fragment 的 onResume() 在 Activity 的 onResume() 之后调用

当点击 F1 的按钮,调用 replace() 替换为 F2,且不加 addToBackStack() 时,日志如下:


  
  1. Fragment2:     [onAttach] BEGIN
  2. Fragment2:     [onAttach] END
  3. BasicActivity: [onAttachFragment] BEGIN
  4. BasicActivity: [onAttachFragment] END
  5. Fragment2:     [onCreate] BEGIN
  6. Fragment2:     [onCreate] END
  7. Fragment1:     [onPause] BEGIN
  8. Fragment1:     [onPause] END
  9. Fragment1:     [onStop] BEGIN
  10. Fragment1:     [onStop] END
  11. Fragment1:     [onDestroyView] BEGIN
  12. Fragment1:     [onDestroyView] END
  13. Fragment1:     [onDestroy] BEGIN
  14. Fragment1:     [onDestroy] END
  15. Fragment1:     [onDetach] BEGIN
  16. Fragment1:     [onDetach] END
  17. Fragment2:     [onCreateView]
  18. Fragment2:     [onViewCreated] BEGIN
  19. Fragment2:     [onViewCreated] END
  20. Fragment2:     [onActivityCreated] BEGIN
  21. Fragment2:     [onActivityCreated] END
  22. Fragment2:     [onStart] BEGIN
  23. Fragment2:     [onStart] END
  24. Fragment2:     [onResume] BEGIN
  25. Fragment2:     [onResume] END

可以看到,F1 最后调用了onDestroy()和onDetach()

当点击 F1 的按钮,调用 replace() 替换为 F2,且加 addToBackStack() 时,日志如下:


  
  1. Fragment2:     [onAttach] BEGIN
  2. Fragment2:     [onAttach] END
  3. BasicActivity: [onAttachFragment] BEGIN
  4. BasicActivity: [onAttachFragment] END
  5. Fragment2:     [onCreate] BEGIN
  6. Fragment2:     [onCreate] END
  7. Fragment1:     [onPause] BEGIN
  8. Fragment1:     [onPause] END
  9. Fragment1:     [onStop] BEGIN
  10. Fragment1:     [onStop] END
  11. Fragment1:     [onDestroyView] BEGIN
  12. Fragment1:     [onDestroyView] END
  13. Fragment2:     [onCreateView]
  14. Fragment2:     [onViewCreated] BEGIN
  15. Fragment2:     [onViewCreated] END
  16. Fragment2:     [onActivityCreated] BEGIN
  17. Fragment2:     [onActivityCreated] END
  18. Fragment2:     [onStart] BEGIN
  19. Fragment2:     [onStart] END
  20. Fragment2:     [onResume] BEGIN
  21. Fragment2:     [onResume] END

可以看到,F1 被替换时,最后只调到了onDestroyView(),并没有调用 onDestroy() 和 onDetach()
当用户点返回按钮回退事务时,F1 会调onCreateView()->onStart()->onResume()
因此在 Fragment 事务中加不加 addToBackStack() 会影响 Fragment 的生命周期。

FragmentTransaction 有一些基本方法,下面给出调用这些方法时,Fragment生命周期的变化:


  
  1. add():     onAttach()->…->onResume()
  2. remove():  onPause()->…->onDetach()
  3. replace(): 相当于旧 Fragment 调用 remove(),新 Fragment 调用 add()
  4. show():    不调用任何生命周期方法,调用该方法的前提是要显示的 Fragment 已经被添加到容器,只是纯粹把 Fragment UI 的 setVisibility 为 true
  5. hide():    不调用任何生命周期方法,调用该方法的前提是要显示的 Fragment 已经被添加到容器,只是纯粹把 Fragment UI 的 setVisibility 为 false
  6. detach():  onPause()->onStop()->onDestroyView(),UI 从布局中移除,但是仍然被 FragmentManager 管理
  7. attach():  onCreateView()->onStart()->onResume()

Fragment 实现原理和Back Stack:

我们知道 Activity 有任务栈,用户通过 startActivity 将 Activity 加入栈,点击返回按钮将 Activity 出栈
Fragment 也有类似的栈,称为回退栈(Back Stack),回退栈是由 FragmentManager 管理的
默认情况下,Fragment 事务是不会加入回退栈的,如果想将 Fragment 事务加入回退栈
则可以加入 addToBackStack(""),如果没有加入回退栈,则用户点击返回按钮会直接将 Activity 出栈
如果加入了回退栈,则用户点击返回按钮会回滚 Fragment 事务。

示例:


  
  1. getSupportFragmentManager().beginTransaction()
  2.     .add(R.id.container, f1, "f1")
  3.     .addToBackStack("")
  4.     .commit();

上面这个代码的功能就是将 Fragment 加入 Activity 中

内部实现为:

创建一个 BackStackRecord 对象,该对象记录了这个事务的全部操作轨迹(这里只做了一次add操作,并且加入回退栈),随后将该对象提交到 FragmentManager 的执行队列中,等待执行

addToBackStack("") 是将 mAddToBackStack 变量记为 true,在 commit() 中会用到该变量
commit() 是异步的,即不是立即生效的,但是后面会看到整个过程还是在主线程完成,只是把事务的执行扔给主线程的 Handler,commit() 内部是 commitInternal(),实现如下:


  
  1. int commitInternal(boolean allowStateLoss) {
  2.     mCommitted = true;
  3.     if (mAddToBackStack) {
  4.         mIndex = mManager.allocBackStackIndex(this);
  5.     } else {
  6.         mIndex = -1;
  7.     }
  8.     mManager.enqueueAction(this, allowStateLoss); //将事务添加进待执行队列中
  9.     return mIndex;
  10. }

回退栈示例:

共有三个Fragment:F1, F2, F3,F1 在初始化时就加入 Activity,点击 F1 中的按钮跳转到 F2,点击 F2 的按钮跳转到 F3,点击 F3 的按钮回退到 F1

在 Activity 的 onCreate() 中,将 F1 加入 Activity 中:


  
  1. getSupportFragmentManager().beginTransaction()
  2.     .add(R.id.container, f1, "f1")
  3.     .addToBackStack(Fragment1.class.getSimpleName())
  4.     .commit();

F1 按钮的 onClick() 内容如下:


  
  1. getFragmentManager().beginTransaction()
  2.     .replace(R.id.container, f2, "f2")
  3.     .addToBackStack(Fragment2.class.getSimpleName())
  4.     .commit();

F2 按钮的 onClick() 如下:


  
  1. getFragmentManager().beginTransaction()
  2.     .replace(R.id.container, f3, "f3")
  3.     .addToBackStack(Fragment3.class.getSimpleName())
  4.     .commit();

F3 按钮的 onClick() 如下:

getFragmentManager().popBackStack(Fragment2.class.getSimpleName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
 

4、Fragment通信

Fragment 向 Activity 传递数据:

在 Fragment 中定义接口,并让 Activity 实现该接口:


  
  1. public interface OnFragmentInteractionListener {
  2.     void onItemClick(String str);  // 将str从Fragment传递给Activity
  3. }

在 Fragment 的 onAttach() 中,将参数 Context 强转为 OnFragmentInteractionListener 对象:


  
  1. public void onAttach(Context context) {
  2.     super.onAttach(context);
  3.         if (context instanceof OnFragmentInteractionListener) {
  4.         mListener = (OnFragmentInteractionListener) context;
  5.     } else {
  6.                 throw new RuntimeException(context.toString()
  7.                 + " must implement OnFragmentInteractionListener");
  8.     }
  9. }

并在 Fragment 合适的地方调用 mListener.onItemClick("hello") 将 "hello" 从 Fragment 传递给 Activity

FABridge:

由于通过接口的方式从 Fragment 向 Activity 进行数据传递比较麻烦,需要在 Fragment 中定义 interface
并让 Activity 实现该 interface,FABridge(https://github.com/hongyangAndroid/FABridge) 通过注解的形式免去了这些定义。

在 build.gradle 中添加依赖:

annotationProcessor 'com.zhy.fabridge:fabridge-compiler:1.0.0'compile 'com.zhy.fabridge:fabridge-api:1.0.0'

首先定义方法 ID,这里为 FAB_ITEM_CLICK,接着在 Activity 中定义接口:

@FCallbackId(id = FAB_ITEM_CLICK)public void onItemClick(String str) {  //方法名任意
    Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}

最后,在 Fragment 中,通过以下形式调用 "ID=FAB_ITEM_CLICK" 的方法(该方法可能在Activity中,也可能在任何类中):

Fabridge.call(mActivity,FAB_ITEM_CLICK,"data");  //调用ID对应的方法,"data"为参数值

Activity 向 Fragment传递数据:

Activity 向 Fragment 传递数据比较简单,获取 Fragment 对象,并调用 Fragment 的方法即可

比如要将一个字符串传递给 Fragment,则在 Fragment 中定义方法:


  
  1. public void setString(String str) { 
  2.     this.str = str;
  3. }

并在 Activity 中调用 fragment.setString("hello") 即可。

5、Fragment之间通信

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

DialogFragment:

用于实现对话框,优点是:即使旋转屏幕,也能保留对话框状态

如果要自定义对话框样式,只需要继承 DialogFragment,并重写 onCreateView(),该方法返回对话框 UI

例子:


  
  1. public class ProgressDialogFragment extends DialogFragment {
  2.     @Override
  3.     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  4.         getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); //消除Title区域
  5.         getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));  //将背景变为透明
  6.         setCancelable(false);  //点击外部不可取消
  7.         View root = inflater.inflate(R.layout.fragment_progress_dialog, container);
  8.         return root;
  9.     }
  10.      public static ProgressDialogFragment newInstance() {
  11.         return new ProgressDialogFragment();
  12.     }
  13. }
  14. <com.airbnb.lottie.LottieAnimationView
  15.     android:layout_width="wrap_content"  //大小根据JSON文件确定
  16.     android:layout_height="wrap_content"
  17.     app:lottie_fileName="loader_ring.json"   //JSON文件
  18.     app:lottie_loop="true"    //循环播放
  19.     app:lottie_autoPlay="true" />  //自动播放

然后通过下面代码显示对话框:

ProgressDialogFragment fragment = ProgressDialogFragment.newInstance();
fragment.show(getSupportFragmentManager(), "tag");//fragment.dismiss();

为了实现圆角,除了在 onCreateView() 中把背景设为透明,还需要对UI加入背景:


  
  1. <shape xmlns:android="http://schemas.android.com/apk/res/android">
  2.     <solid android:color="#ffffff"/>
  3.     <corners
  4.         android:radius="20dp"/>
  5. </shape>

refer:

https://www.jianshu.com/p/11c8ced79193
https://mp.weixin.qq.com/s/dUuGSVhWinAnN9uMiBaXgw

文章来源: blog.csdn.net,作者:简一商业,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/liwei16611/article/details/81984651

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。