I do a lot of apps that use SQLite and very often cannot design the database, because I have to use the Schema defined by the customer.
Obviously :-P the UX/UI designer does not care about the underlying data...she/he has to be cool!
So I, not infrequently, have to use a lot of INNER JOIN
, LEFT JOIN
with very big SQL queries (but thanks to the power of ContentProviders and Services this is not a big problem).
What bores me is the "cut/paste" (or static final) code for handling the different Cursor(s) from all the adapters .getItem(position)
.
So I was thinking of a way to keep things more clean, something like this.
Implementations of this interface will convert a Cursor into an object.
T
is the target type the input Cursor will be converted to.
public interface CursorHandler<T> {
T handle(Cursor cu)
}
Create a custom CursorAdapter that will use the above interface to automatically convert a cursor to a specified pojo
abstract
public class CursorHandlerAdapter<T> extends CursorAdapter {
private LayoutInflater mInflater = null;
private CursorHandler<T> mHandler = null;
public CursorHandlerAdapter(Context context, CursorHandler<T> handler) {
super(context, null, 0);
mHandler = handler;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public T getItem(int position) {
Cursor cu = (Cursor)super.getItem(position);
return ((cu == null) ? null : mHandler.handle(cu));
}
protected T getItem(Cursor cu) {
return mHandler.handle(cu);
}
protected LayoutInflater getLayoutInflater() { return mInflater; }
};
Suppose we have a pojo, let's call it Item:
public class Item implements Parcelable {
public long id;
public String code;
public String title;
public String description;
public String imageUrl;
public Item() { }
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
sb.append("id=");
sb.append(this.id);
sb.append(", ");
/* append the others fields */
return sb.toString();
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (obj == this) return true;
if (!(obj instanceof Item)) return false;
Item test = (Item)obj;
boolean result = true;
result = result && (this.code == test.code ||
(this.code != null && this.code.equals(test.code)));
return result;
}
@Override
public int hashCode() {
int result = 8;
result = 31 * result + (this.code == null ? 0 : this.code.hashCode());
return result;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeLong(this.id);
parcel.writeString(this.code);
/* write the others fields */
}
static final
public Creator<Item> CREATOR = new Creator<Item>() {
@Override
public Item createFromParcel(Parcel source) {
return new Item(source);
}
@Override
public Item[] newArray(int size) {
return new Item[size];
}
};
private Item(Parcel in) {
this.id = in.readLong();
this.code = in.readString();
/* read the others fields */
}
};
Let's create our CursorHandler implementation:
public class ItemCursorHandler implements CursorHandler<Item> {
@Override
public Item handle(Cursor cu) throws SQLException {
Item item = new Item();
int idx = cu.getColumnIndex(CommonsColumns.ID);
if (idx != -1)
item.id = cu.getLong(idx);
idx = cu.getColumnIndex(ItemColumns.CODE);
if (idx != -1)
item.code = cu.getString(idx);
idx = cu.getColumnIndex(ItemColumns.TITLE);
if (idx != -1)
item.title = cu.getString(idx);
idx = cu.getColumnIndex(ItemColumns.DESCRIPTION);
if (idx != -1)
item.description = cu.getString(idx);
idx = cu.getColumnIndex(ItemColumns.IMAGE_URL);
if (idx != -1)
item.imageUrl = cu.getString(idx);
return item;
}
}
and, then, the related CursorAdapter:
public class ItemsCursorAdapter extends CursorHandlerAdapter<Item> {
public ItemsCursorAdapter(Context context) {
super(context, new ItemCursorHandler());
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
/* Here we can fetch our pojo */
Item item = getItem(cursor);
/* Get the ViewHolder as usual and fill/set the views */
ViewHolder holder = (ViewHolder) view.getTag();
holder.xxxx.setYYY(....);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View rowView = getLayoutInflater().inflate(R.layout.my_very_cool_layout, parent, false);
ViewHolder holder = new ViewHolder();
holder.xxxx = (YYYY)rowView.findViewById(R.id.yyyy);
...
...
rowView.setTag(holder);
return rowView;
}
static
private class ViewHolder {
YYYY xxxx;
...
...
};
}
In ours Fragments or Activities we can retrieve the pojo:
ItemsCursorAdapter adapter = ...
Item value = adapter.getItem(position);
/* do whatever with the pojo */
In this way I don't need to "cut/copy or singletonize" all the different cursorToPojo methods.
Is this a proper way of handling the Cursor ? or there are some possible drawbacks ?
Could you point me to more consolidated alternatives ?
Best Regards, LuS
if you are using a ContentProvider then dont pass POJOs, pass an Uri your ContentProvider can use