Освой программирование играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Стандартный список имеет возможность создания множественного выбора. Но мы напишем свой вариант, который может пригодиться для решения других задач.
В канун Рождества самые известные кошки интернета снялись в клипе на песню 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<>();