ListView中的Switch监听器给出了错误的结果

3
这是我的Array适配器。它为列表中的每个开关按钮分配一个监听器,该监听器取决于数据库给出的ID(使用devices.id传递)。但由于某种原因,开关监听器无法从开关获取正确的状态。
出于调试目的,我只是使用相同的on change监听器,而不管状态如何。因此,无论我打开还是关闭它,它都运行相同的函数。
我的问题是,有时候当我打开第一个开关时,第七个开关会被打开,当我打开第二个开关时,第八个开关会被打开。我做错了什么,但我真的搞不清楚。第6、7和8个开关不能正常工作。
您可以在LCD上看到,当我按顺序打开开关时,它必须给我输出“12345678”。但它没有。相反,它给出“12345112”。
编辑:Emil Adz给出的答案有效。给我输出“12345678”。但我有一个问题。如果我打开switch1,滚动到不可见并向上滚动,开关会重置为OFF。为什么会这样? Wireless Controller Screenshot Wireless Controller Screenshot LCD Output
public class DevListAdapter extends ArrayAdapter<Devices>{

    Context context; 
    int layoutResourceId;    
    Devices data[] = null;
    private TextView txt = null;
    
    public DevListAdapter(Context context, int layoutResourceId, Devices[] data) {
        super(context, layoutResourceId, data);
        this.layoutResourceId = layoutResourceId;
        this.context = context;
        this.data = data;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("WCAM","GetView");
        View row = convertView;
        DeviceHolder holder = null;
        Devices devices = data[position];
        String id = devices.id;

        Log.d("WCAM","Inflator");
        if(row == null)
        {
            Log.d("WCAM","True");
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, parent, false);

            holder = new DeviceHolder();
            holder.devTxt = (TextView)row.findViewById(R.id.devdesc);
            holder.nameTxt = (TextView)row.findViewById(R.id.devname);
            holder.toggleSwitch = (Switch)row.findViewById(R.id.toggleswitch);
            holder.txt = (TextView) row.findViewById(R.id.debug);
            final int i;
            
            if(id.matches("1")) {
                i = 0x31;
            }
            else if(id.matches("2")) {
                i = 0x32;
            }
            else if(id.matches("3")) {
                i = 0x33;
            }
            else if(id.matches("4")) {
                i = 0x34;
            }
            else if(id.matches("5")) {
                i = 0x35;
            }
            else if(id.matches("6")) {
                i = 0x36;
            }
            else if(id.matches("7")) {
                i = 0x37;
            }
            else {
                i = 0x38;
            }
            holder.toggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    Control._service.write(i);
                }
             });
            
            row.setTag(holder);
        }
        else
        {
            Log.d("WCAM","False");
            holder = (DeviceHolder)row.getTag();
        }
        
        holder.devTxt.setText(devices.dev);
        holder.nameTxt.setText(devices.name);
        holder.txt.setText(id);
        
        return row;
    }
    
    static class DeviceHolder
    {
        TextView devTxt;
        TextView txt;
        TextView nameTxt;
        Switch toggleSwitch;
    }
}

devices是Devices.java对象的一个实例

public class Devices {
    public String dev;
    public String name;
    public String id;
    public Devices(){
        super();
    }
    
    public Devices(String _id, String dev, String name) {
        super();
        this.dev = dev;
        this.name = name;
        this.id = _id;
    }
}
3个回答

1
尝试像这样:

这样做:

public class DevListAdapter extends ArrayAdapter<Devices>{

Context context; 
int layoutResourceId;    
Devices data[] = null;
private TextView txt = null;

public DevListAdapter(Context context, int layoutResourceId, Devices[] data) {
    super(context, layoutResourceId, data);
    this.layoutResourceId = layoutResourceId;
    this.context = context;
    this.data = data;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    Log.d("WCAM","GetView");
    DeviceHolder holder = null;
    Devices devices = data[position];
    String id = devices.id;

    Log.d("WCAM","Inflator");
        Log.d("WCAM","True");
        LayoutInflater inflater = ((Activity)context).getLayoutInflater();
        row = inflater.inflate(layoutResourceId, parent, false);

        holder = new DeviceHolder();
        holder.devTxt = (TextView)row.findViewById(R.id.devdesc);
        holder.nameTxt = (TextView)row.findViewById(R.id.devname);
        holder.toggleSwitch = (Switch)row.findViewById(R.id.toggleswitch);
        holder.txt = (TextView) row.findViewById(R.id.debug);
        final int i;

        if(id.matches("1")) {
            i = 0x31;
        }
        else if(id.matches("2")) {
            i = 0x32;
        }
        else if(id.matches("3")) {
            i = 0x33;
        }
        else if(id.matches("4")) {
            i = 0x34;
        }
        else if(id.matches("5")) {
            i = 0x35;
        }
        else if(id.matches("6")) {
            i = 0x36;
        }
        else if(id.matches("7")) {
            i = 0x37;
        }
        else {
            i = 0x38;
        }
        holder.toggleSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Control._service.write(i);
            }
         });

    holder.devTxt.setText(devices.dev);
    holder.nameTxt.setText(devices.name);
    holder.txt.setText(id);

    return row;
}

基本上由于某种原因,我遇到了这个问题好几次。当你使用convertView时,整个列表都会出现问题。请注意,这会导致性能下降,但我没有找到合适的解决方案。此外,当列表不是很大时,它并不会改变太多。

更新: 首先看一下这个指南:

可多次点击的 ListView

与复选框状态一样,你会看到:

    //setting data into the the ViewHolder.
    holder.title.setText(RowData.getName());
    holder.checked.setChecked(RowData.isChecked());

    //return the row view.
    return convertView;

在您的情况下,经过这段代码后:

    holder.devTxt.setText(devices.dev);
    holder.nameTxt.setText(devices.name);
    holder.txt.setText(id);

你应该添加类似以下的内容:
   holder.toggleSwitch.setChecked(devices.isChecked());

在你的onCheckedChanged中持久化状态更改。


这个可以运行。输出为“12345678”。但是我有一个问题。如果我打开开关1,向下滚动直到它不可见,然后向上滚动,开关会重置为关闭状态。为什么会这样? - Xpleria
当您更改切换按钮时会发生什么?"Control._service.write(i);"是什么?您需要在某个地方注明您正在更改切换按钮的状态,并且当您重新创建此行(当它再次可见时)以获取此设置并将其应用于正确位置的行。 - Emil Adz
我完全不懂安卓。你能否给我提供一个示例代码,以便重新创建带有新按钮状态的行或至少提供参考?谢谢。Control._service.write(i);Control 活动中的一个方法,通过蓝牙将数据发送到微控制器。 - Xpleria
isChecked()是什么?我该如何实现它? - Xpleria
通常情况下,当您使用ArrayAdapter时,会有一个对象数组,其中数组中的每个对象都分配给列表中的一行。isCheck应该作为布尔参数放置在表示列表项的对象中。请检查我发布的示例链接,并查看复选框是如何实现的。 - Emil Adz
显示剩余3条评论

1
我在我的切换视图上遇到了同样的问题。经过大量搜索和阅读,我发现这个问题是由Android推广的一种视图优化导致的。它试图重用视图对象。这确实有效,但如果你不注意或者只是不知道行为(像我一样),就会出现一些相当奇怪的结果。

无论如何,我找到了这个简单的解决方案:

我将我的视图存储在ViewHolder中,并编写了onCheckedChanged事件,就像你一样。诀窍是,在设置开关是否选中并定义CheckedChange监听器之前,我只需将回调重置为null,这可以确保另一个开关的事件不被触发。

以下是代码片段:

...
viewHolder.switchView.setOnCheckedChangeListener(null);
viewHolder.switchView.setChecked(myObject.getStatus() > 0);
...
viewHolder.switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                ...
            }
        });

myObject是一个最终对象,其中存储了光标数据。

无论开关是否被选中,都必须调用setChecked方法。

希望能对某些人有所帮助!


0

你的ListView目前将一个开关的状态与一行的位置相关联,而不是与一行的实际内容相关联。当用户滚动ListView并且其内容被回收时,它会根据位置放入开关值。

要解决这个问题,你需要以一种与实际行相关联的方式保存开关的状态。Android: Problem With ListViews and CheckBoxes处理了完全相同的问题,但使用复选框。它非常有用,可以解决这些问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接