前言:除了安卓四大组件(
activity
、service
、content provider
、broadcast receiver
)之外,还有一个最重要的知识点是:fragment
。
1. Fragment 是什么?
Fragment 也叫碎片,片段(相当于 迷你的 Activity ,或者是 Activity 的模块化的组件)。是 Google 在 Android 3.0 引入的,主要为了给大屏幕的 UI 提供支持的。
实际开发中两个作用:
- 相当于海报的功能,可以到处粘贴。就是把一部分业务逻辑和 UI 封装在一起,方便灵活的用在各个地方。
- 适配 Pad 和手机。只用一套代码适配,方便重用,提高程序的复用性和可维护性。
Fragment 可以理解为一个迷你的 Activity 或者是 Activity 的模块化的组件,它有自己的生命周期与显示界面,我们可以利用多个 Fragment 嵌套在 Activity 达到以下的功能,如适配平板,或适配横竖屏幕,或者在程序运行的过程中动态的更改我们的 UI 界面。如下图:
上图是显示应用运行在手机情况下,从一个列表页面跳转到详细页面的例子。如果我们不使用 Fragment 的情况,当应用运行在平板上面的情况,就只能显示放大版的手机显示的界面,为了利用平板的特性,Android 在 3.0 版本中提供了Fragment 技术,我们就可以将列表与内容以组件的方式插入,在手机上分屏显示两个内容,在平板上面就可以左右显示两个内容。
你可以将片段视为 Activity 的模块化组成部分,它具有自己的生命周期,能接收自己的输入事件,并且你可以在 Activity 运行时添加或删除片段(有点像你可以在不同 Activity 中重复使用的“子 Activity ”)。
片段必须始终嵌入在 Activity 中,其生命周期直接受宿主 Activity 生命周期的影响。
2. 如何使用 Fragment
2.1 创建 Fragment
如果需要使用 Fragment 的话,需继承 Fragment 类,目前有两个 Fragment 类:
-
import android.support.v4.app.Fragment
提供的向下兼容类(支持3.0以下版本) -
import android.app.Fragment
提供3.0以上版本的类(只支持3.0以上的版本)
继承 Fragment 至少需实现以下方法:
-
onCreateView()
系统会在片段首次绘制其用户界面时调用此方法,并将创建的UI界面返回。
import android.support.v4.app.Fragment;
public class FirstFragment extends Fragment {
@Nullable
@Override
/* 系统会在片段首次绘制其用户界面时调用此方法,并将创建的UI界面返回 */
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
/* 返回一个view,把布局返回去 */
View view = inflater.inflate(R.layout.fragment_first, null);
return view;
}
}
2.2 使用 Fragment
方式1:在 Activity 的布局文件中添加一个 Fragment 控件
<!-- 使用 XML 添加 fragment 子模块 -->
<fragment android:name="com.example.testfragment.FirstFragment"
android:id="@+id/abc"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:name 属性指定要在布局中实例化的 Fragment 类。注意如果是在 xml 中声明的 Fragment 的话,必须给这个 Fragment 设置 id 或者 tag。
方式2:动态添加一个 Fragment 到 Activity 指定的控件内部
FirstFragment firstFragment = new FirstFragment();
// 获取到低版本兼容的 FragmentManager( getFragmentManager(); 支持3.0以上版本 )
FragmentManager fm = getSupportFragmentManager();
// 1.开启一个 Fragment 事务(即可对 Fragment 进行操作)
FragmentTransaction transaction = fm.beginTransaction();
// 2.添加一个 Fragment 到 Activity 指定的控件内部(每调用一次 add 方法,就添加一次,会重复叠加)
// (参数:布局容器 FrameLayout 的 ID,fragment 对象,动态添加时需要设置一个 id/tag)
transaction.add(R.id.content, firstFragment, "abc");
// 3.提交(对 Fragment 进行任何操作都必须提交)
transaction.commit();
2.3 传值给 Fragment
- 通过Fragment的方法、或者回调进行传值
- 给 Fragment 传递参数
setArguments
// 页面之间的传值:Bundle是传递数据的集合
Bundle bundle = new Bundle();
bundle.putString("title", "Fragment的标题");
// bundle.putCharSequence("title", title);
// 通过 Bundle 把参数从 Activity 中传给 Fragment
firstFragment.setArguments(bundle);
- 在 Fragment 中接收参数
getArguments
/* 接收传递的参数 */
Bundle bundle = getArguments();
String title = bundle.getString("title");
3. Fragment 的生命周期
创建的生命周期:
-
onAttach
:Fragment 开始与 Activity 关联。 -
onCreate
:系统会在创建片段时调用此方法。你应该在实现内初始化您想在片段暂停或停止后恢复时保留的必需片段组件。 -
onCreateView
:系统会在片段首次绘制其用户界面时调用此方法。 要想为你的片段绘制 UI,你从此方法中返回的 View 必须是片段布局的根视图。如果片段未提供 UI,你可以返回 null。 -
onActivityCreated
:Activity onCreate 完成的回调。 -
onSaveInstanceState
:移除的生命周期,处理 Fragment 需要保存数据的方法。 -
onDestroyView
:将 Fragment 的 View 试图从UI中移除。 -
onDetach
:将 Fragment 与 Activity 取消关联。
4. Fragment 的操作 FragmentTransaction
我们可以使用 FragmentTransaction 来对 Fragment 进行操作,如 add、replace、attach、detach、remove、show、hide。
4.1 add() 方法
作用:将一个 Fragment 添加到一个页面,可以重复叠加
add(int containerViewId, Fragment fragment, String tag)
- containerViewId:将 Fragment 加入的 ViewGroup 的 id
- fragment:被操作的 Fragment
- tag:被操作的 Fragment 的标示,我们操作成功后可以使用这个 tag 找到相应的 Fragment
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.content, firstFragment, "abc");
transaction.commit();
4.2 replace() 方法
作用:将容器内的 Fragemnt 移除后,再进行添加
replace(int containerViewId, Fragment fragment, String tag)
- containerViewId:将 Fragment 替换的 ViewGroup 的 id
- fragment:被操作的 Fragment
- tag:被操作的 Fragment 的标示,我们操作成功后可以使用这个 tag 找到相应的 Fragment
FragmentTransaction transaction = fm.beginTransaction();
transaction.replace(R.id.content, firstFragment, "abc");
transaction.commit();
4.3 detach() 方法
作用:将一个 Fragment 从 UI 上面解绑
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.detach(fragment);
- 注意解绑并不是删除,我们还可以通过其他方法把解绑的Fragment重新绑定到UI上面。
4.4 attach() 方法
作用:将一个 Fragment 重新绑定到 UI
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.attach(fragment);
transaction.commit();
4.5 remove() 方法
作用:将一个 Fragment 从 Activity 删除
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.remove(fragment);
transaction.commit();
4.6 show() 方法
作用:显示一个 Fragment(不会调用任何生命周期方法,常用这个)
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.show(fragment);
transaction.commit();
4.7 hide() 方法
作用:隐藏一个 Fragment(不会调用任何生命周期方法,常用这个)
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag("abc");
FragmentTransaction transaction = fm.beginTransaction();
transaction.hide(fragment);
transaction.commit();
4.8 commit() 和 commitAllowingStateLoss() 方法的区别
Activity 在以下的操作下容易引起回收并触发 onSaveInstanceState()
- 按下 HOME 键
- 按下电源按键(关闭屏幕显示)时
- 屏幕方向切换
- Activity 跳转的时候
以上几种情况下不能进行状态的提交,如果提交的话会出现以下错误
Can not perform this action after onSaveInstanceState
出现这个错误的时候,我们有两个解决方法:
- 不在 onSaveInstanceState() 后调用 commit() 方法
- 使用 commitAllowingStateLoss() 方法
为了防止在 onSaveInstanceState 方法中调用 commit 会报错,我们使用 commitAllowingStateLoss,可以防止状态丢失报错!
5. Fragment 与返回键
默认情况下,Fragment 是不会响应返回键的。如果需要做到类似Activity回退到上一个界面这样的效果,必须将 FragmentTransaction 加入返回栈。
FragmentTransaction transaction = fm.beginTransaction();
transaction.add(R.id.content, firstFragment, "abc" + index);
// 加入返回任务栈,可通过系统返回按钮控制返回
transaction.addToBackStack(null); // 任务名可为空,也可通过任务名控制多层返回
addToBackStack(String name);
name 参数代表这次 FragmentTransion 的名称,我们可以根据这 个名称找到相应的操作,并进行回退动作。也可以传null,代表不记录该次操作的名称。
如果我们将某次操作加入回退栈的话,我们有以下几种方式进行回退:
- 使用返回键系统自动回退到上一次操作前的状态。
- 使用 popBackStack 回退到指定的操作的状态。
popBackStack()
回退到上一次操作前的状态 。
popBackStack(String name, int flags)
回退到某次 name 的操作状态。
flag 为0
表示回退到任务 name 的操作状态的这一步。
flag 为POP_BACK_STACK_INCLUSIVE
表示回退到任务 name 的操作状态的上一步。
popBackStack(int id, int flags) 回退某个 id 的操作状态,id 为 commit() 返回的。
@Override
public void onBackPressed() {
Log.i("11", "点击了系统返回按钮");
super.onBackPressed();
FragmentManager fm = getSupportFragmentManager();
/* 返回到上一页 */
// fm.popBackStack();
/* 返回到指定页(参数:加入返回任务栈的任务名, 标记) */
fm.popBackStack("abc", 0);
}
6. Fragment 动画
默认情况下 Fragment 显示和隐藏是不显示动画的,不过 FragmentTransaction 提供了三种显示的动画的方式:
-
setTransition
:使用系统提供的默认显示动画 -
setCustomAnimations
:使用自定义动画。 -
setTransition (int transit)
:可以使用系统提供的默认动画,可供选择的有 TRANSIT_NONE,TRANSIT_FRAGMENT_OPEN,or TRANSIT_FRAGMENT_CLOSE -
setCustomAnimations (int enter, int exit, int popenter, int popexit)
:能够使用自定义动画。
enter 设置Fragment 进入动画
exit 设置Fragment 退出动画
popenter popback 后回滚状态后上一个 Fragment 的动画
popexit popback 后回滚状态后当前的Fragment的动画
7. Fragment 间的交互(通信)
使用回调的方式,让 FragmentActivity 充当中间交互的桥梁。
先定义一个接口(一种规范,这里相当于 FragmentActivity 的基类,方便多个不同的 Activity 使用),再在 FragmentActivity 中实现接口中定义的方法。
1》定义一个接口(OneOnClickListener.java)
package net.cbi360.testfragment;
public interface OneOnClickListener {
public void OneOnClick(int index);
}
2》实现接口的方法
public class CommonActivity extends FragmentActivity implements OneOnClickListener {
TwoFragment two;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
FragmentManager fm = getSupportFragmentManager();
OneFragment one = (OneFragment)fm.findFragmentByTag("one");
one.setOneOnClickListener(this);
two = (TwoFragment)fm.findFragmentByTag("two");
}
public void OneOnClick(int index) {
String msg = "点击了" + index + "按钮";
Log.i("CommonActivity", msg);
two.setMessage(msg);
}
}
public class OneFragment extends Fragment {
OneOnClickListener activity;
int index = 0;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (null != savedInstanceState) {
// 恢复参数的值
index = savedInstanceState.getInt("index");
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_one, null);
Button button = (Button)view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Log.i("OneFragment", "点击了按钮");
index++;
activity.OneOnClick(index);
}
});
return view;
}
// public void setActivity(CommonActivity activity) {
// this.activity = activity;
//
// }
// 解耦(减少关联性,方便不同activity重用)
public void setOneOnClickListener(OneOnClickListener activity) {
this.activity = activity;
}
// @Override
// public void onActivityCreated(@Nullable Bundle savedInstanceState) {
// super.onActivityCreated(savedInstanceState);
// // 也可以从生命周期方法中拿到 Activity
// activity = (OneOnClickListener)getActivity();
// }
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
// 保存参数的值
outState.putInt("index", index);
}
}
- 解决页面翻转后,数据丢失问题
/* 这个方法的作用是:当页面的状态/参数值丢失时我们来进行保存(缓存页面参数的值) */
@Override
// 当页面翻转时/按下Home键时/按下电源键时/Activity跳转时,会触发下面这个方法
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
// 保存值
outState.putInt("index", index);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 获取保存的值(注意:先判断一下是否有上次缓存的信息,有的话再获取)
if (null != savedInstanceState) {
index = savedInstanceState.getInt("index");
}
}
网友评论