Android studio学习之路(八)---Fragment碎片化页面的使用

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中几乎都离不开这个功能,所以非常建议大家去学习一下。我也会把代码放在资源库中供大家下载。

相关推荐
Yang-Never3 小时前
Kotlin协程 -> Job.join() 完整流程图与核心源码分析
android·开发语言·kotlin·android studio
知识分享小能手4 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
茯苓gao7 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾7 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT8 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa8 小时前
HTML和CSS学习
前端·css·学习·html
一笑的小酒馆8 小时前
Android性能优化之截屏时黑屏卡顿问题
android
看海天一色听风起雨落9 小时前
Python学习之装饰器
开发语言·python·学习
speop10 小时前
llm的一点学习笔记
笔记·学习
非凡ghost10 小时前
FxSound:提升音频体验,让音乐更动听
前端·学习·音视频·生活·软件需求