Android自定义ListView对比列表后值显示位置错误问题排查
兄弟,这问题我太熟了——ListView的视图复用坑嘛!你遇到的乱显示、滚动时数据乱跑,全是因为没处理好复用导致的旧数据残留,加上getView里的逻辑没覆盖所有情况。来,我给你拆解问题,再把代码改好:
问题根源拆解
未处理视图复用的旧数据残留:
当ListView滚动时,滑出屏幕的视图会被复用给新进入的项。你现在的代码里,只有找到匹配的nameValues时才设置value的文本,但没匹配上的时候没有清空这个TextView,导致复用的视图带着之前的旧值显示出来(比如name18会显示之前某个匹配项的值)。
同样,ImageView的设置也只在服务运行时处理,服务没运行时完全没管,复用的时候会保留旧的箭头图标。没有用ViewHolder模式:
每次getView都重新findViewById,不仅效率低,也更容易因为视图复用出现状态混乱。匹配逻辑效率低(可选优化):
每次getView都遍历整个nameValues列表,数据量大的时候会拖慢列表滚动速度,不如提前把nameValues转成HashMap,直接通过name取value。
修复后的完整代码
我把你的Adapter和相关逻辑改好了,直接替换就行:
public class MainActivity extends AppCompatActivity { private final String TAG = this.getClass().getSimpleName(); List<CustomList> customListList = new ArrayList<>(); CustomAda customAda; ListView listView; LayoutInflater layoutInflater; ServiceSample serviceSample; List<NameValue> nameValues = new ArrayList<>(); // 新增:用HashMap缓存name和value的对应关系,提高匹配效率 private HashMap<String, Integer> nameValueMap = new HashMap<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化列表数据 customListList.add(new CustomList("name1")); customListList.add(new CustomList("name2")); customListList.add(new CustomList("name3")); customListList.add(new CustomList("name4")); customListList.add(new CustomList("name5")); customListList.add(new CustomList("name6")); customListList.add(new CustomList("name7")); customListList.add(new CustomList("name8")); customListList.add(new CustomList("name9")); customListList.add(new CustomList("name10")); customListList.add(new CustomList("name11")); customListList.add(new CustomList("name12")); customListList.add(new CustomList("name13")); customListList.add(new CustomList("name14")); customListList.add(new CustomList("name15")); customListList.add(new CustomList("name16")); customListList.add(new CustomList("name17")); customListList.add(new CustomList("name18")); customListList.add(new CustomList("name19")); customListList.add(new CustomList("name20")); customListList.add(new CustomList("name21")); nameValues.add(new NameValue("name1", 50)); nameValues.add(new NameValue("name2", 20)); // 把nameValues转成HashMap for (NameValue nv : nameValues) { nameValueMap.put(nv.getName().toLowerCase(), nv.getValue()); } serviceSample = new ServiceSample(); listView = findViewById(R.id.listv); customAda = new CustomAda(); listView.setAdapter(customAda); } public static boolean isMyServiceRunning(Class<?> serviceClass, Context context) { ActivityManager manager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE); for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { if (serviceClass.getName().equals(service.service.getClassName())) { return true; } } return false; } // 新增ViewHolder类,缓存控件 static class ViewHolder { ImageView imageView; TextView textView; TextView value; } class CustomAda extends BaseAdapter { @Override public int getCount() { return customListList.size(); } @Override public Object getItem(int i) { return customListList.get(i); } @Override public long getItemId(int i) { return i; } @Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolder holder; if (layoutInflater == null) { layoutInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); } // 处理视图复用和ViewHolder if (view == null) { view = layoutInflater.inflate(R.layout.custom, viewGroup, false); holder = new ViewHolder(); holder.imageView = view.findViewById(R.id.tv1); holder.textView = view.findViewById(R.id.tv2); holder.value = view.findViewById(R.id.tv3); view.setTag(holder); } else { holder = (ViewHolder) view.getTag(); } CustomList customList = customListList.get(i); String currentName = customList.getName(); Log.i(TAG, currentName); // 1. 设置名称文本 holder.textView.setText(currentName); // 2. 设置value文本:先清空,再匹配(解决旧数据残留) Integer matchedValue = nameValueMap.get(currentName.toLowerCase()); if (matchedValue != null) { holder.value.setText(String.valueOf(matchedValue)); } else { holder.value.setText(""); // 没有匹配项时清空 } // 3. 设置ImageView:覆盖服务运行和不运行的所有情况(解决图标残留) boolean isServiceRunning = isMyServiceRunning(ServiceSample.class, getApplicationContext()); if (isServiceRunning) { Log.i(TAG, " Alive"); if (currentName.equalsIgnoreCase("name1")) { holder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.downarr)); } else { holder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.uparrow)); } } else { // 服务没运行时设置默认图标(比如空或者默认箭头,根据你的需求改) holder.imageView.setImageDrawable(null); // 或者 holder.imageView.setImageDrawable(getResources().getDrawable(R.drawable.default_icon)); } // 4. 设置点击事件 holder.textView.setOnClickListener(new View.OnClickListener() { @RequiresApi(api = Build.VERSION_CODES.O) @Override public void onClick(View view) { startForegroundService(new Intent(getApplicationContext(), ServiceSample.class)); customAda.notifyDataSetChanged(); } }); return view; } } }
关键修改点说明
- ViewHolder模式:把控件缓存起来,避免每次getView都findViewById,同时保证复用的视图能正确获取控件。
- 清空未匹配的value:每次设置value前先判断有没有匹配项,没有就清空文本,彻底解决旧数据残留。
- 覆盖ImageView的所有状态:不管服务是否运行,都给ImageView设置状态(服务没运行时可以设默认图或者空),防止复用旧图标。
- HashMap优化匹配:把nameValues转成HashMap,O(1)时间就能找到对应value,比遍历列表效率高多了,数据量大的时候效果明显。
这样改完之后,你说的name18/19/20乱显示值、滚动时数据乱跑的问题就都解决了。
内容的提问来源于stack exchange,提问作者Panache
相关产品推荐
相关产品推荐

