Android studio开发实战之碎片Fragment
一、碎片化的概念
碎片化(Fragment)是 Android 应用开发中的一个重要概念,它的设计初衷是增强界面模块化,便于开发者灵活构建和管理复杂的界面。
- 什么是模块化?
- 将应用界面拆分成多个可复用的小模块(Fragment),每个模块可以独立定义自己的布局、逻辑和交互。
- 为什么需要模块化?
- 当界面变得复杂时,将整个界面写在一个 Activity 中会导致代码混乱、维护困难。
- 使用 Fragment,可以将界面逻辑独立开来,方便开发、调试和测试。
举例:在一个购物应用中:
- 商品列表是一个 Fragment。
- 商品详情是另一个 Fragment。
- 购物车页面又是一个 Fragment。
这样,每个模块的开发和调试是独立的,避免了代码耦合。
Fragment 同样可以解决 手机屏幕和平板屏幕差异。Fragment 作为一个独立的 UI 模块,可以被多个 Activity 复用。
- 在小屏设备上(如手机):
- 每个 Fragment 通常占据一个 Activity,全屏显示,用户需要通过页面跳转查看不同内容。
- 在大屏设备上(如平板):
- 可以在同一个 Activity 中显示多个 Fragment,比如左侧是导航列表,右侧是详情界面。
- 不同 Activity 都可以加载这个 Fragment,而不需要重复写界面和逻辑代码。
二、碎片的静态注册
每个Activity有自己的xml布局文件,作为Activity的一个模块,fragment也需要写自己的xml布局文件。
例如书中案例:
<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="match_parent"
android:layout_weight="4"
android:src="@drawable/adv"
android:scaleType="fitCenter" />
</LinearLayout>
一个简单的线性布局,水平方向上有两个组件,按照1:4的比例水平分布,效果如下:
上面已经说了,这里再强调一遍“将应用界面拆分成多个可复用的小模块(Fragment),每个模块可以独立定义自己的布局、逻辑和交互。”
因此上面的xml布局文件不必直接用于Activity,而是拥有自己的逻辑和交互代码。
例如:
public class StaticFragment extends Fragment implements View.OnClickListener {
private static final String TAG = "StaticFragment";
protected View mView; // 声明一个视图对象
protected Context mContext; // 声明一个上下文对象
// 创建碎片视图
@Override
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
:StaticFragment
是一个Fragment
类,用来定义用户界面的一部分,可以被嵌入到Activity
中。implements View.OnClickListener
:实现了View.OnClickListener
接口,用于处理视图的点击事件。
碎片视图代码详细分析:
-
mContext = getActivity();
getActivity()
获取当前Fragment
所附加的Activity
,即Fragment
的宿主。- 这里将
Activity
的上下文赋值给mContext
,方便后续在Fragment
中使用。 - 为什么这么写?Fragment 不能直接访问全局上下文(如
this
),而是依附于宿主 Activity。因此需要通过getActivity()
获取。 - 用途:上下文在后续创建视图、设置监听器、弹出 Toast 等操作中使用。
-
inflater.inflate(R.layout.fragment_static, container, false)
inflater
是布局填充器,用于将 XML 布局文件(fragment_static.xml
)转换为对应的视图对象。LayoutInflater
是 Android 用来解析 XML 布局文件并将其转化为实际的 View 对象的工具。container
是宿主Activity
中用于容纳当前Fragment
的容器。传入这个参数可以让视图正确地嵌入父容器中。false
表示是否立即将解析后的视图添加到父容器中。通常传false
,因为实际添加操作由系统完成。
-
视图查找与点击监听
mView.findViewById(...)
:从加载的视图中查找指定 ID 的控件。setOnClickListener(this)
:将StaticFragment
本身作为点击事件的监听器。
在 Fragment 中必须通过 视图对象 调用 findViewById
,而不能像 Activity 那样直接调用,这是由 Fragment 的设计原理和生命周期决定的。
Activity 的 findViewById
:
在 Activity 中,findViewById
是直接调用的,因为 Activity 会直接加载布局文件并管理所有控件。
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
setContentView
将布局文件加载到 Activity 的根视图中。findViewById
会在整个视图树(View hierarchy)中查找指定的控件。
Fragment 的 findViewById
:
Fragment 的视图是通过 onCreateView
动态生成的,并不直接属于 Activity 的视图树。
因此:
- 只能通过 Fragment 的根视图(
mView
)调用findViewById
,在其对应的视图范围内查找控件。
mView = inflater.inflate(R.layout.fragment_static, container, false);
TextView textView = mView.findViewById(R.id.text_view);
- Fragment 的视图是在
onCreateView
中动态加载的,而 Activity 的视图是随着setContentView
初始化的。 - 原因: 在 Fragment 中,控件只存在于
onCreateView
加载的布局中,只有通过mView
(Fragment 的根视图)才能访问这些控件。如果尝试直接调用findViewById
会导致空指针异常,因为 Fragment 的控件并没有加入到 Activity 的视图树中,Activity 无法找到这些控件。
好,现在碎片准备完毕,逻辑是点击碎片部分,会在活动页面上显示文本。下面要在活动页面上引用上面定义的碎片代码:步骤是先引用碎片控件,再在活动页面引入布局文件。
活动界面的xml布局文件如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 把碎片当作一个控件使用,其中android:name指明了碎片来源 -->
<fragment
android:id="@+id/fragment_static"
android:name="com.example.testapplication.fragment.StaticFragment"
android:layout_width="match_parent"
android:layout_height="60dp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="这里是每个页面的具体内容"
android:textColor="#000000"
android:textSize="17sp" />
</LinearLayout>
<fragment>
元素:
- 是一个特殊的 XML 标签,用于在布局文件中静态嵌入 Fragment。
- 系统会根据
android:name
指定的 Fragment 类,将其加载到<fragment>
标签的位置,并显示其视图。 android:name
指定需要加载的 Fragment 的类的完整路径(包名+类名)。
活动页面的代码如下:
public class FragmentStaticActivity extends AppCompatActivity {
private static final String TAG = "FragmentStaticActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_static);
Log.d(TAG, "onCreate");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
运行测试结果正常,那么碎片和活动页面的生命周期是怎样的呢?
单独看碎片的生命周期:
onAttach()
- Fragment 被添加到 Activity 上时调用。可以获取 Activity 的引用。
onCreate()
- Fragment 被创建时调用,适合初始化非视图相关的资源或逻辑。
onCreateView()
- 为 Fragment 创建视图时调用。必须返回根视图对象。
onActivityCreated()
- 宿主 Activity 的
onCreate()
方法完成后调用。
- 宿主 Activity 的
onStart()
- Fragment 对用户可见时调用。
onResume()
- Fragment 与用户交互时调用。
onPause()
- Fragment 进入后台时调用,适合保存非持久数据。
onStop()
- Fragment 不再对用户可见时调用。
onDestroyView()
- Fragment 的视图被销毁时调用。
onDestroy()
- Fragment 自身被销毁时调用。
onDetach()
- Fragment 从 Activity 中移除时调用。
碎片和活动页面的调用顺序如下,碎片的创建被优先调用,然后才是活动页面的视图创建。
三、碎片的动态注册
静态注册是通过在 XML 布局文件中直接声明 Fragment 来实现的。系统在加载布局时会自动实例化并显示 Fragment。
静态注册的特点
- 简单易用:在 XML 中直接定义 Fragment,系统会自动实例化它,减少了手动代码的书写。
- 生命周期由系统管理:Fragment 的生命周期由系统自动管理,依赖于宿主 Activity 的生命周期。
- 无法动态替换:一旦静态注册的 Fragment 加载进来,它的存在就固定了,无法像动态注册那样在运行时进行替换或移除。
动态注册是通过代码中手动创建并管理 Fragment 的实例,以及使用 FragmentTransaction
来进行 Fragment 的添加、替换、移除等操作。
动态注册的特点
- 更高的灵活性:可以在运行时动态决定哪些 Fragment 需要显示或隐藏,支持 Fragment 的动态添加、替换、移除等操作。
- 控制生命周期:通过
FragmentTransaction
手动控制 Fragment 的生命周期,例如添加、移除或替换 Fragment 时,可以通过事务来管理这些操作。 - 适应性强:适合那些需要动态变化、经常切换内容的页面。例如,Tab 页的切换、登录/注册界面的切换等。
动态注册最常用与翻页类视图并用,因此先回顾翻页类视图是怎样的:
3.1翻页类视图案例
1创建布局文件:
<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">
<!-- 注意翻页视图ViewPager的节点名称要填全路径 -->
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_content"
android:layout_width="match_parent"
android:layout_height="370dp" />
</LinearLayout>
ViewPager
是 Android 中用于实现 翻页效果(即滑动切换不同页面)的专用组件。
ViewPager
被用来显示多个页面,可以是图片、文本或其他视图。关于 ViewPager
的使用,有几个要点需要注意 :
ViewPager
必须通过适配器来管理它的页面内容。在你的布局文件中,ViewPager
并没有定义任何内容,它只是一个容器。你需要为它指定一个 PagerAdapter
,通常是 FragmentPagerAdapter
或 PagerAdapter
的子类。
ViewPager
提供了页面滑动的效果,用户可以左右滑动页面。如果你希望定制滑动效果(例如,设置切换动画、切换速度等),你可以通过修改 ViewPager
的配置或使用 PageTransformer
来实现。
例如,使用 PageTransformer
实现视差效果:
viewPager.setPageTransformer(true, new ViewPager.PageTransformer() {
@Override
public void transformPage(@NonNull View page, float position) {
page.setRotationY(position * -30); // 例如:左右翻转效果
}
});
2创建手机商品实体类:
public class GoodsInfo {
public long rowid; // 行号
public int xuhao; // 序号
public String name; // 名称
public String desc; // 描述
public float price; // 价格
public String pic_path; // 大图的保存路径
public int pic; // 大图的资源编号
public GoodsInfo() {
rowid = 0L;
xuhao = 0;
name = "";
desc = "";
price = 0;
pic_path = "";
pic = 0;
}
// 声明一个手机商品的名称数组
private static String[] mNameArray = {
"iPhone11", "Mate30", "小米10", "OPPO Reno3", "vivo X30", "荣耀30S"
};
// 声明一个手机商品的描述数组
private static String[] mDescArray = {
"Apple iPhone11 256GB 绿色 4G全网通手机",
"华为 HUAWEI Mate30 8GB+256GB 丹霞橙 5G全网通 全面屏手机",
"小米 MI10 8GB+128GB 钛银黑 5G手机 游戏拍照手机",
"OPPO Reno3 8GB+128GB 蓝色星夜 双模5G 拍照游戏智能手机",
"vivo X30 8GB+128GB 绯云 5G全网通 美颜拍照手机",
"荣耀30S 8GB+128GB 蝶羽红 5G芯片 自拍全面屏手机"
};
// 声明一个手机商品的价格数组
private static float[] mPriceArray = {6299, 4999, 3999, 2999, 2998, 2399};
// 声明一个手机商品的大图数组
private static int[] mPicArray = {
R.drawable.iphone, R.drawable.huawei, R.drawable.xiaomi,
R.drawable.oppo, R.drawable.vivo, R.drawable.rongyao
};
// 获取默认的手机信息列表
public static List<GoodsInfo> getDefaultList() {
List<GoodsInfo> goodsList = new ArrayList<GoodsInfo>();
for (int i = 0; i < mNameArray.length; i++) {
GoodsInfo info = new GoodsInfo();
info.name = mNameArray[i];
info.desc = mDescArray[i];
info.price = mPriceArray[i];
info.pic = mPicArray[i];
goodsList.add(info);
}
return goodsList;
}
}
在 Android 开发中,适配器(Adapter) 是一个用于连接数据源和视图组件的桥梁。它的主要作用是将数据映射到界面元素上,从而使数据能够在界面中展示。
适配器的作用:数据与视图的桥梁——适配器从数据源中获取数据,然后将这些数据绑定到视图组件上。
翻页类视图有自己的适配器,通过构造方法传入商品列表,再用instantiateItem实例化视图对象,并添加到容器。
3翻页类适配器代码如下:
public class ImagePagerAdapater extends PagerAdapter {
// 声明一个图像视图列表
private List<ImageView> mViewList = new ArrayList<ImageView>();
// 声明一个商品信息列表
private List<GoodsInfo> mGoodsList = new ArrayList<GoodsInfo>();
// 图像翻页适配器的构造方法,传入上下文与商品信息列表
public ImagePagerAdapater(Context context, List<GoodsInfo> goodsList) {
mGoodsList = goodsList;
// 给每个商品分配一个专用的图像视图
for (int i = 0; i < mGoodsList.size(); i++) {
ImageView view = new ImageView(context); // 创建一个图像视图对象
view.setLayoutParams(new LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
view.setImageResource(mGoodsList.get(i).pic);
mViewList.add(view); // 把该商品的图像视图添加到图像视图列表
}
}
// 获取页面项的个数
public int getCount() {
return mViewList.size();
}
// 判断当前视图是否来自指定对象
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
// 从容器中销毁指定位置的页面
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mViewList.get(position));
}
// 实例化指定位置的页面,并将其添加到容器中
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mViewList.get(position));
return mViewList.get(position);
}
// 获得指定页面的标题文本
public CharSequence getPageTitle(int position) {
return mGoodsList.get(position).name;
}
}
这段代码是一个自定义的 PagerAdapter
,用于在 Android 中实现 ViewPager 的图像滑动展示。PagerAdapter
是一种适配器,用来管理 ViewPager 中的页面数据。具体来说,这段代码将商品信息(GoodsInfo
)和对应的商品图片(ImageView
)绑定在一起,能够在 ViewPager
中进行滑动展示。
ImagePagerAdapater
继承了 PagerAdapter
,是一个自定义适配器,用于管理 ViewPager
中的页面。
mViewList
:用于存储ImageView
对象的列表,每个ImageView
对应一个商品的图片。mGoodsList
:用于存储商品信息的列表,每个商品包含商品名称和商品图片等信息。
- 构造方法接收一个
Context
和一个List<GoodsInfo>
类型的商品列表。 - 通过商品列表中的信息,为每个商品创建一个
ImageView
。每个商品对应一张图片,图片来源于GoodsInfo
对象的pic
属性。 - 设置每个
ImageView
的布局参数为MATCH_PARENT
和WRAP_CONTENT
,使得图片能够填充整个屏幕宽度并自适应高度。 - 将每个创建的
ImageView
添加到mViewList
列表中,供ViewPager
显示。
- 构造方法接收一个
Context
和一个List<GoodsInfo>
类型的商品列表。 - 通过商品列表中的信息,为每个商品创建一个
ImageView
。每个商品对应一张图片,图片来源于GoodsInfo
对象的pic
属性。 - 设置每个
ImageView
的布局参数为MATCH_PARENT
和WRAP_CONTENT
,使得图片能够填充整个屏幕宽度并自适应高度。 - 将每个创建的
ImageView
添加到mViewList
列表中,供ViewPager
显示。
public int getCount() {
return mViewList.size();
}
返回适配器中总共的页面数量,也就是 mViewList
列表中 ImageView
的数量,即商品的数量。
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
- 该方法用于判断一个视图是否与一个对象关联。
view
是当前显示的视图,object
是instantiateItem()
方法返回的对象。 - 这里判断
view
是否等于object
,即判断当前页面的视图是否是该适配器实例化的视图。
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(mViewList.get(position));
}
- 该方法用于销毁
ViewPager
中某个位置的页面视图。当用户滑动到下一个页面时,当前页面的视图会被销毁以节省内存。 - 通过
container.removeView()
从ViewGroup
中移除视图。
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mViewList.get(position));
return mViewList.get(position);
}
- 该方法用于实例化并返回
ViewPager
中某个位置的页面视图。通过container.addView()
把mViewList
中对应位置的ImageView
添加到ViewPager
容器中。 instantiateItem()
方法的返回值会作为destroyItem()
中的object
参数来使用。
public CharSequence getPageTitle(int position) {
return mGoodsList.get(position).name;
}
- 该方法用于返回每一页的标题。这里的标题来自商品信息列表
mGoodsList
中每个商品的name
属性。
4、活动代码
public class ViewPagerActivity extends AppCompatActivity implements OnPageChangeListener {
private List<GoodsInfo> mGoodsList; // 手机商品列表
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
mGoodsList = GoodsInfo.getDefaultList();
// 构建一个商品图片的翻页适配器
ImagePagerAdapater adapter = new ImagePagerAdapater(this, mGoodsList);
// 从布局视图中获取名叫vp_content的翻页视图
ViewPager vp_content = findViewById(R.id.vp_content);
vp_content.setAdapter(adapter); // 设置翻页视图的适配器
vp_content.setCurrentItem(0); // 设置翻页视图显示第一页
vp_content.addOnPageChangeListener(this); // 给翻页视图添加页面变更监听器
// // 给翻页视图添加简化版的页面变更监听器
// vp_content.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
// @Override
// public void onPageSelected(int position) {
// Toast.makeText(ViewPagerActivity.this, "您翻到的手机品牌是:"
// + mGoodsList.get(position).name, Toast.LENGTH_SHORT).show();
// }
// });
}
// 翻页状态改变时触发。state取值说明为:0表示静止,1表示正在滑动,2表示滑动完毕
// 在翻页过程中,状态值变化依次为:正在滑动→滑动完毕→静止
public void onPageScrollStateChanged(int state) {}
// 在翻页过程中触发。该方法的三个参数取值说明为 :第一个参数表示当前页面的序号
// 第二个参数表示页面偏移的百分比,取值为0到1;第三个参数表示页面的偏移距离
public void onPageScrolled(int position, float ratio, int offset) {}
// 在翻页结束后触发。position表示当前滑到了哪一个页面
public void onPageSelected(int position) {
Toast.makeText(this, "您翻到的手机品牌是:" + mGoodsList.get(position).name, Toast.LENGTH_SHORT).show();
}
}
效果,通过左右滑动,可以显示不同的手机图片:
3.2 碎片动态注册代码
1、布局文件
<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">
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_content"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.viewpager.widget.PagerTabStrip
android:id="@+id/pts_tab"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</androidx.viewpager.widget.ViewPager>
</LinearLayout>
这段代码定义了一个简单的 Android 布局,包含一个 ViewPager
组件和其内嵌的 PagerTabStrip
,用于实现页面翻页功能,同时为翻页视图添加顶部的标签条(Tab Strip)来标识当前页面。
androidx.viewpager.widget.ViewPager
:这是一个翻页视图容器,用于实现页面的滑动切换。上面已经讲过。
androidx.viewpager.widget.PagerTabStrip
:PagerTabStrip
是 ViewPager
的一个子组件,用于在顶部显示页面标题,并标识当前页面。PagerTabStrip
会自动显示与当前页面对应的标题信息。它需要配合 PagerAdapter
来提供标题。
2、碎片适配器
public class MobilePagerAdapter extends FragmentPagerAdapter {
private List<GoodsInfo> mGoodsList = new ArrayList<GoodsInfo>(); // 声明一个商品列表
// 碎片页适配器的构造方法,传入碎片管理器与商品信息列表
public MobilePagerAdapter(FragmentManager fm, List<GoodsInfo> goodsList) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
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;
}
}
MobilePagerAdapter
通过提供 Fragment
的内容和标题,将商品信息(GoodsInfo
)展示为滑动页面,用户可以通过滑动切换不同商品的详情页。
public MobilePagerAdapter(FragmentManager fm, List<GoodsInfo> goodsList) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
mGoodsList = goodsList;
}
- 参数解析:
FragmentManager fm
:由外部传入的碎片管理器,用于管理Fragment
的添加、移除和切换。List<GoodsInfo> goodsList
:商品信息列表,由外部传入适配器,用于为每个页面提供商品数据。
super
调用:BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
:表示只保留当前显示的Fragment
的视图状态,节省内存和性能。
- 功能:构造方法将商品信息列表传递给适配器。
public int getCount() {
return mGoodsList.size();
}
- 功能:返回商品信息列表的长度,即页面的总数。
- 适配器作用:
ViewPager
会调用此方法,确定总共需要显示多少个页面。
public Fragment getItem(int position) {
return DynamicFragment.newInstance(position,
mGoodsList.get(position).pic, mGoodsList.get(position).desc);
}
- 参数解析:
position
:页面索引,表示当前请求的页面在列表中的位置。
- 功能:
- 创建并返回一个新的
DynamicFragment
实例。 DynamicFragment.newInstance
是一个静态方法,用于创建包含特定数据的Fragment
。- 通过
position
获取对应商品的图片(pic
)和描述(desc
),并将其传递给DynamicFragment
。
- 创建并返回一个新的
- 适配器作用:
ViewPager
会调用此方法,为每个页面提供实际的Fragment
内容。
public CharSequence getPageTitle(int position) {
return mGoodsList.get(position).name;
}
- 参数解析:
position
:页面索引。
- 功能:
- 获取当前页面对应商品的名称(
name
)作为标题。 - 返回的标题会显示在配套的标签组件(如
PagerTabStrip
)中。
- 获取当前页面对应商品的名称(
运行时的具体流程
-
初始化适配器:
- 外部通过
new MobilePagerAdapter(fm, goodsList)
初始化适配器,将商品列表传入。
- 外部通过
-
绑定到
ViewPager
:ViewPager
设置此适配器后,会调用适配器的getCount
方法,确定总页面数。
-
加载页面内容:
- 当某个页面需要显示时,
ViewPager
会调用getItem
方法,从适配器获取对应的Fragment
。 DynamicFragment.newInstance
会根据商品的图片和描述生成一个对应的页面。
- 当某个页面需要显示时,
-
显示标题:
- 如果布局中有标签组件(如
PagerTabStrip
),会调用getPageTitle
方法获取标题,显示为对应页面的标签。
- 如果布局中有标签组件(如
3、碎片代码
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; // 返回该碎片的视图对象
}
@Override
public void onAttach(Activity activity) { // 把碎片贴到页面上
super.onAttach(activity);
Log.d(TAG, "onAttach position=" + mPosition);
}
@Override
public void onCreate(Bundle savedInstanceState) { // 页面创建
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate position=" + mPosition);
}
@Override
public void onDestroy() { // 页面销毁
super.onDestroy();
Log.d(TAG, "onDestroy position=" + mPosition);
}
@Override
public void onDestroyView() { // 销毁碎片视图
super.onDestroyView();
Log.d(TAG, "onDestroyView position=" + mPosition);
}
@Override
public void onDetach() { // 把碎片从页面撕下来
super.onDetach();
Log.d(TAG, "onDetach position=" + mPosition);
}
@Override
public void onPause() { // 页面暂停
super.onPause();
Log.d(TAG, "onPause position=" + mPosition);
}
@Override
public void onResume() { // 页面恢复
super.onResume();
Log.d(TAG, "onResume position=" + mPosition);
}
@Override
public void onStart() { // 页面启动
super.onStart();
Log.d(TAG, "onStart position=" + mPosition);
}
@Override
public void onStop() { // 页面停止
super.onStop();
Log.d(TAG, "onStop position=" + mPosition);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) { //在活动页面创建之后
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated position=" + mPosition);
}
}
4、活动页面
public class ViewPagerActivity extends AppCompatActivity implements OnPageChangeListener {
private List<GoodsInfo> mGoodsList; // 手机商品列表
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_pager);
mGoodsList = GoodsInfo.getDefaultList();
// 构建一个商品图片的翻页适配器
ImagePagerAdapater adapter = new ImagePagerAdapater(this, mGoodsList);
// 从布局视图中获取名叫vp_content的翻页视图
ViewPager vp_content = findViewById(R.id.vp_content);
vp_content.setAdapter(adapter); // 设置翻页视图的适配器
vp_content.setCurrentItem(0); // 设置翻页视图显示第一页
vp_content.addOnPageChangeListener(this); // 给翻页视图添加页面变更监听器
// // 给翻页视图添加简化版的页面变更监听器
// vp_content.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
// @Override
// public void onPageSelected(int position) {
// Toast.makeText(ViewPagerActivity.this, "您翻到的手机品牌是:"
// + mGoodsList.get(position).name, Toast.LENGTH_SHORT).show();
// }
// });
}
// 翻页状态改变时触发。state取值说明为:0表示静止,1表示正在滑动,2表示滑动完毕
// 在翻页过程中,状态值变化依次为:正在滑动→滑动完毕→静止
public void onPageScrollStateChanged(int state) {}
// 在翻页过程中触发。该方法的三个参数取值说明为 :第一个参数表示当前页面的序号
// 第二个参数表示页面偏移的百分比,取值为0到1;第三个参数表示页面的偏移距离
public void onPageScrolled(int position, float ratio, int offset) {}
// 在翻页结束后触发。position表示当前滑到了哪一个页面
public void onPageSelected(int position) {
Toast.makeText(this, "您翻到的手机品牌是:" + mGoodsList.get(position).name, Toast.LENGTH_SHORT).show();
}
}
效果: