Недавно я писал, что делаю себе поделку, которая будет трекать выполнение моих полезных привычек. И вчера был очередной баттл за ListView - я хотел чтобы он работал по-моему, а Androd Framework не поддавался. Ну накрутили там... Но через пару часов задача была решена.
Что я хотел? Банальный ListVew с элементами которые имеют чекбоксик. При клике на чекбоксик он выделяется и через секунду юлемент удаляется. Если я за эту секунду снял чекбоксик (передумал), то удаления не произойдет. Так же можно повыделять/поснимать за это время еще и другие чекбоксики, секунда до удаления отсчитывается от последнего клика.
Как любитель code reuse я не мог не выделить эту фичу в отдельный мегастатический монстр :) Я в последнее время долго кодил на JavaScript так что особо не судите за интерфейсность.
package apofig.com.myway; import android.os.Handler; import android.widget.ListView; import java.util.LinkedList; import java.util.List; public class ListViewCustomizer { interface WhenDelete { void doit(List<String> removed); } interface OnClick { void click(int position); } public static OnClick setupListViewThatHideChecked(final ListView listView, final List<String> list, final WhenDelete doit) { final List<Integer> lastPositions = new LinkedList<Integer>(); // будем хранить тут те позиции по которым кликнули // эта штука позволит нам сделать отложенный во времени вызов кода final Handler handler = new Handler(); // вот этот код будет вызываться через секунду после того как был сделан последний клик final Runnable runnable = new Runnable() { public void run() { // если ничего не выбрали, то и удалять нечего if (lastPositions.isEmpty()) return; // тут храним имена удаленных элементов, его вернем клиенту List<String> items = new LinkedList<String>(); while (!lastPositions.isEmpty()) { // по все удаленным Integer index = lastPositions.remove(0); // первый элемент списка // если за это время его успели анчекнуть, пропускаем if (!listView.isItemChecked(index)) continue; items.add(list.get(index)); // сохраняем его list.remove(index.intValue()); // удаляем // нам после удаления элемента надо подтянуть и чекбоксики, потому как они хранятся в другом месте for (int j = index.intValue(); j < list.size(); j++) { listView.setItemChecked(j, listView.isItemChecked(j + 1)); } // ну и пересчитать индексы, потому что все после удаленного стали на 1 меньше for (int i = 0; i < lastPositions.size(); i++) { Integer removed = lastPositions.remove(i); if (removed > index) { removed--; } lastPositions.add(i, removed); } } if (items.isEmpty()) return; // чеи в правду ничего не удалили? выходим // выполняем клиентский код если надо if (doit != null) { doit.doit(items); } // чистим lastPositions.clear(); } }; // этот обработчик следит за тем, чтобы запустить второй, через секунду после того как отыграет последний клик final int[] count = {0}; final Runnable runnable2 = new Runnable() { public void run() { count[0]--; if (count[0] == 0) { handler.postDelayed(runnable, 1000); } } }; // а этот код должен дернуть клиент, каждый раз когда кликнули по элементу и указать position return new OnClick() { @Override public void click(int position) { // странно, но тут не надо инвертировать, потому как значение isItemChecked( - такое как надо, но // оно не применится к чекбоксику, если не вызвать еще и setItemChecked( boolean checked = listView.isItemChecked(position); listView.setItemChecked(position, checked); // добавляем в скписок выделенных только чекнутые, анчекнутые удаляем if (checked) { lastPositions.add(Integer.valueOf(position)); } else { lastPositions.remove(Integer.valueOf(position)); } count[0]++; // Спасибо http://stackoverflow.com/q/8177830 handler.postDelayed(runnable2, 1000); } }; } }
А используется он так
public void onCreate(Bundle savedInstanceState) { // бла бла бла super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ListView listView = (ListView) findViewById(R.id.lvMain); // исходные данные и адаптер List<String> list = new LinkedList<String>(Arrays.asList("one", "two", "three", "four", "five")); ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_checked, list); listView.setAdapter(adapter); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // можно выделять несколько // настраиваем компоненту final ListViewCustomizer.OnClick onClick = ListViewCustomizer.setupListViewThatHideChecked(listView, list, new ListViewCustomizer.WhenDelete() { @Override public void doit(List<String> removed) { // перерисовываем список в соответствии с изменившимися данными adapter.notifyDataSetChanged(); // выводим на экранчик сообщение с именами удаленных элементов Toast.makeText(MainActivity.this, splitWith("\n", removed), Toast.LENGTH_SHORT).show(); } }); // передаем импульс онклика listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { onClick.click(position); } }); } // метод превращения списка в строку разделенных разделителем private static String splitWith(String separator, List<String> removed) { String info = ""; for (String m : removed) { info += m + separator; } info = info.substring(0, info.length() - separator.length()); return info; }Вот еще xml-ки
<!-- Спасибо http://dajver.blogspot.co.uk/2013/09/listview-edittext.html --> <!-- Спасибо http://startandroid.ru/en/uroki/vse-uroki-spiskom/15-urok-6-vidy-layouts-kljuchevye-otlichija-i-svojstva --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <ListView android:id="@+id/lvMain" android:layout_width="match_parent" android:layout_height="wrap_content"> </ListView> </LinearLayout>Может кому-то пригодится... Если надо проект в zip виде, пиши в комменты - выложу.
Комментариев нет:
Отправить комментарий