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



我有一个适配器类,我在这个布局中有一个复选框,并使用"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;
}

}

我该如何修复它?

由于匿名内部类的工作方式,它需要是final。考虑到你似乎想要分配相同的功能,你的列表的每一行,我认为你应该做的是创建一个类实现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
        }
    }
});

因为匿名类的onClick函数是用回调它的View的参数来调用的,所以在这种情况下View参数只能是CheckBox。我想这就是你需要的。

关于为什么在匿名内部类中引用的变量必须是final的详细描述,请参阅此回答。但是,在您的情况下,有一个更简单的解决方案:

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

由于OnClickListenerView参数是被点击的实际视图,并且您将此侦听器设置在CheckBox上,因此您可以简单地将View参数强制转换为Checkable并从那里引用它。

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

如果你把final放在你的holder对象中,那么它将被放置在堆中。

为了减少堆上的内存,只使用来自holder的布尔值作为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 ){  
        }
    }
});

简短的回答-只需将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引用给它。

最新更新