Fragment的概述和使用
Android 自 3.0 版本开始引入了碎片的概念,它可以让界面在平板上更好地展示,支持更加动态和灵活的UI设计。
一、什么是碎片
碎片(Fragment)是一种可以嵌入在活动当中的 UI 片段,它能让程序更加合理和充分地利 用大屏幕的空间
Fragment
表示应用界面中可重复使用的一部分。Fragment 定义和管理自己的布局,具有自己的生命周期,并且可以处理自己的输入事件。Fragment 不能独立存在,而是必须由 Activity 或另一个 Fragment 托管。Fragment 的视图层次结构会成为宿主的视图层次结构的一部分,或附加到宿主的视图层次结构。
二、Fragment的生命周期
和活动一样,碎片也有自己的生命周期,并且它和活动的生命周期很相似。
一个Fragment的生命周期:
-
onAttach()
onAttach()在fragment与Activity关联之后调用。需要注意的是,初始化fragment参数可以从getArguments()获得,但是,当Fragment附加到Activity之后,就无法再调用setArguments()。所以除了在最开始时,其它时间都无法向初始化参数添加内容。
-
onCreate()
fragment初次创建时调用。尽管它看起来像是Activity的OnCreate()函数,但这个只是用来创建Fragment的。此时的Activity还没有创建完成,因为我们的Fragment也是Activity创建的一部分。所以如果获取当前fragment的控件,将会获取不到。如果想要获得Activity相关联的资源,必须在onActivityCreated中获取。
-
onCreateView()
在创建Fragment视图并加载的时候调用
@Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //第一个参数为布局文件 //第二个参数为ViewGroup root,表示父窗口,如果返回null,表示没有父窗口,则该Fragment无法显示 //第三个参数,attchToRoot,如果为true,则表示,当前Fragment的xml的根节点作为整个Activity的根节点,此时Acrtivity的其他控件都将不显示。 View view = inflater.inflate(R.layout.fragment1, container, false); return view; }
通过LayoutInflater的inflate()方法,来绑定xml文件,从而完成视图创建
注意: 当第一次绘制Fragment的UI时系统调用这个方法,该方法将返回一个View,如果Fragment不提供UI也可以返回null。如果继承自ListFragment,onCreateView()默认的实现会返回一个ListView,所以不用自己实现。
此方法在Activity的onCreate()方法结束后调用。只有Activity已经创建完成。
注意: onActivityCreated(),因为该方法是在Activity中的onCreate方法执行完成后调用,所以在onActivityCreated()调用之前 Activity的onCreate可能还没执行,所以不能在onCreateView()中进行 与Activity相关的UI操作,而应该在onActivityCreated()中进行与Activity相关的UI操作,而onCreateView中只进行UI的显示操作。
-
onStart()
启动Fragment的时候回调,这个时候Fragment可见
-
onResume()
Fragment变为活动状态获取焦点的时候是回调,这个时候Fragment已经完全展示在前台,并且可以和用户交互
-
onPause()
Fragemnt变成非活动状态失去焦点的时候调用,注意这个时候Fragment还是可见的,只是不能和用户交互了而已
Fragment变成不可见的时候调用。这个时候Fragment还是活着的,只是可能别加入到了Fragment的回退栈中
Fragment中的布局被移除的时候调用
Fragment被销毁的时候调用
Fragment和Activity解除关联的时候调用
三、Fragment的使用
1、静态添加Fragment
- 新建两个Fragment类,分别为Fragment1和Fragment2
可以通过这样创建一个Fragment,也可以自己手动创建一个Fragment类,然后继承Fragment类,再重写需要的方法,在创建对应的布局即可。
- 第一个Fragment1类,是我手动创建的
public class Fragment1 extends Fragment {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//第一个参数为布局文件
//第二个参数为ViewGroup root,表示父窗口,如果返回null,表示没有父窗口,则该Fragment无法显示
//第三个参数,attchToRoot,如果为true,则表示,当前Fragment的xml的根节点作为整个Activity的根节点,此时Acrtivity的其他控件都将不显示。
View view = inflater.inflate(R.layout.fragment_1, container, false);
return view;
}
@Override
public void onPause() {
super.onPause();
}
}
对应fragment_1.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">
<TextView
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我是第一个fragment" />
</LinearLayout>
- 第一个Fragment2类,是自动创建的
public class Fragment2 extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
public Fragment2() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment Fragment2.
*/
// TODO: Rename and change types and number of parameters
public static Fragment2 newInstance(String param1, String param2) {
Fragment2 fragment = new Fragment2();
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);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_2, container, false);
}
}
对应fragment_2.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Fragment2"
android:background="#ffff00">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="我是第二个fragment" />
</FrameLayout>
- 编写activity_main.xml布局文件
<?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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
android:id="@+id/fragment1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.mq.fragmentdemo.Fragment1"/>
<fragment
android:id="@+id/fragment2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:name="com.mq.fragmentdemo.Fragment2"/>
</LinearLayout>
运行效果:
fragment
中的android:name属性指定了Activity layout实例化的Fragment类,同时也可以使用 class 属性也可完成同样的功能。当系统创建Activity layout时,它实例化layout中的每一个fragment类并回调fragment中的onCreateView()方法,返回并获取fragment的根布局view,然后将根布局View插入到fragment
中。需要注意的是,每一个fragment
都需要一个唯一标识,当Activity重启时,可以用来恢复fragment对象状态。一般的目前Android已经提供了三种方式来为fragment提供唯一标识,如下:
1、为android:id提供唯一一个ID;
2、为android:tag提供唯一一个字符;
3、如果以上两种都没有提供,系统默认使用fragment的容器的ID;
2、动态添加Fragment
动态添加顾名思义就是在程序运行时根据不同的情况动态地添加我们所需的fragment
在Fragment的动态之前先介绍FragmentTransaction和FragmentManager的分析。
1、FragmentManager
FragmentManager是Fragment的管理器,主要用来对Activity中的Fragment进行管理,比如获取Fragmen事务、执行回退栈的出栈等。
常用方法
方法名 | 作用 |
---|---|
beginTransaction() | 获取FragmentTransaction对象 |
findFragmentById( int id) | 根据id获取对应的Fragment |
findFragmentByTag(String tag) | 根据tag获取对应的Fragment |
2、FragmentTransaction
FragmentTransaction是Fragment事务类, 主要用于对Activity中Fragment进行操作,比如add,remove,replace,hide,,show等。
事物最后要通过commit提交事务。
项目结构:
- 创建四个Fragment,Message_Fragment、Directories_Fragment、Find_Fragment、Me_Fragment和对应的布局
这里有展示一个Message_Fragment和布局,其余几个都是一样的。根据自己的需要改变布局就行。我这里就用一张图片来展示
public class Message_Fragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//第一个参数为布局文件
//第二个参数为ViewGroup root,表示父窗口,如果返回null,表示没有父窗口,则该Fragment无法显示
//第三个参数,attchToRoot,如果为true,则表示,当前Fragment的xml的根节点作为整个Activity的根节点,此时Acrtivity的其他控件都将不显示。
View view = inflater.inflate(R.layout.message_fragment, container, false);
return view;
}
message_fragment.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:textColor="#ff0033"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我的第一个fragment"
android:layout_gravity="center_horizontal"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/touxiang"/>
</FrameLayout>
- 编写 主活动的activity_main.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<FrameLayout
android:id="@+id/fl_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/ll_imv"
android:name="com.mq.fragmentdemo.Message_Fragment"/>
<LinearLayout
android:id="@+id/ll_imv"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<ImageView
android:id="@+id/image1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:padding="30dp"
android:src="@drawable/msg"/>
<ImageView
android:id="@+id/image2"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:padding="20dp"
android:src="@drawable/directories"/>
<ImageView
android:id="@+id/image3"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:padding="30dp"
android:src="@drawable/find"/>
<ImageView
android:id="@+id/image4"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:padding="20dp"
android:src="@drawable/me"/>
</LinearLayout>
</RelativeLayout>
编写MainActivity代码,代码里有注释
public class MainActivity extends AppCompatActivity {
private ImageView imageView1,imageView2,imageView3,imageView4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView1 = (ImageView) findViewById(R.id.image1);//获取布局文件的第一个导航图片
imageView2 = (ImageView) findViewById(R.id.image2);//获取布局文件的第二个导航图片
imageView3 = (ImageView) findViewById(R.id.image3);//获取布局文件的第三个导航图片
imageView4 = (ImageView) findViewById(R.id.image4);//获取布局文件的第四个导航图片
imageView1.setOnClickListener(mClickListener);//为第一个导航图片添加单机事件
imageView2.setOnClickListener(mClickListener);//为第二个导航图片添加单机事件
imageView3.setOnClickListener(mClickListener);//为第三个导航图片添加单机事件
imageView4.setOnClickListener(mClickListener);//为第四个导航图片添加单机事件
FragmentManager fm=getSupportFragmentManager();//获取Fragment的管理器
FragmentTransaction transaction = fm.beginTransaction();// 开启一个事务
transaction.add(R.id.fl_content,new Message_Fragment()).commit();//设置初始的fragment
}
//创建单机事件监听器
View.OnClickListener mClickListener =new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentManager fm=getSupportFragmentManager();//获取Fragment的管理器
FragmentTransaction transaction = fm.beginTransaction();// 开启一个事务
Fragment fragment=null;
switch (v.getId()) { //通过获取点击的id判断点击了哪个张图片
case R.id.image1:
fragment = new Message_Fragment(); //创建第一个Fragment
break;
case R.id.image2:
fragment = new Directories_Fragment();//创建第二个Fragment
break;
case R.id.image3:
fragment = new Find_Fragment();//创建第三个Fragment
break;
case R.id.image4:
fragment = new Me_Fragment();//创建第四个Fragment
break;
default:
break;
}
transaction.replace(R.id.fl_content, fragment); //替换Fragment
transaction.commit(); //提交事务
}
};
}
运行效果:点击下面的四个按钮会切换对应的fragment。
这里我每个fragment都只放了一张图片。
到这里简单的介绍了Fragment的生命周期和使用。具体复杂的使用有Fragment的嵌套,Fragment+ViewPager等等。
加油,码农!(ฅ´ω`ฅ)