0

0

Java应用中实现最近使用列表功能:食谱索引管理详解

DDD

DDD

发布时间:2025-10-05 12:59:21

|

429人浏览过

|

来源于php中文网

原创

Java应用中实现最近使用列表功能:食谱索引管理详解

本教程将详细介绍如何在Java应用程序中实现“最近使用”功能,以食谱应用为例,演示如何高效地存储、更新和展示用户最近浏览的食谱索引。文章将涵盖基于数组的元素移动策略,以及更灵活的LinkedList或Deque解决方案,并提供代码示例及持久化存储建议,帮助开发者构建用户友好的互动体验。

在许多应用程序中,“最近使用”功能是提升用户体验的关键组成部分。它允许用户快速访问他们最近查看或操作过的项目,例如食谱、文档或设置。对于一个食谱应用而言,实现一个“最近食谱”部分,显示用户最近浏览过的三道菜品,能够显著提高应用的便捷性。本文将探讨如何在java中有效地管理这些“最近使用”的食谱索引。

一、核心思路:维护一个固定大小的最近使用列表

要实现“最近使用”功能,我们需要一个数据结构来存储用户最近访问过的食谱的标识(在本例中是其在recipesAll列表中的索引)。由于我们只需要跟踪最近的三个食谱,因此这个列表的大小是固定的。当用户查看一个食谱时,我们需要将该食谱的索引添加到这个列表中,并确保列表始终只包含最新访问的三个食谱。

二、基于数组的元素平移策略

最直接的方法是使用一个固定大小的数组或ArrayList来存储最近使用的食谱索引。当一个新的食谱被访问时,我们将这个新索引插入到列表的“最前端”(即最近使用),并将其他现有元素向后平移,同时移除最旧的元素以保持列表大小不变。

假设我们有一个存储最近三个食谱索引的列表 recentRecipes。

1. 初始化最近使用列表

import java.util.ArrayList;
import java.util.List;

public class RecentRecipesManager {
    // 存储最近使用的食谱索引,最多3个
    private List recentRecipes = new ArrayList<>(3);
    private final int MAX_RECENT_SIZE = 3;

    // 假设这是你的食谱数据库类
    private ReceiptsBase receiptsBase;

    public RecentRecipesManager(ReceiptsBase receiptsBase) {
        this.receiptsBase = receiptsBase;
        // 可以在这里加载持久化的最近使用记录
    }

    /**
     * 当一个食谱被查看时调用此方法,更新最近使用列表。
     * @param recipeIndex 被查看食谱的索引
     */
    public void addRecipeToRecents(int recipeIndex) {
        // 1. 如果食谱已在列表中,先移除旧的,确保最新访问的在最前面
        recentRecipes.remove(Integer.valueOf(recipeIndex)); // remove by object

        // 2. 将新食谱添加到列表的最前端
        recentRecipes.add(0, recipeIndex); // add at index 0

        // 3. 如果列表大小超过最大限制,移除最旧的(即列表末尾的)
        if (recentRecipes.size() > MAX_RECENT_SIZE) {
            recentRecipes.remove(recentRecipes.size() - 1);
        }
    }

    /**
     * 获取最近使用的食谱索引列表。
     * @return 包含最近食谱索引的列表
     */
    public List getRecentRecipeIndexes() {
        return new ArrayList<>(recentRecipes); // 返回副本以防止外部修改
    }

    // ... 其他方法,如持久化保存等
}

2. 元素平移逻辑详解

上述 addRecipeToRecents 方法实现了以下逻辑:

立即学习Java免费学习笔记(深入)”;

  • 处理重复项: recentRecipes.remove(Integer.valueOf(recipeIndex)); 这一步非常关键。如果用户多次查看同一个食谱,我们不希望列表中出现重复项,而是希望该食谱的最新访问记录排在最前面。因此,如果该食谱索引已经存在于列表中,我们先将其移除。
  • 插入新项: recentRecipes.add(0, recipeIndex); 将新的食谱索引插入到列表的第一个位置(索引为0),这使其成为“最近使用”的食谱。
  • 维护大小: if (recentRecipes.size() > MAX_RECENT_SIZE) { recentRecipes.remove(recentRecipes.size() - 1); } 在插入新项后,如果列表大小超出了预设的最大值(例如3),则移除列表中的最后一个元素,即“最不最近使用”的食谱。

这种方法简单直观,对于固定且较小的列表大小非常有效。

三、优化方法:使用 LinkedList 或 ArrayDeque

虽然 ArrayList 的 add(0, element) 和 remove(index) 操作在内部涉及到元素的移动(时间复杂度为 O(n)),但对于只有少数元素的列表,性能影响可以忽略不计。然而,在Java中,LinkedList 或 ArrayDeque 是更适合实现队列或双端队列(Deque)行为的数据结构,它们在列表两端的添加和移除操作具有更好的性能(O(1))。

1. 使用 LinkedList 实现

LinkedList 实现了 Deque 接口,提供了 addFirst() 和 removeLast() 等方法,非常适合此场景。

import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

public class RecentRecipesManagerOptimized {
    private Deque recentRecipesDeque = new LinkedList<>();
    private final int MAX_RECENT_SIZE = 3;
    private ReceiptsBase receiptsBase;

    public RecentRecipesManagerOptimized(ReceiptsBase receiptsBase) {
        this.receiptsBase = receiptsBase;
    }

    /**
     * 当一个食谱被查看时调用此方法,更新最近使用列表。
     * @param recipeIndex 被查看食谱的索引
     */
    public void addRecipeToRecents(int recipeIndex) {
        // 1. 如果食谱已在列表中,先移除旧的
        recentRecipesDeque.remove(Integer.valueOf(recipeIndex)); // remove by object

        // 2. 将新食谱添加到列表的最前端
        recentRecipesDeque.addFirst(recipeIndex);

        // 3. 如果列表大小超过最大限制,移除最旧的(即列表末尾的)
        if (recentRecipesDeque.size() > MAX_RECENT_SIZE) {
            recentRecipesDeque.removeLast();
        }
    }

    /**
     * 获取最近使用的食谱索引列表。
     * @return 包含最近食谱索引的列表 (以 List 形式返回)
     */
    public List getRecentRecipeIndexes() {
        return new ArrayList<>(recentRecipesDeque); // 转换为 List 返回
    }
}

使用 LinkedList 的 addFirst() 和 removeLast() 方法,可以更高效地在列表两端进行操作。

四、集成到Android应用中

现在,我们将上述逻辑集成到Android应用的 MainActivity 中,以实现“最近食谱”功能。

STORYD
STORYD

帮你写出让领导满意的精美文稿

下载

1. 修改 MainActivity

首先,在 MainActivity 中实例化 RecentRecipesManager。当用户点击一个食谱时,调用 addRecipeToRecents 方法更新最近使用列表。

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import androidx.appcompat.app.AppCompatActivity;

import java.util.List;
import java.util.ArrayList; // 假设你的ReceiptsBase在同一个包或者已导入

public class MainActivity extends AppCompatActivity {

    private RecentRecipesManager recentRecipesManager; // 使用我们创建的管理器
    private ReceiptsBase receiptsBase; // 你的食谱数据库实例

    // UI元素
    private ImageButton recent1, recent2, recent3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        hideSystemUI(); // 假设这是一个自定义方法

        // 初始化食谱数据库和最近食谱管理器
        receiptsBase = new ReceiptsBase();
        recentRecipesManager = new RecentRecipesManager(receiptsBase);
        // 或者使用优化版:recentRecipesManager = new RecentRecipesManagerOptimized(receiptsBase);

        // 绑定按钮事件
        Button button_recipes = findViewById(R.id.button2);
        button_recipes.setOnClickListener(view -> openRecipes());

        Button button_search = findViewById(R.id.button3);
        button_search.setOnClickListener(view -> openSearch());

        Button button_supriseme = findViewById(R.id.button4);
        button_supriseme.setOnClickListener(view -> openSupriseMe());

        // 绑定最近食谱的ImageButton
        recent1 = findViewById(R.id.rec1);
        recent2 = findViewById(R.id.rec2);
        recent3 = findViewById(R.id.rec3);

        // 在Activity创建时或恢复时加载并显示最近食谱
        updateRecentRecipeUI();
    }

    @Override
    protected void onResume() {
        super.onResume();
        // 当Activity从后台回到前台时,也更新最近食谱UI,以防数据在其他地方被修改
        updateRecentRecipeUI();
    }

    // 更新最近食谱UI的方法
    private void updateRecentRecipeUI() {
        List recentIndexes = recentRecipesManager.getRecentRecipeIndexes();

        // 确保所有最近按钮都先被隐藏或重置,然后根据实际数据设置
        recent1.setVisibility(View.GONE);
        recent2.setVisibility(View.GONE);
        recent3.setVisibility(View.GONE);

        // 清除旧的点击监听器,避免重复绑定或错误引用
        recent1.setOnClickListener(null);
        recent2.setOnClickListener(null);
        recent3.setOnClickListener(null);

        // 根据 recentIndexes 的内容设置 ImageButton
        if (recentIndexes.size() > 0) {
            int index0 = recentIndexes.get(0);
            // 获取食谱的图片资源ID(假设在ReceiptsBase中,第一个元素是图片ID)
            int imageResId0 = receiptsBase.getReceipt(index0).get(0);
            recent1.setImageDrawable(getDrawable(imageResId0));
            recent1.setOnClickListener(view -> openRecipe(index0));
            recent1.setVisibility(View.VISIBLE);
        }
        if (recentIndexes.size() > 1) {
            int index1 = recentIndexes.get(1);
            int imageResId1 = receiptsBase.getReceipt(index1).get(0);
            recent2.setImageDrawable(getDrawable(imageResId1));
            recent2.setOnClickListener(view -> openRecipe(index1));
            recent2.setVisibility(View.VISIBLE);
        }
        if (recentIndexes.size() > 2) {
            int index2 = recentIndexes.get(2);
            int imageResId2 = receiptsBase.getReceipt(index2).get(0);
            recent3.setImageDrawable(getDrawable(imageResId2));
            recent3.setOnClickListener(view -> openRecipe(index2));
            recent3.setVisibility(View.VISIBLE);
        }
    }

    public void openRecipes(){
        Intent rec = new Intent(this, recipes.class);
        startActivity(rec);
        // finish(); // 根据你的导航逻辑决定是否finish
    }
    public void openSearch(){
        Intent sea = new Intent(this, search.class);
        startActivity(sea);
        // finish();
    }
    public void openSupriseMe(){
        Intent sup = new Intent(this, Example.class);
        startActivity(sup);
        // finish();
    }

    // 修改 openRecipe 方法,使其在打开食谱详情页前更新最近使用列表
    public void openRecipe(int recipeIndex){
        // 1. 更新最近使用列表
        recentRecipesManager.addRecipeToRecents(recipeIndex);
        // 2. 刷新UI (可选,如果用户从当前界面再次返回,onResume会处理)
        // updateRecentRecipeUI();

        // 3. 跳转到食谱详情页
        Intent sup = new Intent(this, Example.class); // 假设Example是食谱详情页
        sup.putExtra("recipeIndex", recipeIndex);
        startActivity(sup);
        // finish(); // 如果不希望返回到MainActivity,则finish
    }

    private void hideSystemUI() {
        // 实现隐藏系统UI的逻辑
    }
}

2. 注意事项:数据持久化

上述 RecentRecipesManager 实例在应用进程被杀死后会丢失其状态。为了让“最近食谱”功能在应用重启后依然有效,我们需要将 recentRecipes 列表的内容进行持久化存储。

在Android中,常用的持久化方式有:

  • SharedPreferences: 适合存储少量键值对数据,例如最近使用的食谱索引列表。可以将 List 转换为 Set 或 String(通过JSON序列化)进行存储。
  • 文件存储: 如果数据量较大或结构复杂,可以考虑将列表序列化为JSON或自定义格式存储到文件中。
  • 数据库(SQLite/Room): 对于更复杂的场景,如需要存储每个食谱的访问时间戳、访问次数等,使用数据库会更灵活。

使用 SharedPreferences 进行持久化示例:

在 RecentRecipesManager 中添加保存和加载方法:

import android.content.Context;
import android.content.SharedPreferences;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Deque;
import java.util.LinkedList; // 或者 ArrayDeque

public class RecentRecipesManager {
    private Deque recentRecipesDeque = new LinkedList<>(); // 使用Deque
    private final int MAX_RECENT_SIZE = 3;
    private ReceiptsBase receiptsBase;
    private Context context; // 需要Context来访问SharedPreferences
    private static final String PREFS_NAME = "RecentRecipesPrefs";
    private static final String KEY_RECENT_RECIPES = "recent_recipes_list";
    private Gson gson = new Gson(); // 用于JSON序列化/反序列化

    public RecentRecipesManager(Context context, ReceiptsBase receiptsBase) {
        this.context = context.getApplicationContext(); // 使用ApplicationContext防止内存泄漏
        this.receiptsBase = receiptsBase;
        loadRecentRecipes(); // 构造时加载
    }

    public void addRecipeToRecents(int recipeIndex) {
        recentRecipesDeque.remove(Integer.valueOf(recipeIndex));
        recentRecipesDeque.addFirst(recipeIndex);
        if (recentRecipesDeque.size() > MAX_RECENT_SIZE) {
            recentRecipesDeque.removeLast();
        }
        saveRecentRecipes(); // 每次更新后保存
    }

    public List getRecentRecipeIndexes() {
        return new ArrayList<>(recentRecipesDeque);
    }

    private void saveRecentRecipes() {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefs.edit();
        String json = gson.toJson(recentRecipesDeque); // 将Deque转换为JSON字符串
        editor.putString(KEY_RECENT_RECIPES, json);
        editor.apply(); // 异步保存
    }

    private void loadRecentRecipes() {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
        String json = prefs.getString(KEY_RECENT_RECIPES, null);
        if (json != null) {
            Type type = new TypeToken>() {}.getType(); // 指定类型
            recentRecipesDeque = gson.fromJson(json, type);
            // 确保加载后列表大小不超过MAX_RECENT_SIZE
            while (recentRecipesDeque.size() > MAX_RECENT_SIZE) {
                recentRecipesDeque.removeLast();
            }
        }
        if (recentRecipesDeque == null) { // 防止fromJson返回null
            recentRecipesDeque = new LinkedList<>();
        }
    }
}

在 MainActivity 中实例化时,需要传入 Context:

// 在 MainActivity 的 onCreate 方法中
// ...
receiptsBase = new ReceiptsBase();
recentRecipesManager = new RecentRecipesManager(this, receiptsBase); // 传入this (Context)
// ...

五、总结

实现“最近使用”功能的核心在于维护一个固定大小的数据结构,并在每次访问新项目时更新它。本文介绍了两种主要的实现策略:

  1. 基于 ArrayList 的元素平移: 简单直观,适用于列表元素较少的情况。
  2. 基于 LinkedList 或 ArrayDeque: 在列表两端操作时性能更优,是实现队列或双端队列行为的更佳选择。

无论选择哪种数据结构,处理重复项和维护列表大小是关键。此外,为了确保用户体验的连贯性,将“最近使用”数据进行持久化存储是必不可少的步骤。通过结合这些技术,开发者可以为用户提供一个响应迅速且功能完善的“最近使用”功能模块。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

825

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

724

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

728

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

395

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

445

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

428

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16861

2023.08.03

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

7

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.1万人学习

C# 教程
C# 教程

共94课时 | 5.7万人学习

Java 教程
Java 教程

共578课时 | 40万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号