Android开发:通过字符串名称动态加载Drawable资源以避免运行时错误


Android开发:通过字符串名称动态加载Drawable资源以避免运行时错误

在android应用开发中,直接将`r.drawable`生成的整数id存储到外部数据(如json)是不可靠的,因为这些id在不同编译版本或运行时可能发生变化,导致`resources$notfoundexception`。本教程将详细阐述这一问题的原因,并提供一种健壮的解决方案:通过在外部数据中存储drawable资源的字符串名称,然后在运行时使用`context.getresources().getidentifier()`方法动态获取正确的资源id,确保应用稳定加载图像资源。

理解Android资源ID的不稳定性

Android资源系统为应用中的每个资源(如布局、字符串、图片等)分配一个唯一的整数ID,这些ID定义在自动生成的R类中。例如,R.drawable.tomato会对应一个特定的整数值。然而,一个常见的误解是这些整数ID是固定不变的。实际上,这些资源ID是在每次编译时动态生成的,它们并非在不同编译版本之间稳定不变。

当我们将这些整数ID序列化到外部存储(如JSON文件)中时,问题就出现了。如果应用重新编译,或者在不同的设备/Android版本上运行,之前存储的ID可能不再对应正确的资源,甚至可能对应一个不存在的资源。这会导致运行时抛出android.content.res.Resources$NotFoundException异常,提示“Invalid ID”或“Resource ID #0x...”,从而导致应用崩溃。

解决方案:通过字符串名称动态加载Drawable

为了解决资源ID不稳定的问题,最佳实践是在外部数据中存储Drawable资源的字符串名称,而不是其整数ID。例如,对于R.drawable.tomato,我们应该存储字符串"tomato"。然后在应用运行时,利用Android Resources类提供的getIdentifier()方法,根据字符串名称动态查找并获取当前编译环境下的正确资源ID。

1. 更新数据模型

首先,修改你的数据类(例如Ingredient.j*a),将存储Drawable ID的字段类型从int更改为String。

Ingredient.j*a 更新示例:

package me.eyrim.foodrecords2;

import com.google.gson.annotations.SerializedName;

public class Ingredient {
    @SerializedName("ingredient_name")
    private String ingredientName;
    // 将 int 更改为 String
    @SerializedName("ingredient_drawable_tag")
    private String ingredientDrawableTag; 

    public String getIngredientName() {
        return this.ingredientName;
    }

    // 返回 String 类型
    public String getIngredientDrawableTag() {
        return this.ingredientDrawableTag;
    }
}

2. 更新JSON数据结构

相应地,修改你的JSON数据,将ingredient_drawable_tag的值从整数改为对应的Drawable文件名(不包含文件扩展名)。

Claude Claude

Anthropic发布的与ChatGPT竞争的聊天机器人

Claude 1166 查看详情 Claude

JSON数据更新示例:

{
    "recipe_name": "My test recipe 1 updated",
    "recipe_id": "0",
    "recipe_desc": "this is a test desc for my test recipe 1",
    "ingredients": [{
            "ingredient_name": "Tomato",
            // 从整数 ID 更改为字符串名称
            "ingredient_drawable_tag": "tomato" 
        },
        {
            "ingredient_name": "Pepper",
            // 从整数 ID 更改为字符串名称
            "ingredient_drawable_tag": "pepper" 
        }
    ]
}

3. 在RecyclerView Adapter中动态加载Drawable

在你的RecyclerView.Adapter中,当绑定数据到视图时(onBindViewHolder方法),使用Context.getResources().getIdentifier()方法来获取Drawable的实际资源ID。

IngredientRecyclerViewAdapter.j*a 更新示例:

package me.eyrim.foodrecords2.recipeviewactivity;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import me.eyrim.foodrecords2.Ingredient;
import me.eyrim.foodrecords2.R; // 确保 R 类被导入

public class IngredientRecyclerViewAdapter extends RecyclerView.Adapter<IngredientRecyclerViewAdapter.IngredientViewHolder> {
    private final Ingredient[] ingredients;
    private final Context context; // 存储 Context 实例

    // 构造函数需要接收 Context
    public IngredientRecyclerViewAdapter(Ingredient[] ingredients, Context context) {
        this.ingredients = ingredients;
        this.context = context; // 初始化 Context
    }

    @NonNull
    @Override
    public IngredientViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.ingredient_row, parent, false);
        return new IngredientViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull IngredientViewHolder holder, int position) {
        String drawableName = this.ingredients[position].getIngredientDrawableTag();

        // 使用 getIdentifier() 动态获取资源 ID
        int drawableId = context.getResources().getIdentifier(
            drawableName,      // 资源名称 (字符串)
            "drawable",        // 资源类型 (例如 "drawable", "string", "layout")
            context.getPackageName() // 应用包名
        );

        // 检查是否成功找到资源ID,避免找不到资源时崩溃
        if (drawableId != 0) {
            holder.imageView.setImageResource(drawableId);
        } else {
            // 如果找不到对应的 drawable,可以设置一个默认的占位符图片
            holder.imageView.setImageResource(R.drawable.default_ingredient_placeholder); // 假设你有一个默认图片
            // 或者记录日志,以便调试
            // Log.w("IngredientAdapter", "Drawable not found for name: " + drawableName);
        }
    }

    @Override
    public int getItemCount() {
        return this.ingredients.length;
    }

    public static class IngredientViewHolder extends RecyclerView.ViewHolder { // 静态内部类
        private final ImageView imageView;
        // 如果有 TextView,也在这里初始化
        // private final TextView textView;

        public IngredientViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.ingredient_image);
            // textView = itemView.findViewById(R.id.ingredient_name_text); // 如果有的话
        }
    }
}

关键点说明:

  • Context 实例: getIdentifier() 方法需要一个Context实例来访问应用的资源。在RecyclerView.Adapter中,通常通过构造函数传入Context并作为成员变量保存。
  • getIdentifier(name, defType, defPackage) 参数:
    • name: 要查找的资源名称(例如,"tomato")。
    • defType: 资源的类型,对于图片通常是"drawable"。其他常见类型包括"string"、"layout"、"id"等。
    • defPackage: 资源的包名,通常是context.getPackageName(),即当前应用的包名。
  • 错误处理: 如果getIdentifier()找不到对应的资源,它会返回0。务必在设置图片前检查这个返回值,以避免Resources$NotFoundException,并可以设置一个默认的占位符图片来提升用户体验。

注意事项与最佳实践

  1. 命名规范: 确保JSON中的字符串名称与res/drawable文件夹下的实际Drawable文件名(不含扩展名)完全匹配,包括大小写。
  2. 性能考虑: getIdentifier()方法在运行时通过字符串查找资源,相比直接使用整数ID会有轻微的性能开销。但在大多数RecyclerView场景下,这种开销通常可以忽略不计。如果需要极致性能且资源数量非常庞大,可以考虑在应用启动时将所有字符串名称映射到整数ID的HashMap中,以空间换时间。
  3. 默认图片: 强烈建议在getIdentifier()返回0时,设置一个默认的占位符图片。这可以防止在资源名称拼写错误或资源缺失时,应用界面出现空白或崩溃。
  4. 资源管理: 保持Drawable资源文件的命名清晰和一致性,这有助于代码的可读性和维护性。

总结

通过将Drawable资源的整数ID替换为其字符串名称,并在运行时使用Context.getResources().getIdentifier()动态获取ID,我们可以有效地解决Android资源ID不稳定导致的问题。这种方法使得应用能够更健壮地处理外部配置的资源,提高了应用的可维护性和用户体验,是Android开发中处理动态资源加载的推荐实践。

以上就是Android开发:通过字符串名称动态加载Drawable资源以避免运行时错误的详细内容,更多请关注其它相关文章!


# 配置文件  # 厦门抖音关键词搜索排名  # 杭州网站推广有哪些  # 贵州网站排名推广  # 琳琳seo  # 跳转网站能做优化吗  # 聚焦农村网站推广  # 宁波seo品牌优化  # 营销推广平台覆盖易速达  # 推广财经网站  # 盐城seo公司选1火星  # 也在  # 会有  # 这一  # java  # 不稳定  # 以避免  # 是在  # 数据结构  # 找不到  # 加载  # red  # 应用开发  # google  # go  # json  # js  # android 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 苹果手机怎么合并照片_苹果手机合并多张照片的操作方法  行者app怎样导出日志  使用Python和GBGB API高效抓取指定日期范围和赛道比赛结果教程  火柴人战争网页版在线玩  响应式设计中动态背景颜色条的实现指南  海棠阅读登录教程_详细讲解海棠登录操作  《星露谷物语》克林特好感度事件介绍  HTML Canvas文本样式定制指南:解决外部字体加载与应用难题  Sublime怎么自动添加CSS前缀_Sublime安装Autoprefixer插件  Go Template中优雅处理循环最后一项:自定义函数实践  《海豚家》注销账号方法  实时数据流中高效查找最小值与最大值  C++如何使用CMake构建项目_C++ CMakeLists.txt编写入门教程  windows server2019显卡驱动怎么安装_winserver2019显卡驱动安装与远程桌面优化  Mac如何开启画中画模式_Mac Safari浏览器视频画中画功能  《顺丰同城骑士》查看我的技能方法  优化响应式标题底部边框:CSS实现技巧与最佳实践  AffinityDesigner图层蒙版怎么用_AffinityDesigner图层蒙版设计应用  Pydantic 中“schema”字段命名冲突的解决方案  《画加》约稿流程  Google Drive API服务器端访问指南:服务账户认证详解  厨房地面防滑垫的油污怎么洗? 机洗和手洗防滑垫的注意事项  管理打开的编辑器:固定、分组和关闭技巧  顺丰官方查单号入口 顺丰快递单号查询官网入口  微星主板BIOS怎么调整内存时序_内存参数手动优化BIOS设置教程  铁拳8在线玩 铁拳8在线秒玩入口  《原神》月之一版本新增书籍一览  蜻蜓FM如何设置移动流量播放  深入理解随机递归函数的确定性:内部节点、叶节点与时间复杂度分析  mysql怎么查询数据_mysql基础查询语句使用教程  处理含命名空间的XML文件 Power Query中的高级技巧  铁路12306怎么申请退票_铁路12306退票申请操作流程  b站如何管理订阅_b站订阅标签分类管理  word表格如何按某一列内容进行排序_Word表格按列排序方法  J*a中逻辑运算符如何使用_逻辑与或非的基础用法讲解  Win10关闭UAC用户账户控制的方法 Win10降低安全提示等级【技巧】  电脑开不了机怎么办 电脑无法开机的解决方法  搜狗浏览器如何查找页面中的文字 搜狗浏览器Ctrl+F页面搜索功能  铁路12306入口 铁路12306官网版入口登录网址  歌词怎么展示在|直播|间视频号?有什么注意事项?  汽水音乐在线入口 汽水音乐网页端官方页面快速打开  iPhone 13 mini如何清理Safari缓存_iPhone 13 mini浏览器缓存清理方法  江苏大剧院会员卡购买步骤  c++如何实现观察者设计模式_c++行为型设计模式实战  优化 React onClick 事件处理:函数引用与箭头函数的对比  win11怎么更改账户类型 Win11标准用户和管理员权限切换【教程】  《绝区零》2.3前瞻|直播|内容介绍  纯CSS实现自适应宽度与响应式布局的水平按钮组  J*aScript调试技巧_性能分析与内存快照  如何修改Windows截图的默认保存位置_告别C盘让桌面更整洁【教程】 

 2025-12-05

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.