2

我正在尝试基于自定义可检查列表实现多项选择列表。当只需要一个列表时它相当简单,但是当您应该能够在多个列表之间切换时会变得复杂。

在我的例子中,一个活动的顶部有许多代表类别的按钮。在它们的正下方有一个列表,其中包含当前选择的类别中的项目。

项目的列表视图基于ListAdapter扩展BaseAdapter

public class ListAdapter extends BaseAdapter {

    private static LayoutInflater layoutInflater = null;
    private ArrayList<String> data;

    public ListAdapter(Activity activity, ArrayList<String> data) {
        layoutInflater = (LayoutInflater) activity
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        this.data = data;
    }

    public void setData(ArrayList<String> data) {
        this.data = data;
    }

    @Override
    public int getCount() {
        return data.size();
    }

    @Override
    public Object getItem(int position) {
        return data.get(position);
    }

    @Override
    public long getItemId(int position) {
        return Long.valueOf(position);
    }

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

        if (view == null) {
            view = (CheckableRelativeLayout) layoutInflater.inflate(
                    R.layout.checkable_relative_layout, null);
        }

        TextView number = (TextView) view.findViewById(R.id.number);
        TextView name = (TextView) view.findViewById(R.id.name);

        number.setText(Integer.toString(position));
        name.setText(data.get(position));

        return view;
    }

}

每个项目的视图都是膨胀的CheckableRelativeLayout

public class CheckableRelativeLayout extends RelativeLayout implements
        Checkable {

    private static final int[] CheckedStateSet = { android.R.attr.state_checked };
    private boolean isChecked = false;

    public CheckableRelativeLayout(Context context) {
        super(context);
    }

    public CheckableRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CheckableRelativeLayout(Context context, AttributeSet attrs,
            int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public boolean isChecked() {
        return isChecked;
    }

    @Override
    public void setChecked(boolean checked) {
        isChecked = checked;
        refreshDrawableState();
    }

    @Override
    public void toggle() {
        isChecked = !isChecked;
        refreshDrawableState();
    }

    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
        if (isChecked()) {
            mergeDrawableStates(drawableState, CheckedStateSet);
        }
        return drawableState;
    }

}

UI 中的选择表示在 a 中指定selection.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/gradient_bg_checked" android:state_checked="true"/>
    <item android:drawable="@drawable/gradient_bg"/>
</selector>

主要活动如下:

public class MultipleCheckList extends Activity {

    private ListView listView;
    private ListAdapter adapter;
    private SparseArray<ArrayList<String>> data = new SparseArray<ArrayList<String>>();

    private class ButtonClickListner implements OnClickListener {
        @Override
        public void onClick(View buttonView) {
            Integer tag = (Integer) buttonView.getTag();
            adapter.setData(data.get(tag));
            adapter.notifyDataSetChanged();
            listView.refreshDrawableState();
            // How to save user's selections?
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiple_check);

        Button button1 = (Button) findViewById(R.id.button_1);
        Button button2 = (Button) findViewById(R.id.button_2);
        button1.setTag(1);
        button2.setTag(2);
        button1.setOnClickListener(new ButtonClickListner());
        button2.setOnClickListener(new ButtonClickListner());
        button1.setText("First category");
        button2.setText("Second category");

        ArrayList<String> data1 = new ArrayList<String>();
        data1.add("John");
        data1.add("Bob");
        data1.add("Ted");

        ArrayList<String> data2 = new ArrayList<String>();
        data2.add("Summer");
        data2.add("Winter");
        data2.add("Spring");

        data.append(1, data1);
        data.append(2, data2);

        adapter = new ListAdapter(this, data1);

        listView = (ListView) findViewById(R.id.list);
        listView.setAdapter(adapter);
        listView.setItemsCanFocus(false);
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    }
}

我的问题是,保存用户选择的最佳方式是什么?

我尝试将ListAdapter每个类别的实例存储在 a 中HashMap,并通过调用setAdapter()for each onClick()in来重用它ButtonClickListner。不仅它似乎不起作用(选择的状态没有恢复),而且由于不必要的垃圾收集,它也不是很有效。

然后我尝试使用setData(),notifyDataSetChanged()refreshDrawableState()调用(如上面的代码)并将它们与一些组合HashMap用于选择存储。问题是,即使refreshDrawableState()正在调用,所有选择也会在类别之间共享:

在此处输入图像描述 在此处输入图像描述

我也看过Parcelable,但这似乎仅对跨活动资源交换有用。也许分别膨胀每个列表可以被视为一个好的解决方案?但是那么效率呢?setData()方法可能会更经济。

谢谢您的帮助。

4

3 回答 3

0

我终于解决了我的问题。

首先,我在ListAdapter类中添加了新方法。restoreCheckStatesIfNeeded()尤其是:

private SparseArray<SparseBooleanArray> setsCheckStates = new SparseArray<SparseBooleanArray>();

private void restoreCheckStatesIfNeeded(ListView listView, int position) {
    if (getCheckStates() == null) {
        setsCheckStates.put(currentCollection, new SparseBooleanArray());
    }

    if (stillSwitchingCollection) {
        Boolean itemLatestState = getCheckStates().get(position, false);
        listView.setItemChecked(position, itemLatestState);
    }

    if (position == getCount() - 1) {
        stillSwitchingCollection = false;
    }
}

public void setCurrentCollection(int currentCollection) {
    this.currentCollection = currentCollection;
    stillSwitchingCollection = true;
}

public SparseBooleanArray getCheckStates() {
    return setsCheckStates.get(currentCollection);
}

stillSwitchingCollectionfield 表示我们是否刚刚更改了一个集合,并且它的列表项仍在绘制中。它可以帮助我们判断是否需要恢复之前列表的项目检查状态。

另外,重要的是要注意,我们不能将列表项的检查状态保存在 agetView()中,否则会发生许多奇怪的事情(例如,即使我们没有单击某些项目也会被选中等)。这是因为在getView()通话后仍有一些正在进行的选择设置ListView.setupChild()

因此,更好的选择是将检查状态保存在ButtonClickListner.onClick()

for (int i = 0; i < listView.getChildCount(); i++) {
    CheckableRelativeLayout listItem = (CheckableRelativeLayout) listView.getChildAt(i);
    adapter.getCheckStates().put(i, listItem.isChecked());
}

幸运的是,这种方法解决了这个问题。

于 2013-05-31T11:51:15.963 回答
0

在您的 ListAdapter() 中,您可以存储一个自定义类,该类存储名称以及是否选择了项目。

public class ListAdapter extends BaseAdapter {
    private static LayoutInflater layoutInflater = null;
    private ArrayList<Object E> data; // Here E will your customize Class.
    ...
}
于 2013-05-28T21:34:16.353 回答
0

我不确定你的目标是什么。

ListAdapter 的一个实例:

getView您会同意,我上面提到的答案和下面给出的一个小改动不会有任何问题:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
      if(data.selected) {
             // Make item selected.
       }
}

ListAdapter 的多个实例:

应该使用不同的指针访问它们。

于 2013-05-28T23:18:04.357 回答