В этом уроке:

- формируем список с множественным выбором в диалоге

 

Урок будет аналогичен прошлому. Мы будем формировать в диалоге список с множественным выбором и определять какие элементы были выбраны (чекнуты). На этот раз мы сможем работать только с массивом и курсором. Дать диалогу адаптер не получится, нет соответствующего метода. Это особенности реализации.

 

Создадим проект:

Project name: P0641_AlertDialogItemsMulti
Build Target: Android 2.3.3
Application name: AlertDialogItemsMulti
Package name: ru.startandroid.develop.p0641alertdialogitemsmulti
Create Activity: MainActivity

 

В strings.xml пропишем тексты:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">AlertDialogItemsMulti</string>
    <string name="items">Items</string>
    <string name="cursor">Cursor</string>
    <string name="ok">OK</string>
</resources>

 

main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal">
    <Button
        android:id="@+id/btnItems"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/items"
        android:onClick="onclick">
    </Button>
    <Button
        android:id="@+id/btnCursor"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/cursor"
        android:onClick="onclick">
    </Button>
</LinearLayout>

 

DB.java:

package ru.startandroid.develop.p0641alertdialogitemsmulti;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class DB {
  
  private static final String DB_NAME = "mydb";
  private static final int DB_VERSION = 1;
  private static final String DB_TABLE = "mytab";
  
  public static final String COLUMN_ID = "_id";
  public static final String COLUMN_CHK = "checked";
  public static final String COLUMN_TXT = "txt";
  
  private static final String DB_CREATE = 
    "create table " + DB_TABLE + "(" +
      COLUMN_ID + " integer primary key, " +
      COLUMN_CHK + " integer, " +
      COLUMN_TXT + " text" +
    ");";
  
  private final Context mCtx;
  
  
  private DBHelper mDBHelper;
  private SQLiteDatabase mDB;
  
  public DB(Context ctx) {
    mCtx = ctx;
  }
  
  // открыть подключение
  public void open() {
    mDBHelper = new DBHelper(mCtx, DB_NAME, null, DB_VERSION);
    mDB = mDBHelper.getWritableDatabase();
  }
  
  // закрыть подключение
  public void close() {
    if (mDBHelper!=null) mDBHelper.close();
  }
  
  // получить все данные из таблицы DB_TABLE
  public Cursor getAllData() {
    return mDB.query(DB_TABLE, null, null, null, null, null, null);
  }
  
  //изменить запись в DB_TABLE
  public void changeRec(int pos, boolean isChecked) {
      ContentValues cv = new ContentValues();
      cv.put(COLUMN_CHK, (isChecked) ? 1 : 0);
      mDB.update(DB_TABLE, cv, COLUMN_ID + " = " + (pos + 1), null);
  }
  
  // класс по созданию и управлению БД
  private class DBHelper extends SQLiteOpenHelper {

    public DBHelper(Context context, String name, CursorFactory factory,
        int version) {
      super(context, name, factory, version);
    }

    // создаем и заполняем БД
    @Override
    public void onCreate(SQLiteDatabase db) {
      db.execSQL(DB_CREATE);
      
      ContentValues cv = new ContentValues();
      for (int i = 1; i < 5; i++) {
      cv.put(COLUMN_ID, i);
      cv.put(COLUMN_TXT, "sometext " + i);
      cv.put(COLUMN_CHK, 0);
      db.insert(DB_TABLE, null, cv);
      }
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
  }
}

 

Мы в таблице создаем числовое поле checked. По этому полю список диалога будет определять выделен элемент (значение = 1) или нет (0). Метод changeRec берет на вход позицию элемента в списке и boolean-значение, выделен элемент или нет, и меняет соответствующую запись в таблице.

 

MainActivity.java:

package ru.startandroid.develop.p0641alertdialogitemsmulti;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.View;
import android.widget.CursorAdapter;
import android.widget.ListView;

public class MainActivity extends Activity {

  final String LOG_TAG = "myLogs";
  
  final int DIALOG_ITEMS = 1;
  final int DIALOG_CURSOR = 3;
  DB db;
  Cursor cursor;

  String data[] = { "one", "two", "three", "four" };
  boolean chkd[] = { false, true, true, false };

  /** Called when the activity is first created. */
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // открываем подключение к БД
    db = new DB(this);
    db.open();
    cursor = db.getAllData();
    startManagingCursor(cursor);
  }

  public void onclick(View v) {
    switch (v.getId()) {
    case R.id.btnItems:
      showDialog(DIALOG_ITEMS);
      break;
    case R.id.btnCursor:
      showDialog(DIALOG_CURSOR);
      break;
    default:
      break;
    }
  }

  protected Dialog onCreateDialog(int id) {
    AlertDialog.Builder adb = new AlertDialog.Builder(this);
    switch (id) {
    // массив
    case DIALOG_ITEMS:
      adb.setTitle(R.string.items);
      adb.setMultiChoiceItems(data, chkd, myItemsMultiClickListener);
      break;
    // курсор
    case DIALOG_CURSOR:
      adb.setTitle(R.string.cursor);
      adb.setMultiChoiceItems(cursor, DB.COLUMN_CHK, DB.COLUMN_TXT, myCursorMultiClickListener);
      break;
    }
    adb.setPositiveButton(R.string.ok, myBtnClickListener);
    return adb.create();
  }

  // обработчик для списка массива
  OnMultiChoiceClickListener myItemsMultiClickListener = new OnMultiChoiceClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
      Log.d(LOG_TAG, "which = " + which + ", isChecked = " + isChecked);
    }
  };  
  
  // обработчик для списка курсора
  OnMultiChoiceClickListener myCursorMultiClickListener = new OnMultiChoiceClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
      ListView lv = ((AlertDialog) dialog).getListView();
      Log.d(LOG_TAG, "which = " + which + ", isChecked = " + isChecked);
      db.changeRec(which, isChecked);
      cursor.requery();
    }
  };
  
  // обработчик нажатия на кнопку
  OnClickListener myBtnClickListener = new OnClickListener() {
    public void onClick(DialogInterface dialog, int which) {
        SparseBooleanArray sbArray = ((AlertDialog)dialog).getListView().getCheckedItemPositions();
        for (int i = 0; i < sbArray.size(); i++) {
          int key = sbArray.keyAt(i);
          if (sbArray.get(key))
            Log.d("qwe", "checked: " + key);
        }    
    }
  };

  protected void onDestroy() {
    super.onDestroy();
    db.close();
  }

}

Разбираем код. Кроме массива строк data, нам нужен массив boolean[] chkd, который укажет диалогу, какие элементы списка надо будет сразу сделать выделенными. Будем выделять второй и третий.

 

В onCreate подключаемся к базе.

 

onclick – в зависимости от нажатой кнопки вызываем соответствующий диалог.

 

onCreateDialog – создаем вызываемый диалог, используя AlertDialog.Builder. Диалог может построить список, используя один из следующих объектов:

1) Массив строк. Используется метод setMultiChoiceItems. На вход подается массив строк, boolean-массив, определяющий выделенные элементов, и обработчик нажатия.

2) Курсор БД. Вызываем метод setMultiChoiceItems. Передаем туда курсор, имя поля выделения (данные о выделении элементов списка), имя поля с текстом (текст, который будет отображен в списке) и обработчик нажатия.

Кроме списка указываем только заголовок. В конце добавим кнопку ОК, создаем и возвращаем Dialog.

Для обоих способов создания используется методы с одинаковым названием setMultiChoiceItems, но с разными аргументами на вход.

 

myItemsMultiClickListener – обработчик нажатий на список, построенный из массива. Выводит в лог какой элемент был нажат и стал он выделенным или не выделенным. Реализует интерфейс OnMultiChoiceClickListener.

 

myCursorMultiClickListener - обработчик нажатий на список, построенный из курсора. Выводит в лог какой элемент был нажат и стал он выделенным или не выделенным. Также он соответствующим образом меняет данные в БД и обновляет курсор списка диалога. Т.к. если какой-то элемент выделили, мы должны в обработчике сбегать в БД, обновить соответствующую запись (поле checked) и обновить курсор. Ради интереса закомментируйте код этого обработчика – вы увидите, что галки в списке просто не ставятся.

 

myBtnClickListener – обработчик нажатия на кнопку. Получает из списка информацию о выделенных элементах и выводит ее в лог.

 

В onDestroy закрываем подключение к БД.

 

Все сохраним и запустим. Открываем диалог Items.

Видим, что галки проставились так, как мы указывали в массиве chkd. Если понажимать на пункты списка, лог показывает, какие изменения происходят.

which = 1, isChecked = false
which = 0, isChecked = true
which = 3, isChecked = true

 

Если есть необходимость, можно добавить в обработчик код, который обновлял бы массив chkd в соответствии с нажатиями. Индекс элемента и значение у нас есть.

Откроем диалог Cursor. Здесь ничего не выделено, т.к. в поле checked мы поместили нули.

 

Проставим галки,

 

лог это отобразит

which = 1, isChecked = true
which = 3, isChecked = true

Это работает только потому, что мы вручную обновляем БД и курсор. Повторюсь, попробуйте закоментить код обработчика myCursorMultiClickListener и элементы перестанут выделяться.

 

Я не стал реализовывать метод onPrepareDialog. В случае Items, там все просто, аналогично прошлому уроку используйте такой код:

((AlertDialog)dialog).getListView().setItemChecked(2, true);

В этом примере: 2 – это позиция элемента списка, а true - означает, что галка должна стоять. Если хотите снять галку, передавайте false.

 

В случае с курсором код будет аналогичен коду из обработчика myCursorMultiClickListener. Меняете запись в БД методом db.changeRec, обновляете курсор и передаете его адаптеру.

 

Нетривиальная такая получилась штука, не знаю пригодится кому или нет, но раз уж взялся за диалоги - решил расписать и это.

Также наверно имеет смысл сказать, что у методов setItems, setSingleChoiceItems, setMultiChoiceItems есть также реализация, использующая не напрямую массив, а ID массива строк из файла ресурсов.

 

 

На следующем уроке:

- используем свои View для построения диалога


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня




Language

Автор сайта

Дмитрий Виноградов

Подробнее можно посмотреть или почитать.

Никакие другие люди не имеют к этому сайту никакого отношения и просто занимаются плагиатом.

Социальные сети

 

В канале я публикую ссылки на интересные и полезные статьи по Android

В чате можно обсудить вопросы и проблемы, возникающие при разработке



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal