"Variable is accessed from within inner class, needs to be declared final" 自定义适配器出错

"Variable is accessed from within inner class, needs to be declared final" error in custom adapter

我正在尝试实现带有复选框的多选 ListView,并且我已经定义了一个自定义适配器 class,它如下所示:

package com.ameer.easycooking;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class CategoryAdapter extends ArrayAdapter<String> {

    private List<String> categoryList;
    private Context context;
    private List<String> selected = new ArrayList<>();

    public CategoryAdapter(List<String> categoryList, Context context) {
        super(context, R.layout.category_list_item, categoryList);
        this.categoryList = categoryList;
        this.context = context;
    }

    public static class CategoryHolder {
        private CheckBox checkBox;
        private TextView categoryName;

        public CategoryHolder(CheckBox checkBox, TextView categoryName) {
            this.checkBox = checkBox;
            this.categoryName = categoryName;
        }

        public CheckBox getCheckBox() {
            return checkBox;
        }

        public void setCheckBox(CheckBox checkBox) {
            this.checkBox = checkBox;
        }

        public TextView getCategoryName() {
            return categoryName;
        }

        public void setCategoryName(TextView categoryName) {
            this.categoryName = categoryName;
        }
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View v = convertView;

        CategoryHolder holder = new CategoryHolder((CheckBox) v.findViewById(R.id.checkBox), (TextView) v.findViewById(R.id.name));

        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = inflater.inflate(R.layout.category_list_item, null);

            holder.getCheckBox().setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        if (isChecked) {
                            selected.add(holder.getCheckBox().getText().toString());
                        }else{
                            selected.remove(holder.getCheckBox().getText().toString());
                        }
                }
            });
            v.setTag(holder);
        } else {
            holder = (CategoryHolder) v.getTag();
        }

        String category = categoryList.get(position);
        holder.categoryName.setText(category);
        holder.checkBox.setTag(category);

        return v;
    }

    public List<String> getSelected() {
        return selected;
    }
}

我想将选中的项目存储在 selected 列表中,然后使用 class 末尾的 getter 检索它们,但它告诉我 'holder' 需要声明为 final,如果我将其声明为 final,我将无法再使用 holder = (CategoryHolder) v.getTag(); 为其赋值 所以解决一个问题就是创造另一个问题。 :( 我怎样才能解决这两个问题?

提前致谢。

您可以从OnCheckedChangeListener的参数中得到必要的信息:buttonView是选中的元素。所以你需要做的就是:

@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
       CheckBox checkbox;
       if (buttonView instanceof CheckBox )
       {
           checkbox = (CheckBox)buttonView;
       }
       else return;

       if (isChecked) {
            selected.add(checkbox.getText().toString());
       }else{
            selected.remove(checkbox.getText().toString());
       }
}

如果您将对象用于内部 class,您应该声明它 final。 在这里解释:Why Java inner classes require "final" outer instance variables?

因此,您可以使该变量成为最终变量:

final CategoryHolder holder = convertView != null ? 
    (CategoryHolder) v.getTag() :
    new CategoryHolder((CheckBox) v.findViewById(R.id.checkBox), (TextView) v.findViewById(R.id.name));