Fragment即是Activity碎片,Fragment作为Activity界面的一部分组成出现,可以在一个Activity中同时出现多个Fragment,一个Fragment亦可在多个Activity中使用。
Android3.0引入Fragment的初衷是为了适应大屏幕的平台电脑,Framgment通过对UI组件进行分组,模块化管理,简化了大屏幕UI的设计,可以更方便地在运行过程中动态更新Activity的用户界面。
在Activity运行过程中,可以添加、移除或者替换Fragment(add()、remove()、replace())以达到Fragment动态更新的目的。Fragment可以响应自己的事件,并且有自己的生命周期,当然,它的生命周期直接被其所属的宿主activity的生命周期影响。只有当Activity处于活动状态时,程序员可通过方法独立的操作Fragment。
Fragment的生命周期如下,我们看到Fragment的生命周期与Activity基本一致。
onAttach():当Activity与Fragment发生关联时调用
onCreate():创建Fragment时被回调
onCreateView():创建或者绘制该Fragment的View组件时回调该方法
onActivityCreated():当Fragment所在的Activity的onCreate方法返回值回调该方法
onStart():启动Fragment时被回调
onResume():恢复Fragment时被调用,onStart方法后一定会回调该方法
onPause():暂停Fragment时被调用
onStop():停止Fragment时被调用
onDestoryView():当该Fragment被移除时回调,与oncreateView相对应。
onDestory():销毁Fragment时被回调
onDetach():与onAttach相对应,当该Fragment与Activity关联被取消时回调,被替换完成时回调该方法,onDestory方法后一定回调该方法。
关于Fragment的使用和实现有两种方式——静态导入和动态导入,这两种方式的区别在于静态导入只能以固定的位置将布局导入到同一个布局中一次性都显示出来,而动态导入可以将多个布局以一次只显示一个布局的方式导入到同一个布局中。
1.静态导入实例:
首先准备两个简单的布局:
fragment_first.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff0000">
<TextView
android:id="@+id/tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="FirstFragment"/>
</RelativeLayout>
fragment_second.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00ff00">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="SecondFragment"/>
</RelativeLayout>
然后为两个布局写两个类文件继承Fragment用来拿到布局:
FirstFragment.java
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
public class FirstFragment extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,//父容器
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_first, container,false);
TextView tv = (TextView) view.findViewById(R.id.tv);
tv.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TextView ttvv = (TextView)v;
String str = ttvv.getText().toString();
Toast.makeText(getActivity(), str, Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
SecondFragment.java
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class SecondFragment extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_sencond, container, false);
return view;
}
}
最后只要在布局文件中添加fragment标签,并设置一个name属性,值为它们对应的类文件的包名+类名
<LinearLayout 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"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.briup.fragment.MainActivity" >
<!-- name属性值为fragment的包名+类名 -->
<fragment
android:id="@+id/FirstFM"
android:name="com.briup.fragment.FirstFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
<fragment
android:id="@+id/SencondFM"
android:name="com.briup.fragment.SecondFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
Activity类文件如下
import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
}
效果如下
2.动态导入实例
两个布局和它们对应的类同上。动态导入涉及到不同fragment 的切换所以需要点击事件,我们这里以两个ImageView作为切换事件的触发源。同时需要一个FramLayout(帧布局)来作为占位布局。布局文件如下
<LinearLayout 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"
android:orientation="vertical" >
<!-- FramLayout用来给Fragment占位,将通过代码替换 -->
<FrameLayout
android:id="@+id/fl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1" >
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal" >
<ImageView
android:id="@+id/iv_tuijian"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:src="@drawable/tuijian1" />
<ImageView
android:id="@+id/iv_daohang"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:src="@drawable/daohang1" />
</LinearLayout>
</LinearLayout>
我们希望通过点击图片来切换界面,同时图片也要变化,关于图片的变化即是简单的重新设置图片即可,但是我们无法确定图片是否被点击,所以在点击之前应该将所有图片都设置为未点击的状态。而对于fragment的切换我们需要借助FragmentManager(碎片管理器),开启一个事务Fragment Transaction,它有以下方法可以帮我们达到目的:
add()往Activity中添加一个Fragment
remove()从Activity中移除一个Fragment
replace()使用另一个Fragment替换当前的,集成了add()和remove()方法
hide()隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
show()显示之前隐藏的Fragment
commit()提交事务(任何事务完成后都需要提交)
Activity类文件如下,同样的,我们也需要在点击前remove或hide所有fragment
import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.ImageView;
/**
* 动态添加fragment:
* 1.获取FragmentManager对象(碎片管理器)
* 2.通过FragmentManager开启事务
* 3.将Fragment添加到FramLayout
* 4.提交事务
*
* @author Android
*
*/
public class MainActivity extends Activity implements OnClickListener {
// 界面底边的图片
private ImageView tuijianIv, daohangIv;
private FirstFragment first;
private SecondFragment second;
private FragmentManager fragManager;
private FragmentTransaction fragTransaction;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initView();
setDefault();
}
// 初始化控件
private void initView() {
// FragmentManager声明
fragManager = getFragmentManager();
tuijianIv = (ImageView) findViewById(R.id.iv_tuijian);
daohangIv = (ImageView) findViewById(R.id.iv_daohang);
tuijianIv.setOnClickListener(this);
daohangIv.setOnClickListener(this);
}
// 默认界面
private void setDefault() {
startFragment();// 开启事务
first = new FirstFragment();
fragTransaction.add(R.id.fl, first);// 将Fragment添加到占位的FramLayout
fragTransaction.commit();// 提交事务
tuijianIv.setImageResource(R.drawable.tuijian2);// 设置图片为选中后的形式
}
@Override
public void onClick(View v) {
startFragment();// 开启事务
// 清除之前所选按钮图片
clearIV();
// 隐藏所有Fragment
hideFragment();
switch (v.getId()) {
case R.id.iv_tuijian:
if (first == null) {
first = new FirstFragment();
fragTransaction.add(R.id.fl, first);
}
fragTransaction.show(first);//显示Fragment
tuijianIv.setImageResource(R.drawable.tuijian2);
break;
default:
if (second == null) {
second = new SecondFragment();
fragTransaction.add(R.id.fl, second);
}
fragTransaction.show(second);
daohangIv.setImageResource(R.drawable.daohang2);
break;
}
fragTransaction.commit();// 提交事务
}
private void startFragment() {
// 开启事务,事务提交后必须重新开启
fragTransaction = fragManager.beginTransaction();
}
public void clearIV() {
tuijianIv.setImageResource(R.drawable.tuijian1);
daohangIv.setImageResource(R.drawable.daohang1);
}
public void hideFragment() {
if (first != null)
fragTransaction.hide(first);//隐藏Fragment
if (second != null)
fragTransaction.hide(second);
}
}
效果如下
这里使用了hide和show方法组合来完成fragment的切换,也可以使用add和remove组合或直接用replace完成切换。第一种方式的优点在于所有的fragment都只会被实例化一次,而且如果在当前fragment中做了操作,切回来不会被重置,有保存数据和节约资源的好处。而第二种方式每次切换都会重新实例化fragment,这样无法保存数据也是一种资源的浪费,所以非必须要刷新界面的情况建议使用hide和show组合。