1

我的 Activity 中有一个 ListView,对于每一行,我都使用带有 TextView 和两个按钮的自定义布局。当我单击这两个按钮中的任何一个时,我希望执行某个操作。在我的 ArrayAdapter 中,在 getView 方法中,我将 onClickListeners 设置为这两个按钮。

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

    View v = convertView;
    if (v == null) {
        v = li.inflate(R.layout.process_row, null);
    }


    final Button processCheck = (Button) v.findViewById(R.id.processCheck);
    processCheck.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            if (process.isChecked() == false) {
                process.setChecked(true);
                processCheck.setBackgroundColor(Color.BLUE);
            }
            else {
                process.setChecked(false);
                processCheck.setBackgroundColor(Color.RED);
            }

        }
    });

    return v;
}

ArrayList 保存了 mz 自定义类进程的对象,代码中的进程就是这个类的一个实例。因此,现在当我单击 ListView 行之一中的此按钮时,我希望更改给定 Process 实例中的布尔变量并更改按钮的颜色。会发生这种情况,但不仅是这一排,还有 3-4 排。所以点击后,我有大约 5 个更改按钮而不是一个。你知道我在这里做错了什么吗?请注意,我不太了解 ListActivity 编码,大部分代码实际上是从示例文件中复制和编辑的。谢谢!

编辑:

问题可能只出在某处的布局上。当我单击按钮时,它们中的更多会改变颜色,但只有我单击的进程的布尔值才会改变。

4

4 回答 4

0

问题就在这里

View v = convertView;
    if (v == null) {
        v = li.inflate(R.layout.process_row, null);
    }

这是因为 View v 只为可见项目创建,然后被重用。所以只需删除 ifv==null) 这样你的代码就可以了

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

    View v = convertView;

        v = li.inflate(R.layout.process_row, null);



    final Button processCheck = (Button) v.findViewById(R.id.processCheck);
    processCheck.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            if (process.isChecked() == false) {
                process.setChecked(true);
                processCheck.setBackgroundColor(Color.BLUE);
            }
            else {
                process.setChecked(false);
                processCheck.setBackgroundColor(Color.RED);
            }

        }
    });

    return v;
}
于 2012-05-12T21:06:26.027 回答
0

每次呈现列表时都会调用 getView。因此,您应该在 gerview 条件中添加设置颜色: public View getView(int position, View convertView, ViewGroup parent) {

View v = convertView;

    v = li.inflate(R.layout.process_row, null);

//在下面添加这些行

if (process.isChecked() == false) {

        processCheck.setBackgroundColor(Color.BLUE);
    }
    else {

        processCheck.setBackgroundColor(Color.RED);
    }

final Button processCheck = (Button) v.findViewById(R.id.processCheck);
processCheck.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        if (process.isChecked() == false) {
            process.setChecked(true);
            processCheck.setBackgroundColor(Color.BLUE);
        }
        else {
            process.setChecked(false);
            processCheck.setBackgroundColor(Color.RED);
        }

    }
});

return v;

}

于 2012-05-12T21:17:30.280 回答
0

编辑:在帖子末尾找到一个“开箱即用”示例!

因为您看到多行受到影响,我猜这与系统如何回收资源有关,并且可能对 的引用Button不明确。

我不确定我在哪里学习了这种做法(Android 教程或我们以前通过这些教程学习 android 的开发人员)。但是建议是使用嵌套类ViewHolder

private static class ViewHolder {

    public Button processCheck;
}

将其放在适配器或您getView()声明的任何类中,然后对其进行修改:

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

    View v = convertView;
    $ViewHolder viewHolder; //wait for the magic!
    if (v == null) {
        v = li.inflate(R.layout.process_row, null);
        $viewHolder = new ViewHolder();
        $viewHolder.processCheck = (Button) v.findViewById(R.id.processCheck);
        $v.setTag(viewHolder);
        //ok we somewhat stuffed an object with the Button into our View... so what?
    }

    $viewHolder = (ViewHolder) v.getTag();
    //see explanation below

    $viewHolder.processCheck.setOnClickListener(new View.OnClickListener() {
    //set the onClickListener for this and only this button.
        @Override
        public void onClick(View v) {
            if (process.isChecked() == false) {
                process.setChecked(true);
                processCheck.setBackgroundColor(Color.BLUE);
            }
            else {
                process.setChecked(false);
                processCheck.setBackgroundColor(Color.RED);
            }

        }
    });

    return v;
}

(我用$标记了更改。我认为如果我使用eclipse,他们应该很容易发现。否则查找+替换^^)

因此getTag()将返回一个与调用的视图关联的对象。Object 是如此令人难以置信的通用性,您可以将它调用到您创建的 ViewHolder 对象中。因此,您可以引用按钮并将其存储在对象中。
现在,每当您获得 ListView 时,您都可以检索对象并设置OnclickListener新的和新的。这样你就只有一个 Listener 被调用。

请注意,您不必v.setTag();再次调用来“保存”您的更改。

一个陷阱可能是如果你做了一些疯狂的事情,比如出于什么原因在同一个列表中膨胀不同的布局。您可能很想为ViewHandlers它们分配不同的值,例如:

if (v == null) {
    if( someCriteria) {
        v = li.inflate(R.layout.process_row, null);
        viewHolder = new ViewHolder(); 
    }
        v = li.inflate(R.layout.process_another_row, null);
        viewHolder = new AnotherViewHolder(); 
}

然后getView()对一个或另一个 ViewHolder 的未经检查的调用可能会在运行时引发错误!

于 2012-05-12T21:25:01.010 回答
0

这就是我解决它的方法:

在我的适配器中,我创建了类:

private static class ViewHolder {
    public Button       processCheck;
    public Process      process;
    public TextView     name;
}

比我为我的按钮创建了一个自定义侦听器:

private class CustomListener implements View.OnClickListener {

    public ViewHolder   viewHolder;

    public CustomListener(ViewHolder holder) {
        this.viewHolder = holder;
    }

    @Override
    public void onClick( View v ) {
        if (viewHolder.process.isChecked()) {
            viewHolder.name.setText("not checked");
            viewHolder.process.setChecked(false);               
        }
        else {
            viewHolder.name.setText("checked");
            viewHolder.process.setChecked(true);                
        }           
    }
}

还有我的 getView 函数:

public View getView( int position, View convertView, ViewGroup parent ) {
    ViewHolder viewHolder;

    if ( convertView == null ) {
        convertView = mInflater.inflate( R.layout.process_row, null );

        viewHolder = new ViewHolder();
        viewHolder.processCheck = (Button) convertView.findViewById(R.id.processCheck);
        viewHolder.name = (TextView) convertView.findViewById(R.id.processName);            
        convertView.setTag( viewHolder );
    }


    viewHolder = (ViewHolder) convertView.getTag();
    viewHolder.process = list.get(position);

    if (viewHolder.process.isChecked()) {
        viewHolder.name.setText("checked");         
    } 
    else {
        viewHolder.name.setText("not checked");         
    }       

    CustomListener listener = new CustomListener( viewHolder );
    viewHolder.processCheck.setOnClickListener(listener);

    return convertView;
}
于 2012-05-13T12:28:57.703 回答