You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android开发:如何避免动态调用getIdentifier()获取R.string资源?

问题

我的应用采用单Activity架构,用户在主Activity的RecyclerView中点击recipe1类按钮时,需要跳转到菜谱详情页并加载对应内容。因为不想为每个菜谱单独创建Activity,所以打算通过Intent传递菜谱名称(如recipe1),再从strings.xml中匹配对应的资源(如cooking_time_recipe1、ingredients_recipe1等)来动态加载详情。

目前我用getIdentifier()实现了功能,但Android Studio提示该方法不推荐使用——反射会影响构建优化与编译校验,直接通过资源ID获取效率更高。想请教有没有替代方案:我可以写函数根据菜谱名称生成对应资源名,但还是需要获取资源ID;手动维护HashMap映射字符串和资源ID又感觉和getIdentifier()原理重复,不确定是否有必要。

附相关代码

适配器代码(不含ViewHolder子类)

public class RecipesViewPagerAdapter extends RecyclerView.Adapter<RecipesViewPagerAdapter.ViewHolder> {

    Context context;
    ArrayList<String> recipes;

    public RecipesViewPagerAdapter(Context context, ArrayList<String> recipes) {
        this.context = context;
        this.recipes = recipes;
    }

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

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        CardTriplet currentRecipes = recipes.get(position);
        holder.cardRecipe1.setOnClickListener(view -> onCardClick(holder.getAdapterPosition(), 0));
        if (currentRecipes.get(1) != null) {
            holder.textRecipe2.setText(currentRecipes.get(1).getName());
            holder.cardRecipe2.setOnClickListener(view -> onCardClick(holder.getAdapterPosition(), 1));
        } else {
            holder.cardRecipe2.setVisibility(View.INVISIBLE);
        }
        if (currentRecipes.get(2) != null) {
            holder.textRecipe3.setText(currentRecipes.get(2).getName());
            holder.cardRecipe3.setOnClickListener(view -> onCardClick(holder.getAdapterPosition(), 2));
        } else {
            holder.cardRecipe3.setVisibility(View.INVISIBLE);
        }
    }

    @Override
    public int getItemCount() {
        return Recipes.size();
    }

    private void onCardClick(int tripletNumber, int cardNumber) {
        Card card = Recipes.get(tripletNumber).get(cardNumber);
        Intent intent = new Intent(context, RecipeActivity.class);
        intent.putExtra("name", card.getName());
        context.startActivity(intent);
    }
}

主Activity获取菜谱列表代码

String[] recipeNames = getResources().getStringArray(R.string.recipes);

(通过遍历该数组初始化ArrayList<CardTriplet> recipes内容)


解决方案

方案1:使用枚举类绑定资源ID

创建枚举类统一管理每个菜谱的关联资源ID,完全避免字符串匹配,编译期就能校验资源存在性,安全且高效。

示例代码:

public enum Recipe {
    RECIPE1(
        R.string.recipe1_name,
        R.string.cooking_time_recipe1,
        R.string.ingredients_recipe1
    ),
    RECIPE2(
        R.string.recipe2_name,
        R.string.cooking_time_recipe2,
        R.string.ingredients_recipe2
    );

    public final int nameResId;
    public final int cookingTimeResId;
    public final int ingredientsResId;

    Recipe(int nameResId, int cookingTimeResId, int ingredientsResId) {
        this.nameResId = nameResId;
        this.cookingTimeResId = cookingTimeResId;
        this.ingredientsResId = ingredientsResId;
    }

    // 根据菜谱名称字符串获取枚举实例
    public static Recipe fromName(String name) {
        for (Recipe recipe : values()) {
            if (name.equals(recipe.name().toLowerCase())) {
                return recipe;
            }
        }
        throw new IllegalArgumentException("Unknown recipe: " + name);
    }
}

使用方式:

  1. 主Activity初始化时,用枚举的nameResId填充菜谱列表;传递Intent时直接传枚举名。
  2. RecipeActivity中接收后直接调用资源:
String recipeName = getIntent().getStringExtra("name");
Recipe recipe = Recipe.fromName(recipeName);
tvCookingTime.setText(getString(recipe.cookingTimeResId));
tvIngredients.setText(getString(recipe.ingredientsResId));

方案2:预构建静态资源映射类

如果菜谱数量较多,可创建静态类在App启动时一次性初始化资源映射,仅调用一次getIdentifier(),避免重复反射消耗。

示例代码:

public class RecipeResources {
    private static final Map<String, RecipeRes> RES_MAP = new HashMap<>();

    public static void init(Context context) {
        Resources res = context.getResources();
        String[] recipeNames = res.getStringArray(R.array.recipes); // 建议改用array资源存储菜谱名称列表
        for (String name : recipeNames) {
            int cookingTimeId = res.getIdentifier("cooking_time_" + name, "string", context.getPackageName());
            int ingredientsId = res.getIdentifier("ingredients_" + name, "string", context.getPackageName());
            RES_MAP.put(name, new RecipeRes(cookingTimeId, ingredientsId));
        }
    }

    public static RecipeRes getRecipeRes(String name) {
        return RES_MAP.get(name);
    }

    public static class RecipeRes {
        public final int cookingTimeId;
        public final int ingredientsId;

        public RecipeRes(int cookingTimeId, int ingredientsId) {
            this.cookingTimeId = cookingTimeId;
            this.ingredientsId = ingredientsId;
        }
    }
}

使用方式:

  1. 在Application的onCreate()中调用RecipeResources.init(this);完成初始化。
  2. RecipeActivity中直接获取资源:
String recipeName = getIntent().getStringExtra("name");
RecipeResources.RecipeRes res = RecipeResources.getRecipeRes(recipeName);
tvCookingTime.setText(getString(res.cookingTimeId));

方案3:使用资源数组关联

将每个菜谱的资源ID按顺序存入数组,与菜谱名称数组一一对应,完全无反射,实现简单。

示例(在arrays.xml中定义):

<string-array name="recipe_names">
    <item>recipe1</item>
    <item>recipe2</item>
</string-array>
<integer-array name="recipe_cooking_time_ids">
    <item>@string/cooking_time_recipe1</item>
    <item>@string/cooking_time_recipe2</item>
</integer-array>
<integer-array name="recipe_ingredients_ids">
    <item>@string/ingredients_recipe1</item>
    <item>@string/ingredients_recipe2</item>
</integer-array>

主Activity初始化时关联资源:

String[] names = getResources().getStringArray(R.array.recipe_names);
int[] cookingTimeIds = getResources().getIntArray(R.array.recipe_cooking_time_ids);
int[] ingredientsIds = getResources().getIntArray(R.array.recipe_ingredients_ids);

ArrayList<CardTriplet> recipes = new ArrayList<>();
for (int i = 0; i < names.length; i++) {
    Card card = new Card(names[i], cookingTimeIds[i], ingredientsIds[i]);
    // 将card加入CardTriplet并填充到recipes列表
}

传递Intent时直接传递资源ID或序列化的Card对象,RecipeActivity可直接使用ID获取资源。

方案对比

  • 枚举类:最安全,编译期校验资源,适合菜谱数量固定的场景。
  • 静态映射类:兼顾灵活性与性能,适合菜谱可能动态增加的场景。
  • 资源数组关联:完全无反射,实现简单,适合资源结构统一的场景。

以上方案均比跳转时直接调用getIdentifier()更高效,且避免了编译期无法校验资源的问题。

内容的提问来源于stack exchange,提问作者user20958695

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 17:15:14