当我在适配器类上使用方法“OnClickListener”时,为什么编译器要求我声明变量“final”?

时间:2014-06-02 19:23:55

标签: android checkbox onclicklistener android-adapter

我有一个适配器类,我在这个布局中有一个复选框,并使用" OnClickListener"方法我尝试声明" check = true"但ADT要求我声明final ViewHolder

这是我的适配器类

public class MaterialAdapter extends ArrayAdapter<MaterialModel> {

public MaterialAdapter(Context context, int resource,
        List<MaterialModel> objects) {
    super(context, resource, objects);
    // TODO Auto-generated constructor stub
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null; // IN THIS PART THE ADT ASK ME DECLARE FINAL
    if(convertView == null){
        LayoutInflater inflater = LayoutInflater.from(getContext());
        convertView = inflater.inflate(R.layout.material_adapter, parent, false);

        holder = new ViewHolder();
        holder.id_material = (TextView) convertView.findViewById(R.id.id_material);
        holder.nombre_material = (TextView) convertView.findViewById(R.id.nombre_material);
        holder.checkBox1 = (CheckBox) convertView.findViewById(R.id.checkBox1); 
        holder.checkBox1.setChecked(false);
        convertView.setTag(holder);
    }else{
        holder = (ViewHolder) convertView.getTag();
    }
    MaterialModel entry = getItem(position);
    holder.id_material.setText(entry.getIdMaterial());
    holder.nombre_material.setText(entry.getNombreMaterial());

    holder.checkBox1.setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            if(holder.checkBox1.isChecked()){  // I CAN'T USE THIS BECAUSE IT'S NOT FINAL

            }

        }
    });


    return convertView;
}

public static class ViewHolder{
    private TextView id_material;
    private TextView nombre_material;
    private CheckBox checkBox1;
}

}

我该如何解决?

4 个答案:

答案 0 :(得分:2)

由于匿名内部类的工作方式,它需要是最终的。考虑到您似乎想要将相同的功能分配给列表的每一行,我相信您应该做的是创建一个实现OnClickListener接口的类,并保存对checkBox的引用,其中的状态为功能取决于。但是,我有点担心你有多少物品,因为如果你有很多物品,这会引起严重的记忆问题。

所以我刚想到的是,尝试以下方法:

holder.checkBox1.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        CheckBox checkBox = (CheckBox) v;
        if(checkBox.isChecked()){  // I CAN USE THIS

        }
    }
});

因为使用View的参数调用匿名类的onClick函数,因此在这种情况下View参数只能是CheckBox。我认为这就是你所需要的。

答案 1 :(得分:1)

有关匿名内部类中引用的变量必须为final的原因的详细说明,请参阅this answer。但是,在您的情况下,有一个更简单的解决方案:

holder.checkBox1.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View checkBox) {
        if ((Checkable) checkBox).isChecked()) {
            // Do Stuff
        }
    }
}

由于View的{​​{1}}参数是点击的实际视图,并且您在OnClickListener上设置了此侦听器,因此您只需转换CheckBox参数View并从那里引用它。

编辑:此外,您实际上可能正在寻找Checkable而不是CheckBox.OnCheckedChangeListener

答案 2 :(得分:0)

简短回答 - 只需将ViewHolder复制到final变量中,然后在View.OnClickListener内使用它。像这样:

final ViewHolder holderRef = holder; 
holder.checkBox1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        if(holderRef.checkBox1.isChecked()){
        }
    }
});

更长的答案 - 基本上由于Java管理闭包的方式,你需要做到最终。 您实际上是在这里创建OnClickListener的匿名内部子类的实例。在该类中使用的任何变量都通过自动生成的构造函数复制它们的值。所以这是您可以拥有的另一种解决方案 - 您可以显式创建OnClickListener子类并将ViewHolder引用传递给它。

答案 3 :(得分:0)

如果您将final放在holder对象中,那么它将放在堆中。

为了减少一些进入堆的内存,只需使用holder中的boolean作为final而不是整个对象。

示例:

holder.nombre_material.setText(entry.getNombreMaterial());
final boolean isChecked = holder.checkBox1.isChecked();

holder.checkBox1.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        if(isChecked ){  

        }

    }
});