【问题标题】:How can you implement multi-selection and Contextual ActionMode in ActionBarSherlock?如何在 ActionBarSherlock 中实现多选和上下文 ActionMode?
【发布时间】:2013-02-06 19:50:08
【问题描述】:

AdapterView不提供MultiChoiceModeListener,如何用ActionBarSherlock在AdapterView上实现多选?

这就是它的样子

你怎么能做到这一点?

【问题讨论】:

标签: android android-actionbar actionbarsherlock contextual-action-bar


【解决方案1】:

这就是我所做的。

编辑: 一年多以来,我发现上一个答案有很多无用的代码(呜呜),CAB 的事情可以用更少的努力和更清晰的代码来实现,所以我花了一些时间更新它

LibraryFragment ListView 应该使用选择模式“none”来定义

<ListView
    android:id="@android:id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:choiceMode="none"/>

列表项应该有一个 ?attr/activatedBackgroundIndicator 前景,以便在 list.setItemChecked(pos, true) 上自动绘制突出显示的半透明叠加层

list_item_library.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:foreground="?attr/activatedBackgroundIndicator"
    android:paddingBottom="5dp"
    android:paddingTop="5dp" >

....

ListFragment

import android.support.v4.app.DialogFragment;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;

public final class LibraryFragment
        extends SherlockListFragment
{

    private MyListAdapter adapter;
    private ListView list;

    // if ActoinMode is null - assume we are in normal mode
    private ActionMode actionMode;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState)
    {
        View v = inflater.inflate(R.layout.fragment_library, null);
        this.list = (ListView) v.findViewById(android.R.id.list);
        this.initListView();
        return v;
    }

    @Override
    public void onPause()
    {
        super.onPause();
        if (this.actionMode != null) {
            this.actionMode.finish();
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        updateData();
    }

    // update ListView
    protected void updateData()
    {
        if (adapter == null) {
            return;
        }
        adapter.clear();
        // my kinda stuff :)
        File[] items = scan();
        if (items != null) {
            adapter.updateData(items);
            if (actionMode != null) {
                actionMode.invalidate();
            }
        }
        // if empty - finish action mode.
        if (actionMode != null && (files == null || files.length == 0)) {
            actionMode.finish();
        }
    }

    private void initListView()
    {
        this.adapter = new MyAdapter(getActivity());
        this.list.setAdapter(adapter);
        this.list.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener()
        {

            @Override
            public boolean onItemLongClick(AdapterView<?> arg0,
                    View arg1, int arg2, long arg3)
            {
                if (actionMode != null) {
                    // if already in action mode - do nothing
                    return false;
                }
                // set checked selected item and enter multi selection mode
                list.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE);
                list.setItemChecked(arg2, true);

                getSherlockActivity().startActionMode(
                        new ActionModeCallback());
                return true;
            }
        });
        this.list.setOnItemClickListener(new AdapterView.OnItemClickListener()
        {
            @Override
            public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
                    long arg3)
            {
                if (actionMode != null) {
                    // the items are auomatically "checked" becaise we've set AbsListView.CHOICE_MODE_MULTIPLE before
                    // starting action mode, so the only thing we have to care about is invalidating the actionmode
                    actionMode.invalidate(); //invalidate title and menus.
                } else {
                    // do whatever you should on item click
                }
            }
        });
    }


    // all our ActionMode stuff here :)
    private final class ActionModeCallback
            implements ActionMode.Callback
    {

        // " selected" string resource to update ActionBar text
        private String selected = getActivity().getString(
                R.string.library_selected);

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu)
        {
            actionMode = mode;
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu)
        {
            // remove previous items
            menu.clear();
            final int checked = list.getCheckedItemCount();
            // update title with number of checked items
            mode.setTitle(checked + this.selected);
            switch (checked) {
            case 0:
                // if nothing checked - exit action mode
                mode.finish();
                return true;
            case 1:
                // all items - rename + delete
                getSherlockActivity().getSupportMenuInflater().inflate(
                        R.menu.library_context, menu);
                return true;
            default:
                getSherlockActivity().getSupportMenuInflater().inflate(
                        R.menu.library_context, menu);
                // remove rename option - because we have more than one selected
                menu.removeItem(R.id.library_context_rename);
                return true;
            }
        }

        @Override
        public boolean onActionItemClicked(ActionMode mode,
                com.actionbarsherlock.view.MenuItem item)
        {
            SparseBooleanArray checked;
            switch (item.getItemId()) {
            case R.id.library_context_rename:
                // the rename action is present only when only one item is selected. 
                // so when the first checked item found, show the dialog and break
                checked = list.getCheckedItemPositions();
                for (int i = 0; i < checked.size(); i++) {
                    final int index = checked.keyAt(i);
                    if (checked.get(index)) {
                        final DialogFragment d = RenameDialog.instantiate(adapter.getItem(index).getFile(), LibraryFragment.this);
                        d.show(getActivity().getSupportFragmentManager(), "dialog");
                        break;
                    }
                }
                return true;

            case R.id.library_context_delete:
                // delete every checked item
                checked = list.getCheckedItemPositions();
                for (int i = 0; i < checked.size(); i++) {
                    final int index = checked.keyAt(i);
                    if (checked.get(index)) {
                        adapter.getItem(index).getFile().delete();
                    }
                }
                updateData();
                return true;
            default:
                return false;
            }
        }

        @Override
        public void onDestroyActionMode(ActionMode mode)
        {
            list.clearChoices();

            //workaround for some items not being unchecked.
            //see http://stackoverflow.com/a/10542628/1366471
            for (int i = 0; i < list.getChildCount(); i++) {
                (list.getChildAt(i).getBackground()).setState(new int[] { 0 });
            }

            list.setChoiceMode(AbsListView.CHOICE_MODE_NONE);
            actionMode = null;
        }

    }

【讨论】:

  • 感谢分享。我正在寻找这个!
  • 感谢这篇精彩的帖子,但我有一个关于方向更改的问题。比方说,我在actionmode上选择了几个项目,然后旋转设备,我可以恢复actionbar,并设置选择的项目。但是,直到我将列表从屏幕上滚动并返回,突出显示才会显示。有什么想法吗?
  • @triston 你如何恢复状态?确保在适配器上调用 setChecked()。在这种情况下,它应该可以工作。但如果这就是你正在做的事情并且它不起作用,我恐怕无法帮助你,因为我没有足够的空闲时间来测试这些东西。
  • 我使用了一种相当简单的方法,将&lt;style name="activated" parent="android:Theme.Holo"&gt; &lt;item name="android:background"&gt;?android:attr/activatedBackgroundIndicator&lt;/item&gt; &lt;/style&gt; 设置为我的列表项样式。
  • 以更简洁的方式更新了答案。现在不需要自定义选择器或适配器。
【解决方案2】:

您的解决方案是此线程的最佳和最简单的解决方案。但是 getView() 中存在一个小问题 - 请参考我上面的 cmets。

int version = android.os.Build.VERSION.SDK_INT;     
if(version < 11){
    if (checkedItems.contains(Integer.valueOf(position))) {
        convertView.getBackground().setState(
                new int[] { android.R.attr.state_checked });
    } else {
        convertView.getBackground().setState(
                new int[] { -android.R.attr.state_checked });
    }
}else{

    if (checkedItems.contains(Integer.valueOf(position))) {
        convertView.setActivated(true);
    } else {
        convertView.setActivated(false);        
    }
}

这将为您提供从 API8 到 API18 的全面支持

【讨论】:

  • setActivated 仅从 API 级别 11 开始可用。如果您使用 API 11,则不需要 ActionBarSherlock,我的解决方案很糟糕,因为从 API 11 开始有更简单的方法。developer.android.com/guide/topics/ui/menus.html#CAB
  • 我的项目设置了最低API8,所以我必须同时使用。我也会修改我上面的建议。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多