【问题标题】:How do I stop Android ListView from resetting values after scrolling?如何在滚动后阻止 Android ListView 重置值?
【发布时间】:2016-07-28 12:39:49
【问题描述】:

我有一个带有自定义适配器的 ListView,每一行都包含一个用户可以设置的搜索栏。每个 seekbar 都有一个 seekBarChangeListener 设置在适配器中,当用户更改 ListView 中 seekbar 的值时,适配器的 arraylist 会更新。这是我在适配器的getView() 中得到的:

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    final ViewHolder viewHolder;

    View v = convertView;
    if(v == null){
        v = inflater.inflate(R.layout.row, null);
        viewHolder = new ViewHolder();
        viewHolder.contentView = (TextView) v.findViewById(R.id.pref_content);
        viewHolder.sliderView = (SeekBar) v.findViewById(R.id.pref_slider);
        v.setTag(viewHolder);
    }else{
        viewHolder = (ViewHolder) v.getTag();
    }

    // Get initial values
    Prompt p = promptList.get(position);
    String content = p.getContent();
    int sliderLevel = p.sliderLevel();

    // Set views with initial values
    viewHolder.contentView.setText(content);
    viewHolder.sliderView.setMax(noOfLevels);
    viewHolder.sliderView.setProgress(sliderLevel);

    // Set Change Listener for Seekbar
    viewHolder.sliderView.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            // Set new level of the Slider in the Array List
            promptList.get(position).setSliderLevel(progress);
        }
    });

    return v;
}

因此,当用户更改搜索栏的值时,ChangeListener 从 ArrayList 获取对象并设置其新的 sliderLevel 值。但是,如果我在 ListView 中设置了一个新值,然后再次向下滚动,以便再次调用 getView,则即使我更改了 arrayList 中的值,seekbar 也会返回到它的初始值。

有人可以帮我看看我哪里出错了吗?谢谢

【问题讨论】:

  • 你应该创建一个单独的列表来保存每个seekbar的值,并在getView方法中设置为对应的值。 Listview 回收视图
  • ListView 回收视图,但适配器始终保持相同的 ArrayList。当视图被回收时,再次调用getView(),访问的是同一个ArrayList。因此,如果我更改 ArrayList,一旦再次调用 getView(),它应该会显示新值。
  • 是的,你是对的,这很奇怪
  • 我会尝试添加对viewHolder.sliderView.setMax(..)的调用

标签: android listview arraylist


【解决方案1】:

每次用户更改SeekBar 值时,都会调用方法onProgressChanged()。但它也将被“按系统”调用(我认为这是因为 SeekBar 被作为 convertView 的一部分回收)。所以需要检查fromUser是否为真:

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
    if (!fromUser) return;

    // Set new level of the Slider in the Array List
    promptList.get(position).setSliderLevel(progress);
}

在您的情况下,您不必调用notifyDatasetChanged(),因为只要仍然显示一个特定的SeekBar,只有用户会更改其进度。一旦它被隐藏(因为用户滚动)并且该位置的数据稍后必须再次显示,getView() 将被调用,并且正确的值将设置为 convertView 中的SeekBar 恰好显示您的数据然后。

另一个想法(性能原因):考虑只在用户停止操作滑块后立即更改promptList.get(position) 的进度值。为此,您可以覆盖 onStopTrackingTouch() 而不是 onProgressChanged() 并写入

 promptList.get(position).setSliderLevel(seekBar.getProgress());

【讨论】:

  • 很好的解释,谢谢。我用大致相同的解决方案回答了我自己的问题,但我会接受这个答案,因为这是导致我的 ListView 出现大多数问题的原因。谢谢。
【解决方案2】:

所以我设法通过将搜索栏的OnSeekBarChangeListener() 中的promptList.get(position).setSliderLevel(progress)onProgressChanged() 移动到onStopTrackingTouch(),并将ListView 的高度从wrap_content 更改为match_parent 来解决这个问题。这两件事都导致了不良行为。

由于某种原因,onProgressChanged() 在运行时被调用,根本没有触及 seekbar,即使在使用 arrayList 中的值初始化 seekbar 之后设置了 changeListener。

编辑:0X0nosugar 的回答解释了这种行为

【讨论】:

  • 你能告诉我 serSliderLevel() 在做什么以及放在哪里吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 1970-01-01
  • 2017-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多