一、Fragment 初探
1、诞生的原因
因为屏幕大小差距过大,一些界面在手机上看起来非常美观,但在平板上看起来可能会有控件被过分拉长、元素之间空隙过大等情况。为了让界面在平板更好地展示,Android 自3.0版本开始引入了碎片的概念。
2、定义
那碎片是什么呢?碎片是一种可以嵌入在Activity当中的UI片段。碎片和Activity很像,同样都能包含布局,同样都有自己的生命周期。
注意: 1、Fragment不能独立存在,必须嵌入到Activity中 2、Fragment具有自己的生命周期,接收它自己的事件,并可以在Activity运行时被添加或删除 3、Fragment的生命周期直接受所在的Activity的影响。如:当Activity暂停时,它拥有的所有Fragment都暂停
二、Fragment 的使用方式
我们先新建一个项目,名为 FragmentTest ,然后一起来学习一下如何使用碎片吧!(虽然碎片起初是为了兼容平板而存在,但是在手机上也应用很广泛,所以在这里我还是使用了手机模拟器,感兴趣的同学也可以用平板模拟器,碎片的使用方法也是一样的,可参考《第一行代码》)
1、静态加载碎片
我们先在Activity的布局文件 activity_main.xml 里写一个简单的Button控件
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
android:textSize="15sp" />
接着我们新建一个碎片布局 first_fragment.xml ,并放置了一个 TextView 用于显示文本,为了等会更好地看到效果,我们将这个布局的背景色设置成别的颜色(我这里是水色)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00FFFF"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is the first fragment"
android:textSize="25sp" />
</LinearLayout>
接着我们新建一个 FirstFragment 类,并让它继承 Fragment 。此时会有两个不同包下的 Fragment 让我们选择 一个是AndroidX库中的androidx.fragment.app.Fragment ,一个是系统内置的android.app.Fragment ,这里我们选择AndroidX库中的Fragment ,因为它可以让Fragment的特性在所有Android系统版本中保持一致,而系统内置的Fragment在Android 9.0版本中已被废弃。
然后我们重写onCreateView()方法,在这个方法里通过LayoutInflater的inflate()方法将刚刚定义的first_fragment布局加载进来
public class FirstFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.first_fragment, container, false);
}
}
inflate() 方法带有三个参数: inflater:想要扩展的布局的资源 ID container:布局将插入到的父级 ViewGroup(来自 Activity 的布局) savedInstanceState:保存的状态,在恢复fragment时,提供上一片段实例相关数据的 Bundle 如果第三个参数为true那么返回可能就不是View,当为false的时候返回就是View 在这里布尔值为 false,因为系统已将扩展布局插入 container,而传递 true值会在最终布局中创建一个多余的视图组
接下来我们去修改 activity_main.xml 中的代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
android:textSize="15sp" />
<fragment
android:id="@+id/first_fragment"
android:name="com.example.fragmenttest.FirstFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
我们使用了< fragment > 标签在布局中添加Fragment,这里还需要通android:name属性来显式声明要添加的Fragment类名,注意一定要将类的包名也加上。在这里还使用了weight属性,将剩下来的屏幕空间全部给了fragment ?? 静态加载碎片到这里就写好了,现在可以运行一下程序看看效果了
2、动态添加碎片
上面这个例子只是碎片的简单用法,在真正的项目中几乎没有什么实际的作用,接下来我们学习厉害一点的,在程序运行时动态地添加碎片到活动当中。
我们在前面代码的基础上继续完善,首先,新建一个布局文件second_fragment.xml ,和前面的first_fragment.xml 基本相同,将它的背景色和显示的文字稍加修改一下即可,便于区分一下这是新的碎片
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFF00"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is the second fragment"
android:textSize="25sp" />
</LinearLayout>
然后新建 SecondFragment 类,和前面一样的套路啦
public class SecondFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.second_fragment, container, false);
}
}
接下来修改 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"
android:textSize="15sp" />
<FrameLayout
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
可以看到,我们将fragment换成FrameLayout(帧布局),这是安卓中最简单的一种布局,所有的控件默认都会摆放在布局的左上角,同一时刻只能看到最上面的控件,后续添加的控件会覆盖前一个。由于这里仅需要在布局里放入一个Fragment,不需要任何定位和嵌套,因此非常适合使用FrameLayout。
?? 重点来啦!接着我们来看看如何实现动态添加碎片的功能,完善一下java文件
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
button.setOnClickListener(this);
replaceFragment(new FirstFragment());
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button:
replaceFragment(new SecondFragment());
break;
default:
break;
}
}
private void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.fragment, fragment);
transaction.commit();
}
}
我们先实例化Button,并注册一个点击事件,点击button时,会调用replaceFragment()方法将下侧碎片替换成另一个。FragmentManager能够实现管理activity中fragment;FragmentTransaction对fragment进行添加,移除,替换,以及执行其他动作。
动态添加碎片步骤: 1、创建待添加Fragment的实例 2、调用getSupportFragmentManager()方法获取FragmentManager实例 3、调用beginTransaction()方法开启一个事务 4、使用replace()方法向容器内添加或替换Fragment,需要传入容器的id和待添加的Fragment实例 5、调用commit()方法提交事务
?? 现在我们可以重新运行一下程序,看看效果啦!
3、在碎片中模拟返回栈
换言之,就是返回到之前的上一个碎片。 接着上面的运行结果,我们已经点击过了按钮,此时屏幕上显示的是第二个碎片,若这时按下Back键,你会发现,咦?怎么直接退出了,我只是想回到上一个碎片,那怎么办呢?这时我们可以使用FragmentTransaction提供的addToBackStack()方法,传入一个用于描述返回栈状态的参数,一般传入null即可。
private void replaceFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.fragment, fragment);
transaction.addToBackStack(null);
transaction.commit();
}
现在我们再来运行一下程序看看,是不是按下Back键后,返回到第一个碎片,继续按下Back键,第一个碎片会消失,再次按下Back键后才会退出?
4、碎片和活动之间的交互
在Activity中调用Fragment里的方法:先得到相应Fragment的实例,再调用Fragment里的方法(findFragnentById()方法,专门用于从布局中获取碎片的实例)
FirstFragment firstFragment = getSupportFragmentManager()
.findFragmentById(R.id.first_fragment);
在Fragment中调用Activity里的方法:调用getActivity() 方法来得到和当前碎片相关联的活动实例,再调用Activity里的方法
MainActivity activity = getActivity();
在这里我就不举例子详细介绍了,想要继续了解的同学可以自己看看相关博客或者《第一行代码》呀!
三、Fragment 的生命周期
Fragment的生命周期和Activity的生命周期很像,在这里我就不详细讲解啦,感兴趣的同学可以自己去了解一下
OK,讲完啦!但这只是碎片的基础知识,进一步的学习和应用需要你们自己去探索哦!最后,悄悄问一句,你的冬令营作品开始做了吗??期待看到大家的作品哦?
|