четвер, 23 серпня 2012 р.

Пишем свой SimpleCursorAdapter

Всем привет! Эта статтья будет о прекрасном адаптере под названием SimpleCursorAdapter который помогает связать данные из бд со списком ListView. Эта статтья является как бы продолжением предыдущей ("Пишем свой SQLiteOpenHelper"), так как с той бд мы связываем наш адаптер и "прикручиваем" его к списку.
Для начала напомню что у нас есть объект-контейнер в который "кладутся" данные из бд:

public class Shop {
 
 private int id;
 private String title;
 private double price;
 
 public Shop() {
 
 }
 
 public Shop(String _title, double _price) {
  title = _title;
  price = _price;
 }
 
 public Shop(int _id, String _title, double _price) {
  id = _id;
  title = _title;
  price = _price;
 }
 
 public void setId(int _id) {
  id = _id;
 }
 
 public void setTitle(String _title) {
  title = _title;
 }
 
 public void setPrice(double _price) {
  price = _price;
 }
 
 public int getId() { return id; }
 
 public String getTitle() { return title; }
 
 public double getPrice() { return price; }
}
 
Определим для нашего списка главный лейаут:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@android:id/list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#2898FA">
 
</ListView>

Так же определим ещё один лейаут, он будет ячейкой списка. В ячейке будет два TextView, один для названия товара, другой для цены:

<?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" >
 
    <TextView
        android:id="@+id/titleTV"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:padding="8dp"
        android:background="#32D1D6"
        android:textColor="#000000" />
 
    <TextView
        android:id="@+id/priceTV"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:padding="8dp"
        android:background="#32D1D6"
        android:textColor="#000000" />
 
</LinearLayout>

Всё, марафет навели. Теперь будем разбираться что же всё таки такое SimpleCursorAdapter. Что нам нужно? Нужно определить класс (назовём его MyCursorAdapter) и расширить его SimpleCursorAdapter'ом. В SimpleCursorAdapter'e есть обязательный конструктор, который выглядит так:

 public MyCursorAdapter(Context _context, int _layout, Cursor _cursor, String[] _from, int[] _to) {
  super(_context, _layout, _cursor, _from, _to);
  layout = _layout;
 }
Не важно какой конструктор мы напишем, главное вызвать наследуемый конструктор в который следует передать: 1) текущий контекст; 2) лейаут ячейки; 3) курсор который указывает на данные из бд; 4) названия столбцов с которых следует взять данные; 5) view которые следует заполнить. Немножко не ясно, но потом когда нарисуется вся картина всё прояснится.
Значит наследуя класс SimpleCursorAdapter мы должны реализовать миниммум два метода. Первый называется newView:

@Override
public View newView(Context _context, Cursor _cursor, ViewGroup parent) {
 LayoutInflater inflater = (LayoutInflater) _context.getSystemService(_context.LAYOUT_INFLATER_SERVICE);
 View view = inflater.inflate(layout, parent, false);
 return view;
}

Этот метод создаёт view-ячейку. В качестве параметров ему передаём текущий контекст, курсор и элементы-родителя. В самом методе нам следует определить view ячейки, что мы и делаем с помощью LayoutInflater. На перёд забегая скажу что методы SimpleCursorAdapter'a не вызываются явно пользователем, они вызываются адаптером автоматически.
Второй метод будет "вешать" данные в ячейку:

@Override
public void bindView(View view, Context _context, Cursor _cursor) {
 String title = _cursor.getString(_cursor.getColumnIndex(AppData.COLUMN_TITLE));
 Double price = Double.parseDouble(_cursor.getString(_cursor.getColumnIndex(AppData.COLUMN_PRICE)));
 TextView titleTV = (TextView) view.findViewById(R.id.titleTV);
 TextView priceTV = (TextView) view.findViewById(R.id.priceTV);
 titleTV.setText(title);
 priceTV.setText(price.toString() + "$");
}

В качестве параметров ему передаётся view которую мы создали в предыдущем методе, текущий контекст и курсор. В первую очередь достаём необходимые данные из курсора, далее вставляем их в наши две TextView. Всё, как видим ничего сложного! Полностью класс MyCursorAdapter будет выглядеть так:

import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
 
public class MyCursorAdapter extends SimpleCursorAdapter {
 
 private int layout;
 
 public MyCursorAdapter(Context _context, int _layout, Cursor _cursor, String[] _from, int[] _to) {
  super(_context, _layout, _cursor, _from, _to);
  layout = _layout;
 }
 
 //связывает данные с view на которые указывает курсор
 @Override
 public void bindView(View view, Context _context, Cursor _cursor) {
  String title = _cursor.getString(_cursor.getColumnIndex(AppData.COLUMN_TITLE));
  Double price = Double.parseDouble(_cursor.getString(_cursor.getColumnIndex(AppData.COLUMN_PRICE)));
  TextView titleTV = (TextView) view.findViewById(R.id.titleTV);
  TextView priceTV = (TextView) view.findViewById(R.id.priceTV);
  titleTV.setText(title);
  priceTV.setText(price.toString() + "$");
 }
 
 //сoздаёт нвоую view для хранения данных на которую указывает курсор
 @Override
 public View newView(Context _context, Cursor _cursor, ViewGroup parent) {
  LayoutInflater inflater = (LayoutInflater) _context.getSystemService(_context.LAYOUT_INFLATER_SERVICE);
  View view = inflater.inflate(layout, parent, false);
  return view;
 }
 
}

Теперь переходим в главное активити которое наследуется от ListActivity. Там нам следует провести инициализацию.

    private void init() {
     String[] from = { AppData.COLUMN_TITLE, AppData.COLUMN_PRICE };
     int[] to = { R.id.titleTV, R.id.priceTV };
     db = new DBHelper(this, AppData.DATABASE_NAME, null, 1);
     Cursor cursor = db.getAllCursor();
     adapter = new MyCursorAdapter(this, R.layout.item_listview, cursor, from, to);
     getListView().setAdapter(adapter);
     getListView().setOnItemClickListener(new OnItemClickListener() {
 
   public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    Log.i("MyTag", "position = " + position + " && id = " + id);
 
   }
  });
    }

В первую очередь нам нужно подготовить данные для конструктора адаптера: from - необходимые столбцы из бд; to - эелементы куда вставлять данные взятые из from; db - наш DBHelper из предыдущего урока; cursor - получаем данные таблицы из бд. Далее вызываем конструктор адаптера и присваиваем своему списку адаптер. Так же создаём обработчик onItemClick для нашего списка, где в лог для наглядности будем выводить позицию в списке и айди из бд.
Скриншот программы:


На первом скриншоте видим что данные успешно и красиво загрузились. На втором же скриншоте продемонстрирован лог в который выводятся данные при нажатии на ячейки. Как видим разницу между position и id. Position - позиция нажатой ячейки в списке (начинается снуля). Id - айдишник из бд. Это наверное главный плюс этой всей затеей, так как при клике по ячейке списка мы уже знаем айди данной записи в бд и уже с этой записью можем делать всё что захочем.
Проект можно скачать по ссылкам:

1 коментар: