本文是对Android Fragment 使用解析的学习笔记
一、作用
1. 作用
-
模块化
不必把所有的代码放在 Activity 中,而是放在各自的 Fragment 中。 -
可重用
一个 Activity 可以有多个 Fragment,一个 Fragment 可以被多个 Activity 使用 -
可适配
不同尺寸的屏幕适配
2. 特点
- Fragment 依赖于 Activity,不可独立存在
- 一个 Fragment 可被多个 Activity 重用,一个 Activity 中可以有多个 Fragment
- Fragment 有自己的生命周期,并接受事件输入
- Activity 中可动态地添加、删除 Fragment
二、概念
1. Fragment 生命周期
1)如果希望保留用户数据:
hide()/show()
2)如果不希望保留用户数据:可以先remove()
再add()
,或者直接replace()
3)如果你的当前Activity一直存在,那么在不希望保留用户操作的时候,你可以优先使用detach:remove()
会销毁整个 Fragment 实例,detach()
只是销毁视图结构,实例并不会销毁。
2. 核心类
(1)Fragment
(2)FragmentManager
(3)FragmentTransaction
3. Fragment 回退栈
三、使用
1. 创建 Fragment
public class MyFragment extends Fragment {
private static final String ARG_PARAM = "key1";
public static Fragment newInstance(String str) {
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString(ARG_PARAM, str);
fragment.setArguments(bundle);
return fragment;
}
private Activity mActivity;
private String mParam;
@Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = (Activity) context;
mParam = getArguments().getString(ARG_PARAM);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_1, container, false);
TextView view = root.findViewById(R.id.text);
view.setText(mParam);
return root;
}
}
(1)一般提供一个newInstance()
方法用于创建 Fragment 实例
- 通过
setArguments()
传递给 Fragment 的参数
该方法中通过
setArguments()
设置 Activity 要传递给 Fragment 的参数。相比于构造函数中传入数据地方法,setArguments()
中的数据,在 Fragment 被异常终止后再次恢复时,这些数据仍会保留。
setArguments()
方法必须在 fragment 创建以后,添加给 Activity 前完成。千万不要,首先调用了 add,然后设置 arguments。
(2)onAttach()
-
通过
getArguments()
获取 Activity 传递过来的数据 -
将
Context context
强转为 Activity,获取绑定的 Activity 对象
mActivity = (Activity) context;
。不建议通过getActivity()
,而是将context
进行强转
(3)onCreateView()
inflater.inflate(R.layout.fragment_1, container, false);
加载布局时中inflate()
方法的第三个参数设置为false
,如果设置为true
会造成 Fragment 的布局被重复添加到container
中,产生异常。
Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
2. 将 Fragment 添加到 Activity 中
(1)静态添加
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:layout_width="wrap_content"
android:layout_height="wrap_content"
class="com.example.myapplication.mvp.fragment.MyFragment"/>
</LinearLayout>
(2)动态添加
首先,需要在 Activity 的布局文件中有一个容器放置 Fragment,一般是 FrameLayout。
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
然后在 Activity 的代码中将 Fragment 添加到 Activity 中。
public class MyActivity {
private void addFragment() {
MyFragment myFragment = (MyFragment) getSupportFragmentManager().findFragmentByTag(MyFragment.TAG);
if (myFragment == null) {
myFragment = new MyFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container, myFragment, MyFragment.TAG);
}
}
}
getSupportManager()
因为用的是support.v4
包中的 Fragment,因此也要用对应的getSupportManager()
获取 Fragment 管理器,该管理器可管理该 Activity 中嵌入的一级 Fragment。
- 在创建 Fragment 实例之前先判空,防止 Fragment 重叠
if (myFragment == null)
当 Activity 因为配置发生变化或内存不足被系统回收,造成重新创建时,我们 Fragment 会被保存下来,但是会创建新的 FragmentManager,新的 FragmentManager 会首先会去获取保存下来的 fragment 队列,重建 fragment 队列,从而恢复之前的状态。这样就造成了 Fragment 重叠(重复添加)。
- 添加 Fragment 时为其设置TAG
getSupportFragmentManager().beginTransaction().add(R.id.container, myFragment, MyFragment.TAG);
设置 TAG 是为了方便在 Activity 中查找 Fragment。findFragmentByTag()
MyFragment myFragment= (MyFragment) getSupportFragmentManager().findFragmentByTag(MyFragment.TAG);
建议使用动态添加,灵活性高。
3. Activity 中获取 Fragment 实例
- 若 Activity 中包含自己管理的 Fragment 的引用
那么在 Activity 中可以通过引用直接访问 Fragment 中所有的 public 方法
- 若 Activity 中不包含...
每个Fragment都有一个唯一的TAG或者ID,可以通过getFragmentManager.findFragmentByTag()
或者findFragmentById()
获得任何Fragment实例,然后进行操作。
4. Fragment 中获取 Activity 实例
-
通过
getActivity()
获取当前绑定的 Activity 的实例,然后进行操作。 -
通过在
onAttach(Context context)
中将Context
强转获得 Activity 实例
public class MyFragment extends Fragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
mActivity = (Activity) context;
}
}
四、注意
1. Fragment 和 Activity 之间进行通信
(1)Activity 向 Fragment 传递数据
- Activity 创建 Fragment 时传递数据
建议通过为 Fragment
setArguments(Bundle bundler)
的方式,不建议通过带参数的Fragment 构造函数传递。
因为:通过前一方式创建的 Fragment,在内存紧张时 Fragment 被杀死又恢复时,这些数据能够保留。
private static final String ARG_PARAM = "key1";
public static Fragment newInstance(String str) {
MyFragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString(ARG_PARAM, str);
fragment.setArguments(bundle);
return fragment;
}
- 其他情况传递数据
获取 Fragment 对象,调用 Fragment 对象的方法,将数据传递过去。
Fragment 的实现如下,并提供方法setName(String name)
。
public class MyFragment extends Fragment {
private String name;
public void setName(String name) {
this.name = name;
}
}
在 Activity 中调用fragment.setName("xx");
。
(2)Fragment 向 Activity 传递数据
Android Fragment 真正的完全解析(下),该文中提供了两种最佳实践,选一个即可。在 Fragment 中声明了一个接口,来回调其点击事件。
因为考虑到 Fragment 的重复使用,所以必须降低 Fragment 和 Activity 的耦合。而且Fragment更不应该直接操作别的Fragment,毕竟Fragment操作应该由它的管理者Activity来决定。
最佳实践达到的效果是:Fragment 不和任何 Activity 耦合,任何 Activity 都可以使用
Fragment 中创建一个接口,Activity 实现该接口。
public class MyFragment extends Fragment {
public interface OnMuyClickListener {
void onMyClick();
}
private OnMuyClickListener mOnMuyClickListener;
public void setOnMuyClickListener(OnMuyClickListener listener) {
mOnMuyClickListener = listener;
}
public void test() {
if (mOnMuyClickListener != null) {
mOnMuyClickListener.onMyClick();
}
}
}
Activity 中添加 Fragment 时为其设置监听事件
private void addFragment() {
MyFragment myFragment = (MyFragment) getSupportFragmentManager().findFragmentByTag(MyFragment.TAG);
if (myFragment == null) {
myFragment = new MyFragment();
myFragment.setOnMuyClickListener(new MyFragment.OnMuyClickListener() {
@Override
public void onMyClick() {
//...
}
});
getSupportFragmentManager().beginTransaction().add(R.id.container, myFragment, MyFragment.TAG);
}
}
Fragment 在合适的地方调用接口中的方法mOnFragmentInteractionListener.onItemClick("hello");
将数据传递过去。
(3)Fragment 向 Fragment 传递数据
Fragment 之间没有任何依赖关系,如果二者需要通信的话建议通过 Activity 作为中介,不要直接通信。
网友评论