Here is my SimpleCursorAdapter extension class which I use trying to display information about contacts in a ListView:
private class CustomContactsAdapter extends SimpleCursorAdapter {
private int layout;
private LayoutInflater inflater;
public CustomContactsAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to, 0);
this.layout = layout;
inflater = LayoutInflater.from(context);
}
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = inflater.inflate(layout, parent, false);
return v;
}
#Override
public void bindView(View v, Context context, Cursor cur) {
MatrixCursor c = (MatrixCursor) cur;
final String name = c.getString(c.getColumnIndex(COLUMN_NAME));
final String org = c.getString(c.getColumnIndex(COLUMN_ORG));
final byte[] image = c.getBlob(c.getColumnIndex(COLUMN_PHOTO));
ImageView iv = (ImageView) v.findViewById(R.id.contact_photo);
if(image != null && image.length > 3) {
iv.setImageBitmap(BitmapFactory.decodeByteArray(image, 0, image.length));
}
TextView tname = (TextView) v.findViewById(android.R.id.text1);
tname.setText(name);
TextView torg = (TextView) v.findViewById(android.R.id.text2);
torg.setText(org);
}
}
But when the program reaches the code snippet where I want to get blob data from cursor an UnsupportedOperationException is thrown there with message:
getBlob is not supported
I want to know what am I doing wrong. Also, I pass a MatrixCursor baked by myself as a parameter to the adapter.
That's the implemetaion of getBlob(int) from MatrixCurosr in Android 1.6 and Android 2.3.
public byte[] getBlob(int column) {
throw new UnsupportedOperationException("getBlob is not supported");
}
That's the getBlob(int) implementation for Android ICS
#Override
public byte[] getBlob(int column) {
Object value = get(column);
return (byte[]) value;
}
probably you want to subclass MatrixCursor and implment the getBlob in the ICS way
Related
I have implemented a Custom CursorAdapter in Android without using the from to pattern,in order to be able to widely reuse my adapter I would like to add it to my adapter.How do I do this?
This is my adapter:
public class AtomAdapter extends CursorAdapter {
LayoutInflater inflater;
#SuppressWarnings("deprecation")
public AtomAdapter(Context context, Cursor c) {
super(context, c);
inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//will create a new View only when recycling an older View is not possible
View v= inflater.inflate(R.layout.layout_row, parent,false);
TextView tv=(TextView)v.findViewById(R.id.txt_title);
v.setTag(R.id.txt_title,tv);
TextView tv2=(TextView)v.findViewById(R.id.txt_content);
v.setTag(R.id.txt_content,tv2);
return v;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
// bind data to your row
TextView txt_title=(TextView)view.getTag(R.id.txt_title);
TextView txt_content=(TextView)view.getTag(R.id.txt_content);
txt_title.setText(cursor.getString(cursor.getColumnIndex(AtomDB.TITLE)));
txt_content.setText(cursor.getString(cursor.getColumnIndex(AtomDB.CONTENT)));
}
}
I was reading the SimpleCursorAdapter source code and found that these two methods may be of some help.The first method,findColumns converts from column name to column index:
private void findColumns(String[] from) {
if (mCursor != null) {
int i;
int count = from.length;
if (mFrom == null || mFrom.length != count) {
mFrom = new int[count];
}
for (i = 0; i < count; i++) {
mFrom[i] = mCursor.getColumnIndexOrThrow(from[i]);
}
}
else {
mFrom = null;
}
}
The second method is the bindView method where you can call even more specific methods such as setViewText and setViewImage.
#Override
public void bindView(View view,Context context,Cursor cursor)
{
final int[] from=mFrom;
final int[] to=mTo;
for(int i=0;i<mTo.length;i++)
{
String text=cursor.getString(from[i]);
if(text==null)
text="No text found";
if(view instanceof TextView)
setViewText((TextView)v,text);
else if(view instanceof ImageView)
setViewImage((ImageView)v,text);
else
throw new IllegalStateException(v.getClass().getName()+" is not a View that can be bound by SimpleCursorAdapter");
}
}
The method setImageUri runs on the main UI thread,this could cause latency.
I am making a layout for a row in a list. One of the columns consists of "work Hours", which takes data from the database in 2 parts: start and finish. I am using a cursor loader to get the data into the UI. So my question is what is the best way to combine the data of both start and finish into a format like 12:00~4:00 if the 2 times are stored separately? I've thought about putting another linear layout, but it seems like a waste. Any suggestions?
EDIT:
This is my Loader callbacks, pretty standard I think.
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
String[] projection = Columns.getColumns(RawContract.PARAM_SIWORKDAYS);
CursorLoader cursorLoader = new CursorLoader(mContext,
BidProvider.CONTENT_URI_SIWORKDAYS, projection, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
And I'm using it like this
private void fillData() {
String[] from = new String[] { Columns.COLUMN_WORKDATE,
Columns.COLUMN_DAYCHARGE, Columns.COLUMN_TEMPERATURE,
Columns.COLUMN_WORKFROMHR, Columns.COLUMN_WORKTOHR,
Columns.COLUMN_WEATHER, Columns.COLUMN_CREATEDBY,
Columns.COLUMN_CREATEDDATE };
int[] to = new int[] { R.id.contractor, R.id.contract_num, R.id.title,
R.id.value, R.id.status, R.id.dept_name, R.id.start, R.id.close };
getLoaderManager().initLoader(0, null, this);
mAdapter = new SimpleCursorAdapter(mContext, R.layout.contract_row, null, from,
to, 0);
setListAdapter(mAdapter);
}
So I guess what I'm getting at is that I never manually pull the data from the cursor, so I'm not sure what the best way to put those two pieces of data together is.
I think that is better to use custom CursorAdapter with ViewHolder.
private static class MyCursorAdapter extends CursorAdapter {
private final Context context;
private final LayoutInflater layoutInflater;
public MyCursorAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
this.context = context;
this.layoutInflater = (LayoutInflater) context.getSystemService(Service.LAYOUT_INFLATER_SERVICE);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
View v = layoutInflater.inflate(R.layout.my_list_item, parent, false);
holder.startFinishTimeView = (TextView) v.findViewById(R.id.startFinishTimeView);
holder.content = v.findViewById(R.id.content);
v.setTag(holder);
return v;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
holder.startFinishTimeView.setText(<past here your formated time from Cursor>);
}
private static class ViewHolder {
TextView startFinishTimeView;
View content;
}
}
I'm writing a screen that displays a row's worth of information from a DB. Basically it's a Detail Fragment that represents information pertaining to one 'row' in a table. I want to understand the best practice for binding data from a cursor (one unique row from a table) to a layout of textviews, checkboxes, etc.
Is AdapterView the ticket?
#JoeMalin suggested:
Then write an adapter between a cursor and an array of text views.
Which boils down my question. What's the right way to hook a series of text views to a cursor?
If you want to do processing on some of the cursor data before you move it to the text views, then you're going beyond the adapter pattern, which assumes that "recasting" the form of a data structure to another data structure without any intermediate processing. The virtue of an adapter is that, for two data structures A and B linked by an adapter, it's assumed that B automatically changes whenever A changes.
Of course, you can redefine the idea of adapter to insert your own intermediate operation, such as converting dates, or you could make the conversion an aspect of the view that's displaying the data. I am guessing that the "processing" is really formatting, which you do for display purposes. That's an attribute of the text view, not the data; write something that extends text view and converts dates as needed. Then write an adapter between a cursor and an array of text views.
I recently implemented my own data adapter class that may be in the ball park.
public class NoteImageDataAdapter {
private final View mMainView;
private Cursor mCursor;
private ViewHolder holder;
private ContentObserver mContentObserver;
public static class ViewHolder {
public TextView title;
public TextView text;
public ImageView image;
}
public NoteImageDataAdapter(View mainView, Cursor c) {
if (mainView == null) {
throw new IllegalArgumentException("View mainView cannot be null");
}
if (c == null) {
throw new IllegalArgumentException("Cursor c cannot be null");
}
mMainView = mainView;
mCursor = c;
holder = new ViewHolder();
holder.title = (TextView) mMainView.findViewById(R.id.title);
holder.text = (TextView) mMainView.findViewById(R.id.text);
holder.image = (ImageView) mMainView.findViewById(R.id.myImageView);
mContentObserver = new ImageNoteContentObserver(new Handler());
mCursor.registerContentObserver(mContentObserver);
bindView();
}
class ImageNoteContentObserver extends ContentObserver {
public ImageNoteContentObserver(Handler handler) {
super(handler);
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
#Override
public void onChange(boolean selfChange) {
Log.d("NoteImageDataAdapter", "ImageNoteContentObserver.onChange( "
+ selfChange + ")");
super.onChange(selfChange);
mCursor.requery();
bindView();
}
}
public void bindView() {
Log.d("NoteImageDataAdapter", "bindView");
mCursor.moveToFirst();
holder.text.setText(Note.getText(mCursor));
holder.title.setText(Note.getTitle(mCursor));
Uri imageUri = Note.getImageUri(mCursor);
if (imageUri != null) {
assignImage(holder.image, imageUri);
} else {
Drawable d = Note.getImageThumbnail(mCursor);
holder.image.setImageDrawable(d);
holder.image.setVisibility(View.VISIBLE);
}
}
private static final int MAX_IMAGE_PIXELS = 1024*512;
private void assignImage(ImageView imageView, Uri imageUri){
if (imageView != null && imageUri != null){
ContentResolver cr = imageView.getContext().getContentResolver();
Display display = ((WindowManager) imageView.getContext()
.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
int width = (int) (display.getWidth() * 0.9);
int height = (int) (display.getHeight() * 0.9);
int minSideLength = Math.min(height, width);
Bitmap b = Util.makeBitmap(minSideLength, MAX_IMAGE_PIXELS, imageUri, cr, false);
if (b == null){
b = Util.makeBitmap(minSideLength, MAX_IMAGE_PIXELS/2, imageUri, cr, false);
}
if (b != null){
imageView.setImageBitmap(b);
imageView.setAdjustViewBounds(true);
imageView.setVisibility(View.VISIBLE);
}
}
}
}
and in your activity
private NoteImageDataAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.note_image_view_layout);
wireDataAdapter();
}
private void wireDataAdapter() {
final String[] COLUMNS = new String[] {
Note.Columns.TITLE,
Note.Columns.TEXT,
Note.Columns.IMAGE_URI,
Note.Columns.IMAGE_THUMBNAIL,
Note.Columns._ID };
// the uri for the note row
Uri contentUri = getIntent().getData();
Cursor cur = managedQuery(contentUri, COLUMNS, null, null, null);
View mainLayout = this.findViewById(R.id.noteImageViewLayout);
mAdapter = new NoteImageDataAdapter(mainLayout, cur);
}
From the activity use:
Adpater adapter = new Adapter(Activity.this or context , Cursor);
setListAdapter(adapter) in case of List Activity;
Otherwise
listViewObj.setAdpater(adapter)
public class CustomCursorAdapter extends CursorAdapter {
private LayoutInflater mInflater;
private Context activityContext;
private ViewHolder holder;
public ContactsAdapter(Context aContext,Cursor cursor) {
super(mContext, cursor);
mInflater = LayoutInflater.from(mContext);
activityContext = aContext;
}
public static class ViewHolder{
public TextView textView1;
// View Group on Row inflate lyaout that need to be used
public ImageView imageView;
}
#Override
public void bindView(View v, Context context, Cursor c) {
holder=(ViewHolder)v.getTag();
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.item_inflate_layout, parent, false);
holder = new ViewHolder();
holder.textView1 = (TextView) v.findViewById(R.id.TEXTVIEW1);
// Other Id that need to be used and are available on item_inflate_layout
holder.imageView = (ImageView) v.findViewById(R.id.IMAGEVIEW);
v.setTag(holder);
bindView(v, context, cursor);
return v;
}
}
I'm trying to fill a grid view using data from a db cursor using a custom SimpleCursorAdapter.
My cursor has data (I checked), but nothing is shown in the GridView, and the getView() method is not even called.
Anybody can help? Why is getView() not called?
Thanks
Activity
dbAdapter = new DBAdapter(this);
dbAdapter.open();
Cursor c;
c = dbAdapter.fetchPCList();
startManagingCursor(c);
String[] from = new String[] {};
int[] to = new int[] {};
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new PCIconAdapter(this, R.layout.pc_icon, c, from, to));
c.close();
dbAdapter.close();
Adapter
public class PCIconAdapter extends SimpleCursorAdapter {
private final Context mContext;
private final int mLayout;
private final Cursor mCursor;
private final int mPCIDIndex;
private final int mClassNameIndex;
private final LayoutInflater mLayoutInflater;
private final class ViewHolder {
public TextView pc_id_view;
public TextView clas_name_view;
}
public PCIconAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.mContext = context;
this.mLayout = layout;
this.mCursor = c;
this.mPCIDIndex = mCursor.getColumnIndex(DBAdapter.KEY_PC_LM_ID);
this.mClassNameIndex = mCursor.getColumnIndex(DBAdapter.KEY_PC_CLAS_NAME);
this.mLayoutInflater = LayoutInflater.from(mContext);
}
public View getView(int position, View convertView, ViewGroup parent) {
if (mCursor.moveToPosition(position)) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = mLayoutInflater.inflate(mLayout, null);
viewHolder = new ViewHolder();
viewHolder.pc_id_view = (TextView) convertView.findViewById(R.id.pc_id);
viewHolder.clas_name_view = (TextView) convertView.findViewById(R.id.clas_name);
convertView.setTag(viewHolder);
}
else {
viewHolder = (ViewHolder) convertView.getTag();
}
String pc_id = mCursor.getString(mPCIDIndex);
String clas_name = mCursor.getString(mClassNameIndex);
viewHolder.pc_id_view.setText(pc_id);
viewHolder.clas_name_view.setText(clas_name);
}
return convertView;
}
}
I had the same problem. I was using an ArrayAdapter and then I changed to a SimpleCursorAdapter, but it doesn't show anything on screen.
I removed
c.close();
dbAdapter.close();
and now its working fine.
because CursorAdapter does not have getView method . it have newView method .
so extend SimpleCursorAdapter and overRide newWiev and other methods like
public class ContactListCursorAdapter extends SimpleCursorAdapter implements Filterable {
private Context context;
private int layout;
public ContactListCursorAdapter (Context context, int layout, Cursor c, String[] from, int[] to) {
super(context, layout, c, from, to);
this.context = context;
this.layout = layout;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
int nameCol = c.getColumnIndex(People.NAME);
String name = c.getString(nameCol);
/**
* Next set the name of the entry.
*/
TextView name_text = (TextView) v.findViewById(R.id.name_entry);
if (name_text != null) {
name_text.setText(name);
}
return v;
}
#Override
public void bindView(View v, Context context, Cursor c) {
int nameCol = c.getColumnIndex(People.NAME);
String name = c.getString(nameCol);
/**
* Next set the name of the entry.
*/
TextView name_text = (TextView) v.findViewById(R.id.name_entry);
if (name_text != null) {
name_text.setText(name);
}
}
#Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (getFilterQueryProvider() != null) { return getFilterQueryProvider().runQuery(constraint); }
StringBuilder buffer = null;
String[] args = null;
if (constraint != null) {
buffer = new StringBuilder();
buffer.append("UPPER(");
buffer.append(People.NAME);
buffer.append(") GLOB ?");
args = new String[] { constraint.toString().toUpperCase() + "*" };
}
return context.getContentResolver().query(People.CONTENT_URI, null,
buffer == null ? null : buffer.toString(), args, People.NAME + " ASC");
}
}
Your column names array and Views array are empty. So column name to views mapping will never occur. That maybe the reason you are not getting callback on getView
If I remove
c.close();
dbAdapter.close();
it works...
So where am I supposed to close this cursor?...
I've got ListActivity and I am using custom CursorAdapter.
In each item of the list I've got also checkbox...
Now I have in my list screen a perm button, when you press on it,
It should find all the checkboxes which are 'checked' and do some operations on the item which it's checkbox is 'checked'.
How can I retrieve all the checked ones?
I've done focusable:false, so I can use OnClickListener, but I don't know how farther then
this..
Thanks,
some code:
This is in my ListActivity class:
final String columns[] = new String[] { MyUsers.User._ID,
MyUsers.User.MSG, MyUsers.User.LOCATION };
int[] to = new int[] { R.id.toptext, R.id.bottomtext,R.id.ChkBox,
R.id.Location};
Uri myUri = Uri.parse("content://com.idan.datastorageprovider/users");
Cursor cursor = getContentResolver().query(myUri, columns, null, null, null);
startManagingCursor(cursor);
ListCursorAdapter myCursorAdapter=new ListCursorAdapter(this,R.layout.listitem, cursor, columns, to);
this.setListAdapter(myCursorAdapter);
and this is my Custom Cursor adapter class:
public class ListCursorAdapter extends SimpleCursorAdapter
{
private Context context;
private int layout;
public ListCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to)
{
super(context, layout, c, from, to);
this.context = context;
this.layout = layout;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
return v;
}
#Override
public void bindView(View v, Context context, Cursor c)
{
TextView topText = (TextView) v.findViewById(R.id.toptext);
if (topText != null)
{
topText.setText("");
}
int nameCol = c.getColumnIndex(MyUsers.User.MSG);
String name = c.getString(nameCol);
TextView buttomTxt = (TextView) v.findViewById(R.id.bottomtext);
if (buttomTxt != null)
{
buttomTxt.setText("Message: "+name);
}
nameCol = c.getColumnIndex(MyUsers.User.LOCATION);
name = c.getString(nameCol);
TextView location = (TextView) v.findViewById(R.id.Location);
if (locationLinkTxt != null)
{
locationLinkTxt.setText(name);
}
}
//Modify to meet your needs
String[] from = new String[]{ YourDbAdapter.KEY_WORDS_ROWID, YourDbAdapter.KEY_WORDS_ROWID};
int[] to = new int[]{ R.id.row_item_checkbox};
mAdapter = new SimpleCursorAdapter(this, R.layout.row_item_with_checkbox, yourDbCursor, from, to);
mAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
if(view.getId() == R.id.myCheckbox )
{
CheckBox cb = (CheckBox) view;
cb.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
final long id = cursor.getLong(columnIndex); //Your row id
//You can do the necessary work here such as adding to an List or somehting
}
});
return true;
}
return false;
}
});