Question

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.

CursorHandler.java

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)
}

CursorHandlerAdapter.java

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; }   
};

Usage example

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

Was it helpful?

Solution

if you are using a ContentProvider then dont pass POJOs, pass an Uri your ContentProvider can use

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top