1、静态注册
- Fragment 只占据页面的一小块,但它有自己的生命周期,添加和去除都不会影响到宿主页面的其他区域;
- 每个 Fragment 都有相应的布局文件,依据其使用方法可分为静态注册与动态注册两类。静态注册是在布局文件中直接放置 fragment 节点,类似于一个普通控件,可被多个布局文件同时引用;
- 静态注册一般用于某个通用的页面部件(如 Logo、广告条等),每个活动页面均可直接引用该部件;
- 使用静态注册需要注意: fragment 节点必须指定 id 属性,否则 App 运行时会报错。
下面是 Fragment 布局文件的代码,跟列表项与网格项的布局差不多:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#bbffbb">
<TextView
android:id="@+id/tv_adv"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="广告"
android:textColor="#000000"
android:textSize="17sp" />
<ImageView
android:id="@+id/iv_adv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="5"
android:src="@drawable/adv"
android:scaleType="fitCenter" />
</LinearLayout>
下面是与上述布局文件对应的 Fragment 代码,除了继承自 Fragment 之外,其他地方类似活动页面代码:
public class StaticFragment extends Fragment implements OnClickListener {
private static final String TAG = "StaticFragment";
protected View mView; // 声明一个视图对象
protected Context mContext; // 声明一个上下文对象
// 创建碎片视图
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mContext = getActivity(); // 获取活动页面的上下文
// 根据布局文件fragment_static.xml生成视图对象
mView = inflater.inflate(R.layout.fragment_static, container, false);
TextView tv_adv = mView.findViewById(R.id.tv_adv);
ImageView iv_adv = mView.findViewById(R.id.iv_adv);
tv_adv.setOnClickListener(this);
iv_adv.setOnClickListener(this);
Log.d(TAG, "onCreateView");
return mView; // 返回该碎片的视图对象
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.tv_adv) {
Toast.makeText(mContext, "您点击了广告文本", Toast.LENGTH_LONG).show();
} else if (v.getId() == R.id.iv_adv) {
Toast.makeText(mContext, "您点击了广告图片", Toast.LENGTH_LONG).show();
}
}
}
若想在页面布局文件中引用 Fragment,则可直接加入一个 fragment 节点,注意 fragment 节点要增加 name 属性指定该 Fragment 类的完整路径:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<!-- 把碎片当作一个控件使用,其中android:name指明了碎片来源 -->
<fragment
android:id="@+id/fragment_static"
android:name="com.example.senior.fragment.StaticFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center|top"
android:text="这里是每个页面的具体内容"
android:textColor="#000000"
android:textSize="17sp" />
</LinearLayout>
Fragment 在静态注册时的生命周期,除了拥有 Activity 的基本生命周期方法 onCreate、onStart、onResume、onPause、onStop、onDestroy 之外,Fragment 还拥有以下 5 个生命周期方法:
- onAttach:与 Activity 结合。可在该方法中实例化 Activity 的一个回调对象,在 Fragment 中调用 Activity 的回调方法。这样设计的好处是 Activity 无须调用 set***Listener 方法设置监听器接口。
- onCreateView:创建碎片视图。
- onActivityCreated:在活动页面创建完毕后调用。
- onDestroyView:回收碎片视图。
- onDetach:与 Activity 分离。
打开页面:
退出页面:
总结就是,在静态注册时,除了碎片的创建操作在页面创建之前,其他操作都在页面创建之后。
2、动态注册/碎片适配器 FragmentStatePagerAdapter
- 动态注册直到在代码中才动态添加 Fragment,动态碎片就是给翻页视图使用的;
- 在 ViewPager 中使用 Fragment,需要使用碎片适配器 FragmentStatePagerAdapter。
下面是使用 FragmentStatePagerAdapter 适配器的代码,获取页面视图的地方变成了 getItem 方法:
public class MobilePagerAdapter extends FragmentStatePagerAdapter {
private ArrayList<GoodsInfo> mGoodsList = new ArrayList<GoodsInfo>(); // 声明一个商品队列
// 碎片页适配器的构造函数,传入碎片管理器与商品信息队列
public MobilePagerAdapter(FragmentManager fm, ArrayList<GoodsInfo> goodsList) {
super(fm);
mGoodsList = goodsList;
}
// 获取碎片Fragment的个数
public int getCount() {
return mGoodsList.size();
}
// 获取指定位置的碎片Fragment
public Fragment getItem(int position) {
return DynamicFragment.newInstance(position,
mGoodsList.get(position).pic, mGoodsList.get(position).desc);
}
// 获得指定碎片页的标题文本
public CharSequence getPageTitle(int position) {
return mGoodsList.get(position).name;
}
}
以上适配器在获得碎片对象时不用构造函数,却用了 newInstance 方法,目的是给 Fragment 传递参数信息。通过构造函数获得碎片对象后还得调用 setArguments 方法才能把请求数据塞进去,然后在 Fragment 的 onCreateView 函数中调用 getArguments 方法获得请求数据。
下面是动态注册的碎片代码:
public class DynamicFragment extends Fragment {
private static final String TAG = "DynamicFragment";
protected View mView; // 声明一个视图对象
protected Context mContext; // 声明一个上下文对象
private int mPosition; // 位置序号
private int mImageId; // 图片的资源编号
private String mDesc; // 商品的文字描述
// 获取该碎片的一个实例
public static DynamicFragment newInstance(int position, int image_id, String desc) {
DynamicFragment fragment = new DynamicFragment(); // 创建该碎片的一个实例
Bundle bundle = new Bundle(); // 创建一个新包裹
bundle.putInt("position", position); // 往包裹存入位置序号
bundle.putInt("image_id", image_id); // 往包裹存入图片的资源编号
bundle.putString("desc", desc); // 往包裹存入商品的文字描述
fragment.setArguments(bundle); // 把包裹塞给碎片
return fragment; // 返回碎片实例
}
// 创建碎片视图
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mContext = getActivity(); // 获取活动页面的上下文
if (getArguments() != null) { // 如果碎片携带有包裹,则打开包裹获取参数信息
mPosition = getArguments().getInt("position", 0);
mImageId = getArguments().getInt("image_id", 0);
mDesc = getArguments().getString("desc");
}
// 根据布局文件fragment_dynamic.xml生成视图对象
mView = inflater.inflate(R.layout.fragment_dynamic, container, false);
ImageView iv_pic = mView.findViewById(R.id.iv_pic);
TextView tv_desc = mView.findViewById(R.id.tv_desc);
iv_pic.setImageResource(mImageId);
tv_desc.setText(mDesc);
Log.d(TAG, "onCreateView position=" + mPosition);
return mView; // 返回该碎片的视图对象
}
}
打开页面:
退出页面:
- 动态注册时,Fragment 的 onCreate 操作在 Activity 之后,其余操作的先后顺序与静态注册时保持一致;
- 注意 onActivityCreated 方法。无论是静态注册还是动态注册,该方法都在 Activity 的 onCreate 操作之后。可见该方法在页面创建后才调用;
- 进入第一个 Fragment ,实际只加载了第一页和第二页,并没有加载全部 Fragment。这正是 Fragment 的优越之处,无论当前位于哪一页,系统都只会加载当前页及相邻的前后两页,总共加载不超过三页。