I am trying to refresh a ListFragment after Asynctask is finished. In my Asynctask doInBackground method I connect to a database and add elements to an ArrayList. In the onPostExecute method I want to access my ListFragment by something like this to call it's refreshData(method):
//REFRESH ARTISTFRAGMENT
#Override
public void onPostExecute(ArrayList<String> result) {
ArtistFragment artistFrag = new ArtistFragment();
artistFrag = (ArtistFragment) artistFrag.getSherlockActivity().getSupportFragmentManager().findFragmentById(R.id.frame_container);
if (artistFrag != null) {
artistFrag.refreshData(result);
}
}
But getSupportFragmentManager results in a NullPointerException!
The refreshData method in my Fragment looks like this:
public void refreshData(ArrayList<String> data) {
artists = new ArrayList<String>(data);
this.artistAdpater.notifyDataSetChanged();
}
I have found very similar approaches to do the exact thing I want but I can't find a solution to my problem. Basically it's done over here https://stackoverflow.com/a/16388650 - but it doesn't work for me like that.
Has anybody a solution for this or a workaround?
I am assuming that artists is the array list that feeds your listview.
artists = new ArrayList<String>(data);
What you are doing creates a new arraylist object reference to artists. But the list adapter has the older refrence. Thats why the list is not getting update.
Instead of passing the reference of the new arraylist, you can add it to the old one like so:
newArtistsArraylist = new ArrayList<String>(data);
artists .add(newArtistsArraylist );
Here is a nice solution to your problem.
Related
currently I am using FirebaseRecyclerAdapter to represent data on a RecyclerView using the following Firebase query:
postsQuery = mDatabase.child("lists_new”).orderByKey().limitToFirst(10);
My RecyclerView has a header with 2 buttons: New List, Old List.
New list is loaded by default, and my question is, when the user taps the Old List button, what is the most efficient way to replace the new list with old list.
The Firebase query for the old list looks like this:
postsQuery = mDatabase.child("lists_old”).orderByKey().limitToFirst(10);
Note: both new list and the old list has the same data types, i.e. they share the same Java POJO class and the the same layout.
You will need a new adapter and attach that to the same RecyclerView.
So after constructing the new query, you create a new adapter and attach it to the view:
adapter = new FirebaseRecyclerAdapter<Chat, ChatHolder>(
Chat.class, android.R.layout.two_line_list_item, ChatHolder.class, postsQuery) {
recyclerView.setAdapter(adapter);
I have a similar need and a similar solution except I am calling cleanup() on the adapter before new'ing up another one. I am thinking without calling cleanup() it will create a leak of adapters and/or listeners?
In onActivityCreated() in my Fragment I am calling a method in the Fragment that manages the recycler view. Call the method to initialize or refresh the list and pass in a leaf node name. If the adapter is not null then call cleanup(). Create a new database reference by concatenating a new leaf node with the parent reference, new-up a new adapter and set it.
I call cleanup() in onDestroy() as well, per usual.
It works fine so far though I've only tested using the emulator and a small data set.
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
refreshList(newLeafNode);
}
#Override
public void onDestroy() {
super.onDestroy();
mRVAdapter.cleanup();
}
protected void refreshList(String newLeafNode) {
if (mRVAdapter != null) {
mRVAdapter.cleanup();
}
DatabaseReference newDbRef = mParentRef.child(newLeafNode);
mRVAdapter = new FirebaseRecyclerAdapter<String, MyViewHolder>(String.class, R.layout.single_row, MyViewHolder.class, newDbRef) {
#Override
protected void populateViewHolder(MyViewHolder viewHolder, String model, int position) {
viewHolder.setImage(model);
}
};
mMyRV.setAdapter(mRVAdapter);
}
I want to populate a listView with data retrieved from DB asynchronously,
the problem Is that if I set the adapter, it throws null pointer because the data not arrieved yet, when I recieve data one method is executed, so what I can call in this method to populate my listview? I tried passing the layout to this class to populate directly the listview when the data is recieved but don't worked (nothing happened)
I thought using AsyncTask was a good idea adding wait() and when this "retriever data method" is triggered call notify() but I don't know how to call notify() from an asynctask from another class...
I'm also not sure if asynktask is the best way of doing this, any ideas?
I'm using retrofit2 if it helps
Code of listview create/populate
private List<TmOfsDTO> prepareList() {
List<TmOfsDTO> list;
try {
// Create list of items
ListObtainer listObtainer = new ListObtainer(this);
list = listObtainer.getTmOfsDTOList(user); // this method returns list of objects from DB
} catch (Exception e) {
list = null;
e.printStackTrace();
}
return list;
}
private void populateListView(List<TmOfsDTO> lista) {
// Build Adapter
OrderAdapter orderAdapter = new OrderAdapter(this, 0, lista);
// orderAdapter.getView()
Log.d("LSO", ".....");
// Configure listview
//View rootView = View.inflate(this, R.layout.activity_ordenes, null);
ListView listView = (ListView) findViewById(R.id.lvOrdenes);
listView.setAdapter(orderAdapter);
//listView.invalidateViews();
//listView.refreshDrawableState();
Log.d("LSO", ".......");
Last update:
**
I was doing right the notify when I was recieving data, passing the adapter to the class and calling:
orderAdapter.notifyDataSetChanged();
but the problem is I was missing to add each item with:
orderAdapter.add(orden);
before notify, now seem to work good**
As you logcat show above the lista is null. You are passing this list to the adapter. But if you have not initialized the variable lista it will throw a null pointer exception.
Suggest to initialize the List you passing to the adapter. Then when the data comes in you add the data lista and call the method adapter.notifyDataSetChanged()
Following seems to cause the null pointer exception
} catch (Exception e) {
list = null;
e.printStackTrace();
}
So beside initializing you should also not assign null.
You'd be best using a RecyclerView, anyway. This is part of the new support libraries and is way more efficient than the older ListView.
Some key questions here are whether or not the database is local. If your have an sqlite3 database on the phone that you're loading from then you should have written some classes that load the database information into objects that you can store into an array and pass into the RecyclerView.Adapter very easily.
If you are loading this remotely, then you would be 100% better off syncing the data onto your phone using an AsyncTask before trying to load the RecyclerView, otherwise you'll get those errors.
You could load ONE item into an ArrayList from a remote database, pass that to the RecyclerView, and then carry on loading from the remote database and notifying the RecyclerView of a change upon each item being added to the list.
Create a different constructor for your adapter, one that does not receive data to display in it. When you have that, it will not crash.
To get your data inside, create a local List of your items and populate it with something like:
public void setData(List<MyObject> data){
this.mData = data;
notifyDataSetChanged();
}
Also, don't forget to handle possible null pointer errors in methods such as getItem.
lets say we are in viewDidLoad method. I'd suggest you to do the following:
in your adapter class create public method: setData, which calls adapter.notifyDataSetChanged()
send request to backend by passing callback function
display loading/progress dialog
initialize adapter by passing empty array(not null)
when callback is fired and you get the list call adapter's setData and hide the progress dialog.
If you are using Fragments just call setAdapter() another time after data extraction
My AsyncTaskLoader is loading data from a remote server. When new data arrives, naturally a call is made to onLoadFinished. At this point I don't know how to give the new data to the RecyclerView.Adapter. Calling notifyDataSetChanged does not give it the new data: it simply tells it there is new data. So any advice on how I might do this? Right now the best I can think of is to create my own setData method in my implementation of RecyclerView.Adapter as
public void setData(List<MyObject> data){
if(null != data && !data.isEmpty()){
synchronized(mItems){
mItems.clear();
mItems.addAll(data);
}
notifyDataSetChanged();
}
}
Is my idea the best there is? Or is there a more sound way of doing this?
Expose a public method in your adapter to update data.
For example, you could put it like this
public void updateItems(ArrayList<MyObject> myObjects) {
this.data = myObjects;
notifyDataSetChanged();
}
You have two options,
1. Re- instantiate your adapter with your new data and reset the adapter.
2. The way you do it.
I can not think of any other methods.
Ok, I had the same issue and here is the solution what I did.
1st I passed list object from onLoadFinished and in the RecyclerViewAdapter I have created method name setCardInfoList() and there I passed this object to global List object which I define in adapter class.
In onLoadFinished method..
#Override
public void onLoadFinished(android.content.Loader<List<Earthquake>> loader, List<Earthquake> earthquakes) {
if (earthquakes != null && !earthquakes.isEmpty()) {
adapter.setCardInfoList(earthquakes);
adapter.notifyDataSetChanged();
}else {
emptyView.setText("No Earthquake Found...");
}
}
Inside Adapter class
public void setCardInfoList(List<Earthquake> earthquakes){
this.earthquakeList = earthquakes;
}
Using latest Parse library v1.5.1
Thanks to the update now I can do:
ParseQueryAdapter<ParseObject> mAdapter = new ParseQueryAdapter<ParseObject>(MainActivity.this, new ParseQueryAdapter.QueryFactory<ParseObject>() {
#Override
public ParseQuery<ParseObject> create() {
ParseQuery<ParseObject> query = new ParseQuery<ParseObject>(ParseObject.class);
query.fromLocalDatastore();
return query;
}
});
mListView.setAdapter(mAdapter);
Now I have some pinned objects and they appear correctly, but when I unpin them like so:
//Some ParseObject in the above adapter
object.unpinInBackground(new DeleteCallback() {
#Override
public void done(ParseException e) {
if(e == null) {
//I beleive this would be the correct approach.
mAdapter.notifyDataSetChanged();
}
}
});
Naturally I want that item to disappear from the corresponding ListView, but it doesn't. But say I go back to a different activity and revisit this activity, the ListView is displayed properly without the recently unpinned object.
Is this a bug? If not what am I doing wrong?
I have the same problem) I solve it with invoke method ParseQueryAdapter.loadObjects().
You can try mAdapter.remove(object) before calling notifyDataSetChanged();
unpinInBackground removes the object from the database. Probably the adapter has a local copy of the object.
Looks like there is no remove method in ParseQueryAdapter.
Here is an response from official source:
Since a ParseQueryAdapter is designed to always show the results of a
ParseQuery, you would need to use an API request to reload the query.
https://www.parse.com/questions/delete-a-object-using-parsequeryadapter
This tutorial uses a SimpleAdapter which works fine, but I need to update the arrays in the adapter when new data is entered.
Could you please guide me on how to update a ListView using something other than a SimpleAdapter?
Use a ArrayAdapter backed by an ArrayList. To change the data, just update the data in the list and call adapter.notifyDataSetChanged().
If you create your own adapter, there is one notable abstract function:
public void registerDataSetObserver(DataSetObserver observer) {
...
}
You can use the given observers to notify the system to update:
private ArrayList<DataSetObserver> observers = new ArrayList<DataSetObserver>();
public void registerDataSetObserver(DataSetObserver observer) {
observers.add(observer);
}
public void notifyDataSetChanged(){
for (DataSetObserver observer: observers) {
observer.onChanged();
}
}
Though aren't you glad there are things like the SimpleAdapter and ArrayAdapter and you don't have to do all that?
SimpleListAdapter's are primarily used for static data! If you want to handle dynamic data, you're better off working with an ArrayAdapter, ListAdapter or with a CursorAdapter if your data is coming in from the database.
Here's a useful tutorial in understanding binding data in a ListAdapter
As referenced in this SO question
Most people recommend using notifyDataSetChanged(), but I found this link pretty useful. In fact using clear and add you can accomplish the same goal using less memory footprint, and more responsibe app.
For example:
notesListAdapter.clear();
notes = new ArrayList<Note>();
notesListAdapter.add(todayNote);
if (birthdayNote != null) notesListAdapter.add(birthdayNote);
/* no need to refresh, let the adaptor do its job */
I created a method just for that. I use it any time I need to manually update a ListView. Hopefully this gives you an idea of how to implement your own
public static void UpdateListView(List<SomeObject> SomeObjects, ListView ListVw)
{
if(ListVw != null)
{
final YourAdapter adapter = (YourAdapter) ListVw.getAdapter();
//You'll have to create this method in your adapter class. It's a simple setter.
adapter.SetList(SomeObjects);
adapter.notifyDataSetChanged();
}
}
I'm using an adapter that inherites from BaseAdapter. Should work for any other type of adapter.