Освой программирование играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Список с множественным выбором и собственным адаптером ArrayAdapter

Стандартный список имеет возможность создания множественного выбора. Но мы напишем свой вариант, который может пригодиться для решения других задач.

В канун Рождества самые известные кошки интернета снялись в клипе на песню Hard to be a Cat at Christmas (Трудно быть котом в Рождество). В съёмках ролика приняли участие кошка по имени Соус Тартар, известная в интернете как Хмурый кот (Grumpy Cat), Полковник Мяу (Colonel Meow), слепой кот Оскар (Oskar the Blind Cat), косоглазая кошка Нала и кот-хипстер Гамильтон. В клипе коты смотрят на снегопад за окном, распаковывают рождественские подарки, играют с елочными украшениями и лакомятся рождественским ужином.

Напишем пример выбора лучших котиков из этого клипа.

Макет основного экрана


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Ваш выбор"/>

    <ListView
        android:id="@+id/listView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

Создадим разметку для отдельного пункта списка (list_item.xml), которая будет состоять из изображения и флажка. В нашем случае все значки будут одинаковыми.


<?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="wrap_content"
              android:orientation="horizontal">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_gravity="start"
        android:src="@mipmap/ic_launcher"/>

    <CheckedTextView
        android:id="@+id/checkedTextView"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/listPreferredItemHeightSmall"
        android:layout_gravity="end"
        android:checkMark="?android:attr/listChoiceIndicatorMultiple"
        android:gravity="end|center_vertical"
        android:paddingStart="?android:attr/listPreferredItemPaddingLeft"
        android:textAppearance="?android:attr/textAppearanceListItemSmall"/>

</LinearLayout>

Подготовим небольшой массив из имён котов и передадим его адаптеру на основе ArrayAdapter. При нажатии на элементах списка флажок будет менять своё состояние на противоположное. Через кнопку мы можем получить информацию, какие флажки были выбраны.


package ru.alexanderklimov.multichoice;

import ...

public class MainActivity extends AppCompatActivity {

    private ArrayList<String> mCatNameList = new ArrayList<>();
    private MyArrayAdapter mArrayAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView listView = (ListView) findViewById(R.id.listView);

        initList();

        mArrayAdapter = new MyArrayAdapter(this, R.layout.list_item,
                android.R.id.text1, mCatNameList);

        listView.setAdapter(mArrayAdapter);
        listView.setOnItemClickListener(myOnItemClickListener);
    }

    public void onClick(View view) {
        String result = "";
        List<String> resultList = mArrayAdapter.getCheckedItems();
        for (int i = 0; i < resultList.size(); i++) {
            result += String.valueOf(resultList.get(i)) + "\n";
        }

        Toast.makeText(getApplicationContext(), result, Toast.LENGTH_LONG)
                .show();
    }

    private void initList() {
        mCatNameList.add("Grumpy Cat");
        mCatNameList.add("Colonel Meow");
        mCatNameList.add("Oskar the Blind Cat");
        mCatNameList.add("Nala Cat");
        mCatNameList.add("Hamilton the Hipster Cat");
    }

    AdapterView.OnItemClickListener myOnItemClickListener = new AdapterView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                                long id) {
            mArrayAdapter.toggleChecked(position);
        }
    };

    private class MyArrayAdapter extends ArrayAdapter<String> {

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

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

            for (int i = 0; i < objects.size(); i++) {
                mCheckedMap.put(i, false);
            }
        }

        void toggleChecked(int position) {
            if (mCheckedMap.get(position)) {
                mCheckedMap.put(position, false);
            } else {
                mCheckedMap.put(position, true);
            }

            notifyDataSetChanged();
        }

        public List<Integer> getCheckedItemPositions() {
            List<Integer> checkedItemPositions = new ArrayList<>();

            for (int i = 0; i < mCheckedMap.size(); i++) {
                if (mCheckedMap.get(i)) {
                    (checkedItemPositions).add(i);
                }
            }

            return checkedItemPositions;
        }

        List<String> getCheckedItems() {
            List<String> checkedItems = new ArrayList<>();

            for (int i = 0; i < mCheckedMap.size(); i++) {
                if (mCheckedMap.get(i)) {
                    (checkedItems).add(mCatNameList.get(i));
                }
            }

            return checkedItems;
        }

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

            if (row == null) {
                LayoutInflater inflater = getLayoutInflater();
                row = inflater.inflate(R.layout.list_item, parent, false);
            }

            CheckedTextView checkedTextView = (CheckedTextView) row
                    .findViewById(R.id.checkedTextView);
            checkedTextView.setText(mCatNameList.get(position));

            Boolean checked = mCheckedMap.get(position);
            if (checked != null) {
                checkedTextView.setChecked(checked);
            }

            return row;
        }
    }
}

Множественный выбор

В Android лучше использовать SparseBooleanArray вместо HashMap<Integer, Boolean>, так как предлагаемый вариант требует гораздо меньше ресурсов, что для больших списков можете стать важным фактором. Всё, что нужно сделать, это заменить одну строчку кода


private SparseBooleanArray mCheckedMap = new SparseBooleanArray();
// Не рекомендуется: private HashMap<Integer, Boolean> mCheckedMap = new HashMap<>();
Реклама