仅用于个人学习使用, 对大多数知识点进行了总结, 欢迎大家!
同 Activity 一样, Fragment 要有自己的布局文件 (无需在 AndroidManifest.xml 中注册)
Fragment 接近 view, 是帮助开发者封装view组合的工具类,你也可以把它理解为带有生命周期的Android提供的可自主定义布局的view容器. (子 Activity)
Fragment 关心更多的是view的拆分和组合 (适配手机和平板横竖屏)
创建
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BlankFragment extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
public BlankFragment() {
// Required empty public constructor
}
public static BlankFragment newInstance(String param1, String param2) {
BlankFragment fragment = new BlankFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
// 在 onCreateView() 中加载布局
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_blank, container, false);
}
}
静态创建
1. 编写 fragment 的类: BlankFragment 2. 在 activity 的布局文件中声明 <FragmentContainerView/> (代替 <fragment/> ) <FragmentContainerView android:id="@+id/blankFragment" android:name="com.example.app.BlankFragment" <!--指定 fragment 的路径 --> /> 3. 创建 fragment_blank.xml
FragmentContainerView 位于androidx.fragment.app 包下,是专门为 Fragment 设计的自定义布局。它扩展了 FrameLayout,所以它可以可靠地处理Fragment事务,并且它还有其他功能来协调 Fragment 行为。FragmentContainerView 应该被用作 fragment 的容器,通常在一个 activity 的 xml 布局中设置
FragmentContainerView 只允许由 Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle) 返回的 view。试图添加任何其他视图将导致 IllegalStateException。
动态创建 Fragment
FragmentActivity 已经被淘汰了, 用 Activity 就行
不在 activity 的布局文件中声明 <FragmentContainerView/> , 而是设置一个 view(不一定是 FragmentContainerView) 等待填充 fragment
BlankFragment fragment = BlankFragment.newInstance(param1, param2);
FragmentManager mFragmentManager = getSupportFragmentManager(); // 通过管理器处理 Fragment 事务
// 事务需要开启和提交 (Fragment的动态添加)
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
// 该fragment.getid()获取的是activity_main.xml中的fragment的容器的id,即R.id.xxx,
// 因此没有通过 fragmentTransaction.add 加入的fragment是没有id的
Log.d(TAG, "replaceFragment: " + fragment.getId());
fragmentTransaction.replace(R.id.linear, fragment); // 第一个参数: 容器 view 的 id (建议用 FrameLayout)
fragmentTransaction.addToBackStack(null); // 一般为 null (返回栈) (返回栈为空时才算真正退出)
// 事务提交
fragmentTransaction.commit(); // 安排一个针对该事务的提交。提交并没有立刻发生,会安排到在主线程下次准备好的时候来执行
// 一个 fragmentTransaction 只能提交一次事务,因此要多次提交需要多个 fragmentTransaction
链式调用:
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.fragment_container, ExampleFragment.class, null)
.setReorderingAllowed(true) //可优化事务中涉及的 Fragment 的状态变化,以使动画和过渡正常运行
.addToBackStack("name") // name can be null
.commit(); // 注意, commit() 是带延时的, 如果刚 commit() 就打算获取会为null的
FragmentContainerView
FragmentContainerView 位于androidx.fragment.app包下面,是专门为Fragment设计的自定义布局。它扩展了FrameLayout,所以它可以可靠地处理Fragment事务,并且它还有其他功能来协调Fragment行为。FragmentContainerView应该被用作fragment的容器,通常在一个activity的xml布局中设置,例如:
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.fragment.app.FragmentContainerView>
FragmentContainerView也可以通过使用android:name属性来添加一个Fragment。FragmentContainerView将执行一次操作:
-
创建Fragment的新实例
-
调用Fragment.onInflate(Context, AttributeSet, Bundle)
-
执行FragmentTransaction将Fragment添加到适当的FragmentManager
可以选择包括 android:tag, 它允许你使用FragmentManager.findFragmentByTag(String)来检索添加的Fragment。
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.MyFragment"
android:tag="my_tag">
</androidx.fragment.app.FragmentContainerView>
FragmentContainerView不应该被用作Fragment用例之外的其他 viewgroup (FrameLayout, LinearLayout等)的替代。
FragmentContainerView只允许由Fragment.onCreateView(LayoutInflater, ViewGroup, Bundle)返回的view。试图添加任何其他视图将导致IllegalStateException。
对于大于17的api, FragmentContainerView禁用布局动画和transitions。否则,动画应该通过FragmentTransaction.setCustomAnimations(int, int, int, int)完成。如果animateLayoutChanges被设置为true或setLayoutTransition(LayoutTransition)被直接调用,将抛出UnsupportedOperationException。
在FragmentContainerView中,使用exit动画的片段在所有其他动画之前绘制。这确保了退出片段不会出现在视图的顶部。
FragmentTransaction
常用方法:
add(int containerViewId, Fragment fragment, String tag) // 向Activity state中添加一个Fragment
// add方法 : 参数 containerViewId 一般会传 Activity 中某个视图容器的 id. 如果 containerViewId 传 0,则这个 Fragment 不会被放置在一个容器中
// (不要认为 Fragment 没添加进来,只是我们添加了一个没有视图的 Fragment,这个Fragment 可以用来做一些类似 于service 的后台工作)
// Tag: Optional tag name for the fragment, to later retrieve the fragment with FragmentManager.findFragmentByTag(String)
remove(Fragment fragment) // 移除一个已经存在的Fragment
replace(int containerViewId, Fragment fragment) // 方法作用:类似于先 remove 掉视图容器所有的 Fragment,再add一个想要添加的 Fragment (当前视图为空时类似于 add)(会使fragment初始化, 有更优解(hide & show))
获得 Fragment 依附的 activity
getActivity(); /** 返回一个和此fragment绑定的FragmentActivity或者其子类的实例。 相反,如果此fragment绑定的是一个context的话,可能会返回null。 因为getActivity()大部分都是在fragment中使用到,而fragment需要依赖于activity, 所有我们在fragment里头需要做一些动作,比如启动一个activity, 就需要拿到activity对象才可以启动,而fragment对象是没有startActivity()方法的。*/ final public FragmentActivity getActivity() { return mHost == null ? null : (FragmentActivity) mHost.getActivity(); }
获取 Fragment
访问 FragmentManager
在 Activity 中访问
每个及其子类(如 AppCompatActivity) 都可以通过 getSupportFragmentManager()
方法访问 FragmentManager
在 Fragment 中访问
Fragment 也能够托管一个或多个子 Fragment。在 Fragment 内,您可以通过 getChildFragmentManager()
获取对管理 Fragment 子级的 FragmentManager
的引用。如果您需要访问其宿主 FragmentManager
,可以使用 getParentFragmentManager()
另外: 只要是继承 FragmentActivity 或者 FragmentActivity 本身,都可通过 getSupportFragmentManager()
方法获取到 FragmentManager(后面简称 FM)
若是 Fragment 嵌套 Fragment 的情况,父 Fragment 可通过 getChildFragmentManager()
方法获取 FM 来管理子 Fragment,同样,子 Fragment 也可通过 getParentFragmentManager()
来获取 FM
1. 通过 FragmentContainerView, 无论是静态还是动态生成的(其实都利用 FragmentTransaction 进行注册) 2. FragmentManager (getParentFragmentManager() & getSupportFragmentManager()), 为单例模式. 3. getChildFragmentManager() 获取的 FragmentManager,分别管理不同的分支, 彼此不相干
获取Fragment
-
通过动态注册的, 可通过容器(androidx.fragment.app.FragmentContainerView)的 Id 来获取
-
getParentFragmentManager().findFragmentById(Fragment的容器的id);
-
注册 Tag 的, 无论动态还是静态, 都可以通过 Tag 获取
-
getParentFragmentManager().findFragmentByTag("tag");
-
FragmentManager宿主环境 FragmentManager宿主环境指的是其管理类,通常在Activity中通过接口getSupportFragmentManager或者getFragmentManager获得FragmentManager对象, 可以认为Activity是FragmentManager的宿主环境类.FragmentManager宿主环境类状态变化时(如onStart, onResume, onStop等回调),FragmentManager的状态也随之更新(FragmentManager类有一个成员变量mCurState来记录当前状态),同时会同步其管理的所有Fragment的状态. 从用户的角度来看, FragmentManager的宿主环境类管理了一系列Fragment,并且Fragment随着FragmentMannager的宿主环境类的状态变化而变化.
-
当前级和向上一级 fragment 由 getParentFragmentManager() 管理, 子级 fragment 由 getChildFragmentManager() 管理
-
一个 fragment 树中, 每一级都有唯一的 FragmentManager 管理. (不同 Activity 对应不同的 fragment 树)
举例:
Activity1 | |
---|---|
getSupportFragmentManager(): FragmentManager{59e006d in HostCallbacks{9b594a2}}} | |
BlankFragment1 | BlankFragment2 |
getParentFragmentManager(): FragmentManager{59e006d in HostCallbacks{9b594a2}}} getChildFragmentManager(): FragmentManager{7fc58ee in BlankFragment1{f9e918f}}} | getParentFragmentManager(): FragmentManager{59e006d in HostCallbacks{9b594a2}}} getChildFragmentManager(): FragmentManager{f527e08 in BlankFragment2{2f6e084}}} |
ChildFragment1 | ChildFragment2 |
getParentFragmentManager(): FragmentManager{7fc58ee in BlankFragment1{f9e918f}}} getChildFragmentManager(): FragmentManager{2ccdedd in ChildFragment1{a77bc1c}}} | getParentFragmentManager(): FragmentManager{f527e08 in BlankFragment2{2f6e084}}} getChildFragmentManager(): FragmentManager{7bd8bd9 in ChildFragment2{ab649a1}}} |
优雅地动态添加
getSupportFragmentManager().beginTransaction() .setReorderingAllowed(true) // 不要省略 .add(R.id.fragment_container_view, ExampleFragment.class, null) .addToBackStack(null) .commit(); // 如果添加fragment时使用了 .addToBackStack(null),会将Fragment添加到回退栈中, 此时点击back键,回调Activity的onBackPressed()函数会把当前展示的fragment弹出回退栈 (要用栈管理就都用栈,要么都不入栈)