Как вы можете реализовать множественный выбор и контекстный режим действия в ActionBarSherlock?

Как мне реализовать множественный выбор в AdapterView с помощью ActionBarSherlock, поскольку он не предоставляет MultiChoiceModeListener?

вот как это выглядит

Режим контекстного действия

Как вы можете это сделать?


person Yaroslav Mytkalyk    schedule 06.02.2013    source источник
comment
Для этого есть полное решение. github.com/deniskratinov/selectablelistview надеюсь, что это поможет вам...   -  person chinnuu    schedule 12.02.2014
comment
я ищу это для recyclerview, пожалуйста, помогите мне   -  person Harsha    schedule 15.10.2016


Ответы (2)


Итак, вот что я сделал.

Правка: прошло больше года с тех пор, как я обнаружил, что в предыдущем ответе было много бесполезного кода (упс), а 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" >

....

Фрагмент списка

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;
        }

    }
person Yaroslav Mytkalyk    schedule 06.02.2013
comment
Спасибо, что поделился. Я искал именно это! - person h4ck3d; 06.02.2013
comment
спасибо за этот отличный пост, но у меня проблема с изменением ориентации. Скажем, я выбрал несколько элементов в режиме действий, затем повернул устройство, я могу восстановить панель действий и установить выбранные элементы. Но подсветка не появляется, пока я не прокручиваю список с экрана и обратно, подсветка показывалась. Любая идея? - person triston; 29.07.2013
comment
@triston как ты восстанавливаешь состояние? Убедитесь, что вы вызываете setChecked() для адаптера. В этом случае он должен работать. Но если это то, что вы делали, и это не работает, боюсь, я не могу вам помочь, так как у меня недостаточно свободного времени, чтобы проверить эти вещи. - person Yaroslav Mytkalyk; 29.07.2013
comment
эй DD, спасибо, что ответили мне, очень ценю за вашу доброту. И да, я вызывал setChecked() после поворота, элементы адаптера проверяются (с помощью setChecked), я вижу из LogCat. Когда я прокручиваю список до listItems (у меня большой заголовок списка, поэтому мне приходится прокручивать вниз до элементов списка после каждого поворота), выделение не отображается, затем я прокручиваю обратно вверх, затем прокручиваю вниз до элементы списка, выделение показало. - person triston; 30.07.2013
comment
Этот ответ является новым 42. Большое спасибо за предоставление этого примера кода. - person Phillip; 24.08.2013
comment
Я использовал довольно простой способ, установив <style name="activated" parent="android:Theme.Holo"> <item name="android:background">?android:attr/activatedBackgroundIndicator</item> </style> в качестве стиля элемента списка. - person Sufian; 14.03.2014
comment
@Sufian Я недавно думал об этом варианте. Когда я писал этот ответ, я не знал об этом. В ближайшее время подумаю об обновлении. - person Yaroslav Mytkalyk; 14.03.2014
comment
Обновил ответ более чистым способом. Теперь не нужны специальные селекторы или адаптеры. - person Yaroslav Mytkalyk; 14.03.2014
comment
Ваше решение идеально, но проблема, с которой я столкнулся, заключается в том, что я не могу выделить выбранное изображение и использовать эту строку: android:foreground=?attr/activatedBackgroundIndicator дает мне исключение, не найденное ресурсом, и я уже добавил библиотеку actionbarsherlock - person Satyen Udeshi; 30.04.2014
comment
@SatyenUdeshi ActionBarSherlock имеет эти атрибуты в своих темах. Вы уверены, что тема вашего приложения расширяет тему Шерлока? В любом случае, вы можете попробовать использовать drawable вместо @drawable/abs__activated_background_holo_dark или @drawable/abs__activated_background_holo_light. Кроме того, теперь есть библиотека Google Appcompat-v7 с ActionBar, в которой меньше ошибок, и вы можете использовать ее вместо Sherlock. - person Yaroslav Mytkalyk; 30.04.2014
comment
Спасибо за ваш ответ. Я поддерживаю API 8+, что является проблемой для создания списка множественного выбора с помощью CAB. Я использую библиотеку поддержки v7 вместо ActionBarSherlock, и с небольшими изменениями в вашем коде она работает безупречно. - person Sandak; 12.07.2014
comment
У меня также были проблемы с выделением выбранного элемента, хотя я не использую ActionBarSherlock. Решение для меня состояло в том, чтобы создать selector drawable, содержащий этот <item android:state_activated="true" android:drawable="@drawable/my_item_selected"/>. Затем используйте его в качестве фона элемента списка, например android:background="@drawable/my_list_selector" - person Robert; 22.05.2015
comment
@ Роберт, да, это то, что делает ?attr/activatedBackgroundIndicator, который я упомянул как передний план FrameLayout, что подходит для темы по умолчанию. Если вы хотите переопределить свои собственные цвета, вы должны создать свой собственный, поскольку он имеет структуру, которую вы упомянули. - person Yaroslav Mytkalyk; 22.05.2015

ваше решение - лучшее и самое простое решение для этой темы. Но в getView() есть небольшая проблема - вернитесь к моим комментариям выше.

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.

person triston    schedule 29.07.2013
comment
setActivated доступен только начиная с уровня API 11. Если вы используете API 11, в ActionBarSherlock нет необходимости, и мое решение плохое, поскольку есть гораздо более простой способ сделать это, начиная с API 11. developer.android.com/guide/topics/ui/menus.html#CAB - person Yaroslav Mytkalyk; 30.07.2013
comment
В моем проекте установлен минимальный API8, поэтому я должен использовать оба. Я также изменю свое предложение выше. Спасибо. - person triston; 01.08.2013