Android读取XML多维数组异常:属性值重复且列表元素单一
解决XML多维数组读取的两个问题
我来帮你搞定这两个困扰你的问题,先拆解下原因,再一步步给出修正方案:
问题根源分析
1. 所有属性都被name值覆盖的原因
你在Activity里获取TypedArray中属性值的方式完全走偏了:
String deviceName = item.getString(item.getResources().getIdentifier("name", "string", packageName));
这段代码是去全局的string资源里找名为"name"的资源ID,而不是读取当前这个TypedArray里的item值。这就导致weight、repetition也都拿到了同一个全局的"name"资源值,自然所有属性都变成了name的值。
正确的做法是直接操作当前的TypedArray,通过item的name或者索引来获取对应的值。
2. 列表里全是同一个元素的原因
这个其实是第一个问题的连锁反应——因为所有属性值都错成了同一个,所以看起来所有元素都一模一样。不过还要提一句:虽然你每次循环都new了DeviceInformationOverview对象,但属性值错了,自然元素看起来重复。另外你的ResourceHelper循环逻辑也有小隐患,后面一起修正。
修正后的完整代码
1. 先给属性定义Styleable(可选但更健壮)
在res/values/attrs.xml中添加以下内容,让TypedArray可以通过name识别item:
<declare-styleable name="DeviceInformation"> <attr name="name" format="string" /> <attr name="weight" format="string" /> <attr name="repetition" format="string" /> </declare-styleable>
2. 修正Activity中的取值逻辑
替换你原来的循环代码,改成正确的TypedArray取值方式:
for (TypedArray item : ResourceHelper.getMultiTypedArray(this, "device")) { // 通过Styleable获取对应item的索引 int nameIndex = item.getIndex(R.styleable.DeviceInformation_name); int weightIndex = item.getIndex(R.styleable.DeviceInformation_weight); int repetitionIndex = item.getIndex(R.styleable.DeviceInformation_repetition); // 读取对应的值 String deviceName = item.getString(nameIndex); String weight = item.getString(weightIndex); String repetition = item.getString(repetitionIndex); // 创建新的对象并赋值 DeviceInformationOverview deviceInfo = new DeviceInformationOverview(); deviceInfo.setDeviceName(deviceName); deviceInfo.setWeight(weight); deviceInfo.setRepetitionCount(repetition); devices.add(deviceInfo); // 必须回收TypedArray,避免内存泄漏 item.recycle(); }
如果觉得Styleable麻烦,也可以直接按固定索引取值(因为你的XML中每个array的item顺序固定):
for (TypedArray item : ResourceHelper.getMultiTypedArray(this, "device")) { String deviceName = item.getString(0); String weight = item.getString(1); String repetition = item.getString(2); DeviceInformationOverview deviceInfo = new DeviceInformationOverview(); deviceInfo.setDeviceName(deviceName); deviceInfo.setWeight(weight); deviceInfo.setRepetitionCount(repetition); devices.add(deviceInfo); item.recycle(); }
3. 修正ResourceHelper的循环逻辑
原有的do-while会在找不到资源时抛出异常,改成更安全的循环方式:
import android.content.Context; import android.content.res.TypedArray; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import ch.lbuehler.updateapp.R; public class ResourceHelper { public static List<TypedArray> getMultiTypedArray(Context context, String key) { List<TypedArray> arrayList = new ArrayList<>(); Class<R.array> resArrayClass = R.array.class; int counter = 0; while (true) { try { Field field = resArrayClass.getField(key + "_" + counter); int resourceId = field.getInt(null); TypedArray typedArray = context.getResources().obtainTypedArray(resourceId); arrayList.add(typedArray); counter++; } catch (NoSuchFieldException e) { // 找不到对应字段,说明所有数组已遍历完成 break; } catch (Exception e) { e.printStackTrace(); break; } } return arrayList; } }
4. 补充Adapter的完整逻辑(修复不完整部分)
确保Adapter能正确绑定每个元素的数据:
public class TrainingPlanListAdapter extends ArrayAdapter<DeviceInformationOverview> { private final Activity context; private final List<DeviceInformationOverview> deviceList; public TrainingPlanListAdapter(Activity context, List<DeviceInformationOverview> deviceList) { super(context, R.layout.exercise_overview_item, deviceList); this.context = context; this.deviceList = deviceList; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = context.getLayoutInflater(); View exerciseItem = inflater.inflate(R.layout.exercise_overview_item, parent, false); // 获取控件引用 TextView weightField = exerciseItem.findViewById(R.id.weightInfo); TextView repetitionField = exerciseItem.findViewById(R.id.repeatitionInfo); Button deviceButton = exerciseItem.findViewById(R.id.buttonDevice); // 获取当前位置的设备信息 DeviceInformationOverview currentDevice = deviceList.get(position); deviceButton.setText(currentDevice.getDeviceName()); weightField.setText(currentDevice.getWeight()); repetitionField.setText(currentDevice.getRepetitionCount()); // 按钮点击事件 deviceButton.setOnClickListener(v -> { // 这里补充你的跳转逻辑 }); return exerciseItem; } }
总结
- 核心问题是错误地使用全局资源ID获取TypedArray中的item值,导致所有属性值重复;
- 修正取值逻辑后,每个元素的属性会正确读取,自然就不会出现"同一个元素"的假象;
- 一定要记得回收
TypedArray,避免内存泄漏; - 优化后的
ResourceHelper循环逻辑会更稳定,不会因为找不到资源而抛出异常。
内容的提问来源于stack exchange,提问作者Larissa
相关产品推荐
相关产品推荐

