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

相关推荐
豆沙沙包?4 分钟前
5.学习笔记-SpringMVC(P61-P70)
数据库·笔记·学习
_一条咸鱼_2 小时前
深度揭秘!Android HorizontalScrollView 使用原理全解析
android·面试·android jetpack
_一条咸鱼_2 小时前
揭秘 Android RippleDrawable:深入解析使用原理
android·面试·android jetpack
_一条咸鱼_2 小时前
深入剖析:Android Snackbar 使用原理的源码级探秘
android·面试·android jetpack
_一条咸鱼_2 小时前
揭秘 Android FloatingActionButton:从入门到源码深度剖析
android·面试·android jetpack
_一条咸鱼_2 小时前
深度剖析 Android SmartRefreshLayout:原理、源码与实战
android·面试·android jetpack
_一条咸鱼_2 小时前
揭秘 Android GestureDetector:深入剖析使用原理
android·面试·android jetpack
_一条咸鱼_2 小时前
深入探秘 Android DrawerLayout:源码级使用原理剖析
android·面试·android jetpack
_一条咸鱼_2 小时前
深度揭秘:Android CardView 使用原理的源码级剖析
android·面试·android jetpack
_一条咸鱼_2 小时前
惊爆!Android RecyclerView 性能优化全解析
android·面试·android jetpack