在ListView中切换侦听器会给出错误的结果

时间:2013-05-05 17:44:02

标签: java android

这是我的数组适配器。它为列表中的每个开关按钮分配一个侦听器,该侦听器依赖于数据库给出的id(使用devices.id传递)。但由于某种原因,交换机监听器表现得很奇怪。

出于调试目的,无论状态如何,我只是在更改侦听器上使用相同的内容。因此,无论我将其打开还是关闭,它都会运行相同的功能。

我的问题有时候我打开第一个开关,第7个开关打开,当我打开第2个开关时n,第8个开关打开。我正在做错事,但我真的没弄明白。第6,第7和第8个开关无法正常工作。

你可以在液晶显示屏上看到,当我按顺序打开开关时,它必须给我输出“12345678”。但事实并非如此。相反,它给出“12345112”。

编辑:Emil Adz给出的答案有效。给我输出“12345678”。但我有一个问题。如果我打开开关1,向下滚动直到看不见并向上滚动,开关重置为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 个答案:

答案 0 :(得分: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时,所有列表都会变得疯狂。请注意它可以获得性能,但我没有找到适合此问题的解决方案。当列表不那么大时,它不会发生太大变化。

<强>更新 首先看一下本指南:

Multi-clickable 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中,状态会发生变化。

答案 1 :(得分: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是存储Cursor数据的最终对象。

无论是否检查开关,都必须调用setChecked方法。

希望它有所帮助!

答案 2 :(得分:0)

您的ListView当前正在将行的状态与行的位置相关联,而不是与行的实际内容相关联。当用户滚动ListView,并且它的内容被回收时,它会根据位置输入切换值。

要解决此问题,您需要以与实际行关联的方式保存交换机的状态。 Android: Problem With ListViews and CheckBoxes处理完全相同的问题,但使用复选框。它对于解决这些问题非常有用