《第一行代码》核心知识点:活动Activity的儿子叫碎片Fragment
- 前言
- 四、活动(Activity)的儿子叫碎片(Fragment)
- 4.1 碎片是神马?
- 4.2 碎片的基本使用
- 4.3 向容器中动态添加碎片
- 4.4 活动与碎片之间通信方法
- 4.5 碎片的生命周期
- 4.6 使用限定符动态加载布局
- 参考书籍:第一行代码
前言
本文讲解Android中的碎片(Fragment),它与活动非常相似,通常可以称为迷你Activity。通过本文你可以了解什么是碎片,它的作用是什么,以及它的基本使用方法和生命周期。
四、活动(Activity)的儿子叫碎片(Fragment)
4.1 碎片是神马?
碎片可以理解为迷你型的活动,它可以作为一个UI片段嵌入到活动中,简单来说一个活动中可以嵌入多个碎片。通过活动可以有效的充分利用屏幕空间,如展示一个新闻界面,普通的手机屏幕,我们将标题和内容分为放在两个活动中,如下图所示。
但是,如果一个平板采用这样的设计方案,会导致标题页有很多的空余空间,如下图所示:
此时,就可以采用碎片解决这个问题,可以在一个活动中嵌入两个碎片,一个碎片用于展示标题,另一个碎片用于展示内容,如下图所示。
4.2 碎片的基本使用
碎片的具体使用方法非常简单,它与上面3.3讲解的创建自定义控件类似,其实也可以把碎片看做一种特殊的自定义控件,具体步骤如下:
- 创建碎片布局(xml)
- 创建碎片类继承自Fragment,重写onCreateView加载创建的布局
- 在活动布局中引用该碎片类即可(像用其它普通控件一样)
如将一个活动中嵌入两个碎片,一个碎片在左侧,一个碎片在右侧
- 创建两个碎片的布局
a. 左侧碎片布局
<?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">
<Button
android:id="@+id/btn_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Button"/>
</LinearLayout>
b. 右侧碎片布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#E41818"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="20sp"
android:text="right fragment"/>
</LinearLayout>
- 创建两个碎片对应的继承自Fragment的类
a. 左侧碎片类
public class LeftFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.left_fragment, container, false);
return view;
}
}
b. 右侧碎片类
public class RightFragment extends Fragment {
//为碎片创建视图
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.right_fragment, container, false);
}
}
- 在活动在引入这两个碎片
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<!--name需要完整的包名-->
<fragment
android:id="@+id/left_fragment"
android:name="com.xiaomi.fragmenttest.LeftFragment"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent" />
<fragment
android:id="@+id/right_fragment"
android:name="com.xiaomi.fragmenttest.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"/>
</LinearLayout>
- 运行效果
4.3 向容器中动态添加碎片
核心代码如下
//获取碎片管理器
FragmentManager fragmentManager = getSupportFragmentManager();
//获取碎片事务
FragmentTransaction transaction = fragmentManager.beginTransaction();
//向容器中添加或者替换碎片,一般用replace方法
transaction.replace(R.id.right_fragment,fragment);
//transaction.add(R.id.right_layout,fragment);
//将fragment添加到返回栈中
transaction.addToBackStack(null);
//提交事务
transaction.commit();
4.4 活动与碎片之间通信方法
碎片虽然嵌入在活动中,但是碎片和活动分别是一个单独的类,因此如何实现在活动调用碎片的方法,或者在碎片调用活动的方法呢?
- 在活动调用碎片的方法
FragmentManager类下提供了类似findViewById的方法findFragmentById方法来获取碎片的实例,获取碎片实例后,我们就可以对碎片的方法进行调用了。
RightFragment rightFragment = (RightFragment) getSupportFragmentManager().findFragmentById(R.id.right_fragment);
- 在碎片调用活动的方法
碎片中提供了getActivity方法用来获取该碎片所在活动的实例,通过该实例可以调用活动中的方法。
MainActivity mainActivity = (MainActivity) getActivity();
4.5 碎片的生命周期
- 碎片的四种状态
因为碎片嵌入在活动中,所以碎片的状态与活动相关联,随着活动状态的变化,碎片也有相应的变化。活动的状态特征请参考上文2.3.2
- 运行状态:碎片可见,且活动处于运行状态
- 暂停状态:碎片可见,活动处于暂停状态
- 停止状态:碎片不可见,当活动处于停止状态,或者调用FragmentTransaction的remove,replace方法将碎片移除,并在事务提交之前调用addToBackStack方法将移除的碎片添加到了返回栈中,此时的碎片进入停止状态。
- 销毁状态:当活动被销毁,或者调用FragmentTransaction的remove,replace方法将碎片移除,但在事务提交之前没有调用addToBackStack方法将移除的碎片添加到了返回栈中,此时的碎片进入销毁状态。
- 碎片的整个生命周期的回调函数
碎片的回调函数与活动类似,只是比活动多了几个回调方法:
- onAttach 碎片与活动建立关联时调用
- onCreateView 为碎片创建视图(加载碎片布局)时调用
- onActivityCreated 确保碎片与相关联的活动已经创建完成时调用
- onDestroyView 当与碎片关联的视图移除时调用
- onDetach 当碎片和活动解除关联时调用
我们可以将一个碎片类的所有回调函数打印log,通过log了解回调函数的调用时期,示例代码:
public class RightFragment extends Fragment {
private static final String TAG = "RightFragment";
//碎片与活动建立关联
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(TAG, "onAttach: 1. 碎片与活动建立关联");
}
//创建活动
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: 2. 创建活动");
MainActivity mainActivity = (MainActivity) getActivity();
}
//为碎片创建视图
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.d(TAG, "onCreateView: 3. 为碎片创建视图");
return inflater.inflate(R.layout.right_fragment, container, false);
}
//确保碎片与相关联的活动已经创建完成
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated: 4. 确保碎片与相关联的活动已经创建完成");
}
//活动启动
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "onStart: 5. 活动启动");
}
//活动一切准备就绪
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "onResume: 6. 活动一切准备就绪");
}
//活动进入暂停状态 ==>onResume
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "onPause: 7. 活动进入暂停状态");
}
//活动停止 ==>onStart
@Override
public void onStop() {
super.onStop();
Log.d(TAG, "onStop: 8. 活动停止");
}
//与碎片关联的视图移除 ==>onCreateView
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "onDestroyView: 9. 与碎片关联的视图移除");
}
//销毁活动
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: 10. 销毁活动");
}
//碎片和活动解除关联==>OnAttach
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "onDetach: 11. 碎片和活动解除关联");
}
}
当活动启动时log如下:
4.6 使用限定符动态加载布局
如何让程序自动根据当前屏幕的大小动态加载合适的布局呢?
Android提供了一系列限定符,如下图所示:
比如我们在res目录下创建一个layout-large文件夹,然后在这个文件夹下创建针对大屏幕情况下的主页布局activity_main.xml文件;我们也可以在res目录下创建一个layout-xlarge文件夹,然后在这个文件夹下创建针对超大屏幕情况下的主页布局activity_main.xml文件;
上表的一系列限定符系统有默认的大小,比如多大的屏幕算大,系统有自己默认的值,如果我们想要自己设定多大的大小算大,可以采用宽度限定符。如我们在res目录下创建一个layout-sw600dp文件夹,那么当屏幕宽度大于600dp则加载这个文件夹下的布局,否则加载默认的layout文件夹下的布局。
参考书籍:第一行代码
链接:https://pan.baidu.com/s/1aXtOQCXL6qzxEFLBlqXs1Q?pwd=n5ag
提取码:n5ag