【问题标题】:Multiple selection in custom ListView with CAB使用 CAB 在自定义 ListView 中进行多项选择
【发布时间】:2012-05-15 10:00:20
【问题描述】:

在阅读并尝试了几天后,我放弃并寻求帮助。

我正在使用 ActionBarSherlock。 编辑>

我想要达到的目标: 每行都有一个自定义布局的 ListView,用户可以在其中选择多个列表项。 选定的列表项应具有不同的背景颜色。当至少选择了一项时,应显示上下文操作栏 (CAB)。 它应该看起来或多或少像 GMail 应用程序中的多个电子邮件选择。唯一的区别是,在 gmail 应用程序中,选择是通过单击一行的复选框来完成的,而我不想有一个复选框,但是无论用户点击哪里,都应该选择一行。

我尝试了什么:this tutorial 之后,使用带有一些逻辑的 Checkable 行布局在切换检查状态时更改背景颜色,除了我无法在 ListView 上注册像 OnItemClickListener 这样的单击侦听器以显示 CAB 之外,我得到了一切工作。为每一行 View 提供单击侦听器都没有帮助,因为这阻止了更改所选项目的背景颜色。 我也尝试像这样向 ListView 添加一个 MultiChoiceModeListener

    listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
    listView.setMultiChoiceModeListener(new MultiChoiceModeListener() { //.. });

结果相同,背景颜色没有变化。

我在寻找什么:如何做到这一点的提示或教程或示例代码。如果您需要一些代码 sn-ps 来帮助,请告诉我。

【问题讨论】:

  • 我已经编辑了我的答案,我的代码应该做你想做的(如果我理解你的要求)。
  • 教程链接已失效。你能解决它吗?

标签: android android-listview


【解决方案1】:

看看代码是否对你有帮助(它基本上是一个ListActivity,带有一个自定义适配器来保存检查项目的状态(+不同的背景)):

public class CABSelection extends ListActivity {

    private ArrayList<String> mItems = new ArrayList<String>();
    private SelectionAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        for (int i = 0; i < 24; i++) {
            mItems.add("Name" + i);
        }
        // R.layout.adapters_cabselection_row is a LinearLayout(with green
        // background(#99cc00)) that wraps an ImageView and a TextView
        mAdapter = new SelectionAdapter(this,
                R.layout.adapters_cabselection_row, R.id.the_text, mItems);
        setListAdapter(mAdapter);
        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        getListView().setMultiChoiceModeListener(new MultiChoiceModeListener() {

            private int nr = 0;

            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                MenuInflater inflater = getMenuInflater();
                inflater.inflate(R.menu.cabselection_menu, menu);
                return true;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                StringBuilder sb = new StringBuilder();
                Set<Integer> positions = mAdapter.getCurrentCheckedPosition();
                for (Integer pos : positions) {
                    sb.append(" " + pos + ",");
                }               
                switch (item.getItemId()) {
                case R.id.edit_entry:
                    Toast.makeText(CABSelection.this, "Edited entries: " + sb.toString(),
                            Toast.LENGTH_SHORT).show();
                    break;
                case R.id.delete_entry:
                    Toast.makeText(CABSelection.this, "Deleted entries : " + sb.toString(),
                            Toast.LENGTH_SHORT).show();
                    break;
                case R.id.finish_it:
                    nr = 0;
                    mAdapter.clearSelection();
                    Toast.makeText(CABSelection.this, "Finish the CAB!",
                            Toast.LENGTH_SHORT).show();
                    mode.finish();
                }
                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                nr = 0;
                mAdapter.clearSelection();
            }

            @Override
            public void onItemCheckedStateChanged(ActionMode mode,
                    int position, long id, boolean checked) {
                if (checked) {
                    nr++;
                    mAdapter.setNewSelection(position, checked);                    
                } else {
                    nr--;
                    mAdapter.removeSelection(position);                 
                }
                mode.setTitle(nr + " rows selected!");

            }

        });
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        l.setItemChecked(position, !mAdapter.isPositionChecked(position));
    }

    private class SelectionAdapter extends ArrayAdapter<String> {

        private HashMap<Integer, Boolean> mSelection = new HashMap<Integer, Boolean>();

        public SelectionAdapter(Context context, int resource,
                int textViewResourceId, List<String> objects) {
            super(context, resource, textViewResourceId, objects);
        }

        public void setNewSelection(int position, boolean value) {
            mSelection.put(position, value);
            notifyDataSetChanged();
        }

        public boolean isPositionChecked(int position) {
            Boolean result = mSelection.get(position);
            return result == null ? false : result;
        }

        public Set<Integer> getCurrentCheckedPosition() {
            return mSelection.keySet();
        }

        public void removeSelection(int position) {
            mSelection.remove(position);
            notifyDataSetChanged();
        }

        public void clearSelection() {
            mSelection = new HashMap<Integer, Boolean>();
            notifyDataSetChanged();
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = super.getView(position, convertView, parent);//let the adapter handle setting up the row views
            v.setBackgroundColor(Color.parseColor("#99cc00")); //default color
            if (mSelection.get(position) != null) {
                v.setBackgroundColor(Color.RED);// this is a selected position so make it red
            }
            return v;
        }

    }

}

R.layout.adapters_cabselection_row 是行的自定义布局(非常简单),背景为绿色:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#99cc00" >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher" />

    <TextView
        android:id="@+id/the_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#ffffff"
        android:textSize="17sp"
        android:textStyle="bold" />

</LinearLayout>

R.menu.cabselection_menu 是一个带有 3 个选项(编辑、删除、完成 CAB)的菜单文件,除了弹出 Toast 并显示有关所选行的消息外,它们什么也不做:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/edit_entry"
        android:icon="@android:drawable/ic_menu_edit"
        android:title="Edit!"/>
    <item
        android:id="@+id/delete_entry"
        android:icon="@android:drawable/ic_menu_delete"
        android:title="Delete!"/>
    <item
        android:id="@+id/finish_it"
        android:icon="@android:drawable/ic_menu_crop"
        android:title="Get me out!"/>

</menu>

【讨论】:

  • 我无法验证这是正确的答案,因为我忘了提到我正在使用 ActionBarSherlock。 MultiChoiceModeListener 仅从 API 级别 11 开始可用,并且 ABS 尚未实现等效项。但是您之前的回答似乎对我有所帮助。
  • 适用于 API = 11 及更高版本,但不支持 API
  • @Apurva A MultiChoiceModeListener 是基本ActionMode 之上的补充。如果您想在低于 11 的版本上实现该功能,您可以简单地实现您自己的 ActionMode 并将其与 ListView 事件连接起来。 Konsumierer 在下面的回答显示了您如何做到这一点。
  • @Luksprog 谢谢建议,我试试看。
  • 谢谢!像魅力一样工作!
【解决方案2】:

我认为最简单的方法是申请

android:background="android:attr/activatedBackgroundIndicator"

您将点击哪个布局。

这会在使用

选择时突出显示布局
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);

无论如何都为我工作

【讨论】:

  • 这是否适用于预蜂窝版本?
  • 编辑器只显示一个错误:“错误:错误:不允许字符串类型(在'背景',值为'android:attr/activatedBackgroundColor')。”
  • 因为正确的格式是?android:attr/activatedBackgroundIndicator 不,它不适用于预蜂窝版本,这仅适用于 API 11+
【解决方案3】:

使用 ActionBarSherlock,如果您想支持 API 级别 MultiChoiceModeListener 尚不可用。

一种解决方法是使用 onItemClickListener。

列表设置:

listView = (ListView) timeline.findViewById(android.R.id.list);
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listView.setItemsCanFocus(false);
listView.setAdapter(new ListAdapter(getActivity(), R.layout.cleaning_list_item, items));

ListFragment 或 ListActivity 的监听器:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    SparseBooleanArray checked = listView.getCheckedItemPositions();
    boolean hasCheckedElement = false;
    for (int i = 0; i < checked.size() && !hasCheckedElement; i++) {
        hasCheckedElement = checked.valueAt(i);
    }

    if (hasCheckedElement) {
        if (mMode == null) {
            mMode = ((SherlockFragmentActivity) getActivity()).startActionMode(new MyActionMode());
            mMode.invalidate();
        } else {
            mMode.invalidate();
        }
    } else {
        if (mMode != null) {
            mMode.finish();
        }
    }
}

其中 MyActionMode 是 ActionMode.Callback 的实现:

private final class MyActionMode implements ActionMode.Callback { /* ... */ }

【讨论】:

  • 你应该提到你使用ActionBarSherlock 我本可以给出相同的答案。即使在我的示例中(MultiChoceiListener 可用),您也可以实现自己的 ActionMode 实例来启动 CAB。
  • 是的,这是我的错。当我发布问题时,我认为这并不重要。但我很确定您的回答会对使用 API 级别 11 及更高级别的其他人有所帮助。
  • 在此代码示例中; “新的 ListAdapter”是什么意思? ListAdapter 是抽象的。那里的代码只是为了说明一点吗?签名看起来有点像 ArrayAdapter 的。是笔误吗?
  • ListAdapter 是我自己从 ArrayAdapter 扩展而来的类。只是处理自定义列表的常规方式。
  • 这对我不起作用,所以这是我的解决方法stackoverflow.com/questions/14737519/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多