fragment的用法很常见,你可能经常看见这样的画面:
通过滑动来进行切换页面,今天我们就来实现这样的形式
介绍
使用 Fragment 的核心价值在于 模块化设计 和 动态适配能力,尤其适合以下场景:
需要复用界面组件。
适配不同屏幕尺寸(手机/平板)。
管理复杂界面生命周期。
结合 Jetpack 组件构建现代化架构。
掌握 Fragment 的使用,可以显著提升代码可维护性和用户体验。
模块化与代码复用
1.场景:
当需要将界面拆分为多个独立功能块(如新闻列表 + 详情页)时,每个功能块可以用 Fragment 实现。
23优势:
复用性:同一 Fragment 可在多个 Activity 中重复使用(如登录模块)。
解耦性:将界面逻辑分离到 Fragment,Activity 仅负责整体协调。
动态界面适配
3.场景:
在手机和平板上,同一功能可能需要不同布局(如手机单窗 vs 平板多窗)。
4.优势:
响应式设计:通过配置不同布局文件(如 res/layout-sw600dp),自动切换 Fragment 组合。
运行时调整:根据屏幕方向或窗口大小动态增减 Fragment。
何时不使用 Fragment?
简单界面:单一静态界面直接使用 Activity。
性能敏感场景:频繁动态切换 Fragment 可能影响性能,需谨慎设计。
OK,我们接下来实现代码
初级的代码形式(解释都放在注释里了)
我们今天的代码都是通过动态形式进行实现的,相对与静态,动态的随用随加载的形式更有利于减少内存,提高运行效率:
首先我们需要给页面中的信息的属性进行一个初始化(这里我们可以去找几张手机的图片和文本信息,这次我们做一个类似于淘宝中产品介绍的页面)
GoodsInfo.java
java
package com.example.tttplean;
import com.example.tttplean.R;
import java.util.ArrayList;
import java.util.List;
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;
}
}
接下来,我们对手机的页面的图片显示做一个适配:
MobilePagerAdapter.java:
java
package com.example.tttplean;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.example.tttplean.GoodsInfo;
import com.example.tttplean.DynamicFragment;
import java.util.ArrayList;
import java.util.List;
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;
}
}
接下来,我们来对fragment的属性与性质进行初始化与设定
DynamicFragment.java(并非activity)
java
package com.example.tttplean;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import com.example.tttplean.R;
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);
}
}
页面的设计
fragment_dynamic.xml:
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_pic"
android:layout_width="match_parent"
android:layout_height="360dp"
android:scaleType="fitCenter" />
<TextView
android:id="@+id/tv_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="left"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
接下来,我们创立activity来进行实现操作:
FragmentDynamicActivity.java:
java
package com.example.tttplean;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.PagerTabStrip;
import androidx.viewpager.widget.ViewPager;
import com.example.tttplean.MobilePagerAdapter;
import com.example.tttplean.GoodsInfo;
import java.util.List;
public class FragmentDynamicActivity extends AppCompatActivity {
private static final String TAG = "FragmentDynamicActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_dynamic);
Log.d(TAG, "onCreate");
initPagerStrip(); // 初始化翻页标签栏
initViewPager(); // 初始化翻页视图
}
// 初始化翻页标签栏
private void initPagerStrip() {
// 从布局视图中获取名叫pts_tab的翻页标签栏
PagerTabStrip pts_tab = findViewById(R.id.pts_tab);
// 设置翻页标签栏的文本大小
pts_tab.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
// 设置翻页标签栏的文本颜色
pts_tab.setTextColor(Color.BLACK);
}
// 初始化翻页视图
private void initViewPager() {
List<GoodsInfo> goodsList = GoodsInfo.getDefaultList();
// 构建一个手机商品的碎片翻页适配器
MobilePagerAdapter adapter = new MobilePagerAdapter(
getSupportFragmentManager(), goodsList);
// 从布局视图中获取名叫vp_content的翻页视图
ViewPager vp_content = findViewById(R.id.vp_content);
vp_content.setAdapter(adapter); // 设置翻页视图的适配器
vp_content.setCurrentItem(0); // 设置翻页视图显示第一页
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
}
activity_fragment_dynamic.xml
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: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>
效果图:
通过滑动进行页面的改变。
改善后的代码
改善后的代码,可以启动更快,代码的减少代码的耦合
LaunchImproveAdapter.java
java
package com.example.tttplean;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import com.example.tttplean.LaunchFragment;
public class LaunchImproveAdapter extends FragmentPagerAdapter {
private int[] mImageArray; // 声明一个图片数组
// 碎片页适配器的构造方法,传入碎片管理器与图片数组
public LaunchImproveAdapter(FragmentManager fm, int[] imageArray) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
mImageArray = imageArray;
}
// 获取碎片Fragment的个数
public int getCount() {
return mImageArray.length;
}
// 获取指定位置的碎片Fragment
public Fragment getItem(int position) {
return LaunchFragment.newInstance(position, mImageArray[position]);
}
}
LaunchFragment.java
java
package com.example.tttplean;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import com.example.tttplean.R;
public class LaunchFragment extends Fragment {
protected View mView; // 声明一个视图对象
protected Context mContext; // 声明一个上下文对象
private int mPosition; // 位置序号
private int mImageId; // 图片的资源编号
private int mCount = 4; // 引导页的数量
// 获取该碎片的一个实例
public static LaunchFragment newInstance(int position, int image_id) {
LaunchFragment fragment = new LaunchFragment(); // 创建该碎片的一个实例
Bundle bundle = new Bundle(); // 创建一个新包裹
bundle.putInt("position", position); // 往包裹存入位置序号
bundle.putInt("image_id", image_id); // 往包裹存入图片的资源编号
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); // 从包裹获取图片的资源编号
}
// 根据布局文件item_launch.xml生成视图对象
mView = inflater.inflate(R.layout.item_launch, container, false);
ImageView iv_launch = mView.findViewById(R.id.iv_launch);
RadioGroup rg_indicate = mView.findViewById(R.id.rg_indicate);
Button btn_start = mView.findViewById(R.id.btn_start);
iv_launch.setImageResource(mImageId); // 设置引导页的全屏图片
// 每个页面都分配一个对应的单选按钮
for (int j = 0; j < mCount; j++) {
RadioButton radio = new RadioButton(mContext); // 创建一个单选按钮
radio.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
radio.setButtonDrawable(R.drawable.launch_guide); // 设置单选按钮的图标
radio.setPadding(10, 10, 10, 10); // 设置单选按钮的四周间距
rg_indicate.addView(radio); // 把单选按钮添加到页面底部的单选组
}
// 当前位置的单选按钮要高亮显示,比如第二个引导页就高亮第二个单选按钮
((RadioButton) rg_indicate.getChildAt(mPosition)).setChecked(true);
// 如果是最后一个引导页,则显示入口按钮,以便用户点击按钮进入首页
if (mPosition == mCount - 1) {
btn_start.setVisibility(View.VISIBLE);
btn_start.setOnClickListener(v -> {
// 这里要跳到应用主页
Toast.makeText(mContext, "欢迎您开启美好生活", Toast.LENGTH_SHORT).show();
});
}
return mView; // 返回该碎片的视图对象
}
}
xml:
xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 这是引导图片的图像视图 -->
<ImageView
android:id="@+id/iv_launch"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY" />
<!-- 这里容纳引导页底部的一排圆点 -->
<RadioGroup
android:id="@+id/rg_indicate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:orientation="horizontal"
android:paddingBottom="20dp" />
<!-- 这是最后一页的入口按钮 -->
<Button
android:id="@+id/btn_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="立即开始美好生活"
android:textColor="#ff3300"
android:textSize="22sp"
android:visibility="gone" />"
</RelativeLayout>
LaunchImproveActivity.java
java
package com.example.tttplean;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;
import com.example.tttplean.LaunchImproveAdapter;
public class LaunchImproveActivity extends AppCompatActivity {
// 声明引导页面的图片数组
private int[] lanuchImageArray = {R.drawable.guide_bg1,
R.drawable.guide_bg2, R.drawable.guide_bg3, R.drawable.guide_bg4};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch_improve);
// 从布局视图中获取名叫vp_launch的翻页视图
ViewPager vp_launch = findViewById(R.id.vp_launch);
// 构建一个引导页面的碎片翻页适配器
LaunchImproveAdapter adapter = new LaunchImproveAdapter(getSupportFragmentManager(), lanuchImageArray);
vp_launch.setAdapter(adapter); // 设置翻页视图的适配器
vp_launch.setCurrentItem(0); // 设置翻页视图显示第一页
}
}
xml:
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/vp_launch"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
效果图:
这里给予每个页面都有了表示和按钮,而且在最后一个页面中才会出现跳转页面的按钮:
尾言:
本次的更新还是非常有用的,在我见到的项目APP中几乎都离不开这个功能,所以非常建议大家去学习一下。我也会把代码放在资源库中供大家下载。