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;
}
}
Related
In my onCreate:
ListView Definition:
listView = (ListView) findViewById(R.id.listView1);
Loader Manager:
getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(ListDetailActivity.this,
ReceiptProvider.URI_RECEIPT, Receipt.FIELDS, null, null,
null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
System.out.println("Cursor: " + c);
}
#Override
public void onLoaderReset(Loader<Cursor> arg0) {
}
});
My custom Cursor adapter:
private class CurAdapter extends CursorAdapter {
public CurAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView tv = (TextView) view.findViewById(R.id.textView2);
String name = (cursor.getString(cursor.getColumnIndexOrThrow("receipt_name")));
tv.setText(name);
//setImage(image, iv);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = LayoutInflater.from(context).inflate(R.layout.list_layout, null);
return view;
}
}
How do I set the Cursor to list now? I know how to do it for a SimpleCursorAdapter, but I am clueless on how to do it for a Cursor Adapter, any hints?
For now I am doing it like:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
CurAdapter Cur = new CurAdapter(ListDetailActivity.this, c, 0);
listView.setAdapter(Cur);
}
It works properly, but I am not sure if this is the proper way to do this?
maybe so:
listView.setAdapter(new CurAdapter(this, cursor));
you need to set your adapter to listview in init method and hold the reference to adapter as a field. it is ok to pass null as a cursor parameter. There are two methods in CursorAdapter class changeCursor and swapCursor, use one you need to set the cursor in onLoadFinished.
Update: https://developer.android.com/training/load-data-background/handle-results.html
Sorry for my bad english and a stupid noob question.
I have a SimpleCursorAdapter and a ListView with Buttons on each item (row in database).
I realize deleting row but I don`t know how to refresh the ListView.
I hope that somebody can help me with simple and clear examples
my adapter
public class MySimpleCursorAdapter extends SimpleCursorAdapter {
Context context;
public MySimpleCursorAdapter(Context contxt, int layout, Cursor c, String[] from, int[] to, int flags) {
super(contxt, layout, c, from, to, flags);
context=contxt;
}
public View newView(Context _context, Cursor _cursor, ViewGroup parent){
LayoutInflater inflater = (LayoutInflater) _context.getSystemService(_context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.listform_item, parent, false);
return view;
}
#Override
public void bindView(View view, Context Context, Cursor cursor) {
String name = cursor.getString(cursor.getColumnIndex(DBHelper.FORM_NAME));
String title = cursor.getString(cursor.getColumnIndex(DBHelper.FORM_TITLE));
TextView formname = (TextView)view.findViewById(R.id.tvFormName);
formname.setText(name);
TextView formtitle=(TextView)view.findViewById(R.id.tvFormTitle);
formtitle.setText(title);
ImageButton yourButton = (ImageButton)view.findViewById(R.id.ibtnDelete);
yourButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(view != null) {
Object obj = view.getTag();
//if(obj != null && obj instanceof Integer) {
dbForm form=new dbForm(context);
form.open();
String st=obj.toString();
form.deleteForm(Long.valueOf(st).longValue());
Toast.makeText(context, "Delete row with id = " + st, Toast.LENGTH_LONG).show();
}
}
});
Object obj = cursor.getString(cursor.getColumnIndex(DBHelper.FORM_ID));
yourButton.setTag(obj);
}
}
I also use a CursorLoader in Main to acces the database
UPD: i use use cursor loader and a dont undertand how call his reset in my custom adapter, hope for help.
public class MainActivity extends FragmentActivity implements LoaderCallbacks<Cursor> {
ListView lvForms;
dbForm table_form;
SimpleCursorAdapter scAdapter;
/**
* Called when the activity is first created.
*/
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
table_form = new dbForm(this);
table_form.open();
String[] from = new String[]{DBHelper.FORM_NAME, DBHelper.FORM_TITLE};
int[] to = new int[]{R.id.tvFormName, R.id.tvFormTitle};
scAdapter = new MySimpleCursorAdapter(this, R.layout.listform_item, null, from, to, 0);
lvForms = (ListView) findViewById(R.id.lvForms);
lvForms.setAdapter(scAdapter);
registerForContextMenu(lvForms);
getSupportLoaderManager().initLoader(0, null, this);
}
public void onButtonClick(View view) {
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
startActivity(intent);
}
protected void onDestroy() {
super.onDestroy();
table_form.close();
// закрываем подключение при выходе
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle bndl) {
return new MyCursorLoader(this, table_form);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
scAdapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
scAdapter.swapCursor(null);
}
static class MyCursorLoader extends CursorLoader {
dbForm table_form;
public MyCursorLoader(Context context, dbForm table_form) {
super(context);
this.table_form = table_form;
}
#Override
public Cursor loadInBackground() {
Cursor cursor = table_form.getAllData();
return cursor;
}
}
}
For a SimpleCursorAdapter it's best to use a CursorLoader like this :
public Loader<Cursor> onCreateLoader(int id,Bundle arg) {
return new SimpleCursorLoader(this) {
public Cursor loadInBackground() {
// This field reserved to what you want to load in your cursor adater and you return it (for example database query result)
// return Cursor ;
}
};
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
}
public void onLoaderReset(Loader<Cursor> loader){
adapter.swapCursor(null);
}
And in your MainActivity or where you will instanciate the adapter, just before add this :
getLoaderManager().initLoader(0x01, null, this);
// declare your new Custom Simple Cursor Adapter here
scAdapter = new MySimpleCursorAdapter ....
And add :
public void onResume() {
super.onResume();
// Restart loader so that it refreshes displayed items according to database
getLoaderManager().restartLoader(0x01, null, this);
}
I work with loaders this way, I hope it helps you .
You should reload your cursor and update your cursor adapter:
// Reload your cursor here
Cursor newCursor = ….;
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD){
adapter.swapCursor(newCurosr);
} else {
adapter.changeCursor(newCursor);
}
Then, notify your list view about data changes:
adapter.notifyDataSetChanged();
OK so I have a rather annoying issue. I am simply attempting to list all the songs on the device using a CursorLoader and LoaderCallbacks. My problem is simply that nothing is being displayed. I am using EXACTLY the same method as I am to load all albums, artists and playlists on the device. Using some debugging I have discovered that the problem is that newView() is only being called once within the CursorAdapter
Here is my CursorAdapter:
private class SongItemAdapter extends CursorAdapter
{
public SongItemAdapter(Context context)
{
super(context, null, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor)
{
final int albumId = cursor.getInt(cursor
.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
final String songName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
final String artistName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
final String duration =
Utilities.milliSecondsToTimer(
cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
final ImageView albumCover = (ImageView) view.findViewById(R.id.all_songs_album_cover);
final TextView songNameTextView = (TextView) view.findViewById(R.id.all_songs_song_name);
final TextView artistNameTextView = (TextView) view.findViewById(R.id.all_songs_artist_name);
final TextView durationTextView = (TextView) view.findViewById(R.id.all_songs_song_duration);
ImageLoader.getInstance().displayImage(
ContentUris.withAppendedId(
sArtworkUri, albumId).toString(), albumCover);
songNameTextView.setText(songName);
artistNameTextView.setText(artistName);
durationTextView.setText(duration);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent)
{
return LayoutInflater.from(context).inflate(R.layout.song_list_item, parent, false);
}
}
Here is my LoaderCallbacks:
private final LoaderCallbacks<Cursor> mCursorCallbacks = new LoaderCallbacks<Cursor>()
{
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args)
{
return new CursorLoader(getActivity(),
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Song.FILLED_PROJECTION, MediaStore.Audio.Media.IS_MUSIC + "!=0", null,
MediaStore.Audio.Media.TITLE + " ASC");
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data)
{
mAdapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader)
{
mAdapter.swapCursor(null);
}
};
And here is the initialisation of my adapter and listview and whatnot:
mAdapter = new SongItemAdapter(this.getActivity());
mListView = (ListView) rootView.findViewById(R.id.all_songs_list);
mListView.setOnItemClickListener(this);
mListView.setOnScrollListener(new PauseOnScrollListener(ImageLoader.getInstance(), false, false));
mListView.setAdapter(mAdapter);
mListView.setFastScrollEnabled(true);
mListView.setFastScrollAlwaysVisible(true);
mListView.setRecyclerListener(new RecyclerListener()
{
#Override
public void onMovedToScrapHeap(View view)
{
// Release strong reference when a view is recycled
final ImageView imageView = (ImageView) view.findViewById(R.id.all_songs_album_cover);
imageView.setImageBitmap(null);
}
});
getLoaderManager().initLoader(LOADER_CURSOR, null, mCursorCallbacks);
As I stated previously, this is exactly the method I use for loading albums, artists and playlists and those are working fine.
I'am loading data into listview from cursors by initiating multiple loaders. I want my listview to show the data from all cursors but data in listview is changing immediately with a 2 seconds interval and showing data of only last cursor. Please help me. Where iam going wrong? Do I have to merge the cursors?
Inside onCreateView() of my fragment :
for (int i = 0; i < idsArray.length; i++) {
Bundle b = new Bundle();
b.putInt("id", idsArray[i]);
getLoaderManager().initLoader(idsArray[i], b, this); //initiating multiple loaders
}
adapter = new CustomAdapter(getActivity(), R.layout.list_row, null, column_names_array, ui_ids_array, 0);
listView.setAdapter(adapter);
And loader callback methods :
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle b) {
Builder uriBuilder = MY_PROVIDER_URI.buildUpon();
uriBuilder.appendPath("categories");
uriBuilder.appendPath(String.valueOf(b.getInt("id")));
uriBuilder.appendPath("data");
return new CursorLoader(getActivity(), uriBuilder.build(), null, null, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if(adapter != null && data != null){
adapter.swapCursor(data);
adapter.notifyDataSetChanged();
}
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
And here is my adapter class :
public class CustomAdapter extends SimpleCursorAdapter {
Cursor cursor;
Context context;
Activity activity;
int layout;
public CustomAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags ) {
super(context, layout, c, from, to, flags);
this.cursor = c;
this.context = context;
this.activity = (Activity) context;
this.layout = layout;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(this.layout, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
super.bindView(view, context, cursor);
ViewHolder holder = (ViewHolder) view.getTag();
if (holder == null) {
holder = new ViewHolder();
//finding UI elements in list row here
view.setTag(holder);
}
//binding data to views of viewholder here
}
static class ViewHolder {
//variables of all my UI elements
}
}
Your loop :
for (int i = 0; i < idsArray.length; i++) {
Bundle b = new Bundle();
b.putInt("id", idsArray[i]);
getLoaderManager().initLoader(idsArray[i], b, this); //initiating multiple loaders
}
will result in creating multiple Loaders, once a Loader has finished loading its data, the method
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if(adapter != null && data != null){
// This line result in replacing the last Cursor by the new one
adapter.swapCursor(data);
adapter.notifyDataSetChanged();
}
}
is invoked. When the last Loader has finished loading the Cursor, its data will replace the last Cursor because of swapCursor.
If you look at the CursorAdapter documentation, you will notice that this CursorAdapter can only manage one Cursor at a time.
To resolve your problem, you should try to query all the wanted data into a single query, resulting in a single Cursor.
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