Actually I know how to send extra values from activity to fragment, but I need to send contacts cursor to fragment and after that I am passing that cursor to cursor adapter and showing data in listview.
There are a number of ways to accomplish what you are trying to do. Only some of them require you to possess the Cursor prior to creating the Fragment.
You could create a method that creates your fragment and pass in the cursor then:
public class YourFragment extends Fragment {
private Cursor mCursor;
public static YourFragment createYourFragmentWithCursor( Cursor cursor ) {
YourFragment fragment = new YourFragment();
fragment.setCursor( cursor );
return fragment;
}
#Override
public void onViewCreated (View view, Bundle savedInstanceState) {
ListView listView = (ListView) findViewById( R.id.yourListView );
listView.setAdapter( new CursorAdapter( getActivity(), getCursor() );
}
protected Cursor getCursor() {
return mCursor;
}
private Cursor setCursor( Cursor cursor ) {
mCursor = cursor;
}
}
You could pass the cursor from the activity to a fragment it controls would be to have the activity implement an interface containing a method that returns the cursor. Then, in your fragment you can get a reference to that interface.
For example:
public interface CursorProvidingInterface {
Cursor getCursor();
}
public class YourActivity extends Activity implements CursorProvidingInterface {
...
#Override
public Cursor getCursor() {
Cursor cursor = whateverYouDoToAcquireYourCursor();
return cursor;
}
...
}
public class YourFragment extends Fragment {
private CursorProvidingInterface cursorProvidingInterface;
#Override
public void onAttach( Activity activity ) {
try {
cursorProvidingInterface = (CursorProvidingInterface) activity;
}
catch (ClassCastException e) {
throw new RuntimeException( activity.getClass().getName() + " must implement " + CursorProvidingInterface.class.getName() );
}
}
#Override
public void onViewCreated (View view, Bundle savedInstanceState) {
ListView listView = (ListView) findViewById( R.id.yourListView );
listView.setAdapter( new CursorAdapter( getActivity(), cursorProvidingInterface.getCursor() );
}
}
Depending on your situation the above strategies may not be the best to choose.
Assuming your cursor comes from a database I would suggest that you simply acquire the cursor from the database when your fragment is created. You could pass in any query parameters you might need as arguments to the fragment if necessary.
In this case, I prefer to use some form of dependency injection such as RoboGuice and create a #Singleton class that will handle your database transactions that you can then #Inject and then call upon when needed.
You might consider implementing ContentProvider and using it to get the cursor you need. This could be especially helpful if the data you are showing from your Cursor is likely to change frequently.
These last two cases are the more robust ways to go about passing data around in a Cursor and I suggest you familiarize yourself with them. A search for tutorials on these methods will yield much better examples than I will provide here.
use constructor to take the values to the fragment. then assign the values from constructor to instance variable. then you can use cursor inside onCreateView() method to initialize it with fragment starting.
Updated:
create setter method for set the Cursor in Fragment, so add following method
public static setMyCursor(Cursor pCursor){
this.cursor=pCursor;
}
use constructor as public
public MyFragment(){
}
set the cursor using setMyCursor() method
Related
updated my code. My issue happens when i back out of the activity. Listview items are lost. I checked the Sqlite database and all items are saved, just not showing up again on listView when I reStart-Activity.
MainActivity
private ListView lst;
private CustomeAdapter cv;
private EditText nameEd, middleEd, lastEd;
private ArrayList<People> peopleArrayList;
private DataHelper myData;
peopleArrayList = new ArrayList<>();
OnCreate.....
public void addPerosn(View view) {
String myName = nameed.getText().toString();
String myMiddle = middleed.getText().toString();
String myLast = lasted.getText().toString();
boolean insert = myData.addData(myName, myMiddle, myLast);
if (insert == true) {
peopleArrayList.add(new People(myName, myMiddle, myLast));
cv = new CustomeAdapter(this, peopleArrayList);
lst.setAdapter(cv);
nameed.setText("");
middleed.setText("");
lasted.setText("");
} else {
Toast.makeText(getApplicationContext(), "Error", Toast.LENGTH_SHORT).show();
}
}
}
My DataHelper method i want to call to Show All
public Cursor showData(){
SQLiteDatabase db = this.getWritableDatabase();
Cursor data = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
return data;
}
Any suggestions are appreciated . Thanks
Make sure you have overridden getCount and it returns proper count.
#Override
public int getCount() {
return items.length;
}
Apart from above solution, I would recomment you to do it in proper way
a) Create a model/pojo class say Person which will have firstName,lastName and middleName
b) create a data set of Person, i.e list of person
c) create a method addPerson in adapter class, and call whenever you want to add new Person data into the list. addPerson method will also refresh the adapter by calling notifyDataSetChanged
d) In activity create adapter object only once, later on just use method of it say adapter.addPerson(person)
I've created some CursorWrapper class
public class DogsCursorWrapper extends CursorWrapper {
public DogsCursorWrapper(Cursor cursor) {
super(cursor);
}
public Dog getDog() {
Dog dog = new Dog();
dog.setDogId(getInt(getColumnIndex(DogTable.ID)));
dog.setDogName(getString(getColumnIndex(DogTable.NAME)));
dog.setDogKind(getString(getColumnIndex(DogTable.KIND)));
return dog;
}
}
Then i use queryDogs method to fill a cursor and then return an instance of wrapper class
private DogsCursorWrapper queryDogs() {
Cursor simpleCursor = db.query(
DogTable.TABLE_NAME, null,null,null,null,null,null);
DogsCursorWrapper dogsCursor = new DogsCursorWrapper(simpleCursor);
// simpleCursor.close(); // this line causes an error in runtime
return dogsCursor;
}
Next step i call the method above in method below:
private void loadDogs() {
DogsCursorWrapper dogsCursor;
dogs = new ArrayList<>();
try {
dogsCursor = queryDogs();
dogsCursor.moveToFirst();
while (!dogsCursor.isAfterLast()) {
dogs.add(dogsCursor.getDog());
dogsCursor.moveToNext();
}
} finally {
dogsCursor.close();
}
}
In fact i do close the dogsCursor in my last method and my question is: didn't i miss some cursor that i have to close? To be clear i have some doubts about simpleCursor in queryDogs method. Should i close that one?
Is it correct decision to use custom DogsCursorWrapper class in this way? Thanks a lot!
The line that has been commented out:
// simpleCursor.close(); // this line causes an error in runtime
is unnecessary. I think you're asking: Does it make a copy of the cursor? The answer is no. The cursor wrapper uses the cursor that is passed in.
The easiest way to verify that is to do:
private DogsCursorWrapper queryDogs() {
Cursor simpleCursor = db.query(
DogTable.TABLE_NAME, null,null,null,null,null,null);
DogsCursorWrapper dogsCursor = new DogsCursorWrapper(simpleCursor);
simpleCursor.close(); // this line causes an error in runtime
if (dogsCursor.isClosed()) { // Because of this
Log.w(TAG, "Houston we have a problem...");
}
return dogsCursor;
}
Running this code demonstrates that closing the original cursor also closes the cursor held by the CursorWrapper.
I am building a very simple app that contains a SQLiteDatabase which I want to display in a ListFragment, using a custom SimpleCursorAdapter.
My code is working fine, but I'm not sure if I'm doing things the correct way. I have searched a lot for (authoritative) examples of this, but have only found either overly simplified examples using ArrayAdapter, or overly complicated examples using ContentProvider.
ListFragment
public class CallListFragment extends ListFragment{
private CallListDbHelper dbHelper;
private SQLiteDatabase db;
private Cursor cursor;
private CallListAdapter adapter;
#Override
public void onResume() {
super.onResume();
// Create a database helper
dbHelper = new CallListDbHelper(getActivity());
// Get the database
db = dbHelper.getReadableDatabase();
// Get a cursor to the entire call list from the database
cursor = db.query( // SELECT
CallEntry.TABLE_NAME, // FROM ...
new String[] { // <columns>
CallEntry._ID,
CallEntry.COLUMN_NUMBER,
CallEntry.COLUMN_TIME },
null, // WHERE ... (x = ?, y = ?)
null, // <columnX, columnY>
null, // GROUP BY ...
null, // HAVING ...
CallEntry.COLUMN_TIME + " DESC" // ORDER BY ...
);
adapter = new CallListAdapter(getActivity(), cursor);
setListAdapter(adapter);
}
#Override
public void onPause() {
super.onPause();
// Close cursor, database and helper
if( null !=cursor ) cursor.close();
if( null != db ) db.close();
if( null != dbHelper ) dbHelper.close();
}
}
Adapter
public class CallListAdapter extends SimpleCursorAdapter {
private static final String[] FROM = {
CallListContract.CallEntry.COLUMN_NUMBER,
CallListContract.CallEntry.COLUMN_TIME
};
private static final int[] TO = {
R.id.phoneNumber,
R.id.time
};
public CallListAdapter(Context context, Cursor cursor){
this(context, R.layout.listitem_call, cursor, FROM, TO);
}
private CallListAdapter(Context context, int layout, Cursor cursor, String[] from, int[] to) {
super(context, layout, cursor, from, to, 0);
}
}
For something simple it will work. But...
Do you use your DB in one place?
How big is your DB?
How often do you hit onResume/onPause?
General recommendation is onResume/onPause should be as fast as possible, but DB operations can be blocking...
Especially first touch (creation) of DB can be time-consuming and potentially you can get ANR.
Don't use Activity as a Context or you may can get memory leaks. The recommended way is to use the Context of your Application in conjunction with singleton pattern, so you'll not bother to close DB.
CursorLoader needs the cursor to be open in order to function and will call close() on the cursor for you. As for SimpleCursorAdapter I don't see auto-close feature in the source code =(.
I have a simple contentProvider, a layout with a ListView and a button for adding Items in content Provider and a CursorLoader. The android.content.Loader, D reference states that
The Loader will monitor for changes to the data, and report them to
you through new calls here. You should not monitor the data yourself.
For example, if the data is a Cursor and you place it in a
CursorAdapter, use the CursorAdapter(android.content.Context,
android.database.Cursor, int) constructor without passing in either
FLAG_AUTO_REQUERY or FLAG_REGISTER_CONTENT_OBSERVER (that is, use 0
for the flags argument). This prevents the CursorAdapter from doing
its own observing of the Cursor, which is not needed since when a
change happens you will get a new Cursor throw another call here.
But the Log.info line in the onLoadFinished method was not executed and listView didn't refreshed. Here is my (simple) code:
public class HomeActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor>{
static final String TAG = "HomeActivity";
SimpleCursorAdapter adapter;
ListView listAnnunciVicini;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.home);
listAnnunciVicini = (ListView) findViewById(R.id.lista_annunci_vicini);
adapter = new SimpleCursorAdapter(this, R.layout.list_item, null,
new String[] {
ContentDescriptor.Annunci.Cols.ID,
ContentDescriptor.Annunci.Cols.TITOLO,
ContentDescriptor.Annunci.Cols.DESCRIZIONE
}, new int[] {
R.id.list_annunci_item_id_annuncio,
R.id.list_annunci_item_titolo_annuncio,
R.id.list_annunci_item_descrizione_annuncio
}, 0);
listAnnunciVicini.setAdapter(adapter);
// Prepare the loader. Either re-connect with an existing one,
// or start a new one.
getSupportLoaderManager().initLoader(0, null, this).forceLoad();
}
public void addRandomItem(View sender) {
ContentValues dataToAdd = new ContentValues();
dataToAdd.put(ContentDescriptor.Annunci.Cols.TITOLO, "Titolo");
dataToAdd.put(ContentDescriptor.Annunci.Cols.DESCRIZIONE, "Lorem ipsum dolor sit amet.");
this.getContentResolver().insert(ContentDescriptor.Annunci.CONTENT_URI, dataToAdd);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// creating a Cursor for the data being displayed.
String[] proiezione = new String[] {ContentDescriptor.Annunci.Cols.ID, ContentDescriptor.Annunci.Cols.TITOLO, ContentDescriptor.Annunci.Cols.DESCRIZIONE };
CursorLoader cl = new CursorLoader(this, ContentDescriptor.Annunci.CONTENT_URI, proiezione, null, null, null);
return cl;
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
adapter.swapCursor(data);
Log.i(TAG, "I dati sono stati ricaricati");
}
public void onLoaderReset(Loader<Cursor> loader) {
// This is called when the last Cursor provided to onLoadFinished()
// above is about to be closed. We need to make sure we are no
// longer using it.
adapter.swapCursor(null);
}
}
Any suggestion?
AFAIK, you need to implement notification yourself in ContentProvider. For this, add something like getContext().getContentResolver().notifyChange(uri, null); to insert,update and delete method of ContentProvider and invoke.setNotificationUri(getContext().getContentResolver(), uri) on your Cursor in queryas described in official documentation. Here you can find the whole picture.
Note: You should not close the cursor (cursor.close()) in order to get
notifications about the changes.
I need to make a ListAdapter that presents data from multiple ContentProviders. The ContentProviders themselves represent one table each from relational database.
I want to use the CursorLoader system to retrieve aggregate data into ListView. Is this possible to do with 1 loader or do I need to use multiple loaders? I'd prefer to use one.
I'm not sure how I can have 2 ContentProviders interact with each other beyond doing the join manually in code which doesn't seem like a great option either.
You will have to write a Custom Loader class. For example:
public class FooLoader extends AsyncTaskLoader {
Context context;
public FooLoader(Context context) {
super(context);
this.context = context;
}
#Override
public Cursor loadInBackground() {
Log.d(TAG, "loadInBackground");
YourDatabase dbHelper = new YourDataBase(context);
SQLiteDatabase db= dbHelper.getReadableDatabase();
/*** create a custom cursor whether it is join of multiple tables or complex query**/
Cursor cursor = db.query(<TableName>, null,null, null, null, null, null, null);
return cursor;
}
}
In the calling activity or fragments onCreate() method, you would need to call the custom loader class:
public class MyFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate():" + mContent);
Loader loader = getLoaderManager().initLoader(0, null, this);
loader.forceLoad();
}
#Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
Log.d(TAG, "onCreateLoader()") ;
return new FooLoader(getActivity());
}
#Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
Log.d(TAG, "onLoadFinished");
}
#Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
}
}
You might want to take a look at CursorJoiner.
I'm new to ContentLoaders myself, but I haven't yet seen a way that you could use one ContentLoader to handle multiple ContentProviders.
Are the tables you're querying in separate databases? It isn't clear from your question. If the tables are all in the same database, one alternative might be to instead use one ContentProvider for the separate tables. The data can be joined and returned to one cursor, which means you could use one CursorLoader. The SQLiteQueryBuilder.setTables() method has some information on this:
http://developer.android.com/reference/android/database/sqlite/SQLiteQueryBuilder.html#setTables%28java.lang.String%29
and you can see it in action here:
http://code.google.com/p/openintents/source/browse/trunk/shoppinglist/ShoppingList/src/org/openintents/shopping/provider/ShoppingProvider.java
this might also be helpful:
https://stackoverflow.com/a/3196484/399105