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

Android自定义ListView对比列表后值显示位置错误问题排查

兄弟,这问题我太熟了——ListView的视图复用坑嘛!你遇到的乱显示、滚动时数据乱跑,全是因为没处理好复用导致的旧数据残留,加上getView里的逻辑没覆盖所有情况。来,我给你拆解问题,再把代码改好:

问题根源拆解

  1. 未处理视图复用的旧数据残留:
    当ListView滚动时,滑出屏幕的视图会被复用给新进入的项。你现在的代码里,只有找到匹配的nameValues时才设置value的文本,但没匹配上的时候没有清空这个TextView,导致复用的视图带着之前的旧值显示出来(比如name18会显示之前某个匹配项的值)。
    同样,ImageView的设置也只在服务运行时处理,服务没运行时完全没管,复用的时候会保留旧的箭头图标。

  2. 没有用ViewHolder模式:
    每次getView都重新findViewById,不仅效率低,也更容易因为视图复用出现状态混乱。

  3. 匹配逻辑效率低(可选优化):
    每次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;
        }
    }
}

关键修改点说明

  1. ViewHolder模式:把控件缓存起来,避免每次getView都findViewById,同时保证复用的视图能正确获取控件。
  2. 清空未匹配的value:每次设置value前先判断有没有匹配项,没有就清空文本,彻底解决旧数据残留。
  3. 覆盖ImageView的所有状态:不管服务是否运行,都给ImageView设置状态(服务没运行时可以设默认图或者空),防止复用旧图标。
  4. HashMap优化匹配:把nameValues转成HashMap,O(1)时间就能找到对应value,比遍历列表效率高多了,数据量大的时候效果明显。

这样改完之后,你说的name18/19/20乱显示值、滚动时数据乱跑的问题就都解决了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:02:40