Why does ArrayAdapter fail to update listview after activity restart? - android

I have an activity that in onCreate() does the following:
Creates an empty ArrayList
Creates a new ArrayAdapter associated with the above ArrayList
Sets ListView to use the above ArrayAdapter
Uses Volley to send a GET request to my API to fetch some JSON data to load into the ListView
Once the data is fetched I add it to my ArrayList and the ListView is populated as expected
My problem is that when the activity is restarted (i.e. the screen is rotated via the emulator or the activity is restarted through Android Studio) the ListView no longer populates.
I am not saving any state. I expect the activity to return to its initial default state so I don't think onSaveInstanceState() is the answer.
I've verified that the data is returned successfully from the API and that the adapter's hashcode is the same before and after the volley request and that it equals the ListView's set adapter. I've also verified that onDestroy() and then onCreate() are called when the activity is restarted so I know it is going through a full life cycle.
If I rotate the screen programmatically with setRequestedOrientation() I don't experience this issue. If I add items to my ArrayList outside of the GET request callback, I don't experience this issue.
Here is my activity onCreate()
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
//The data to be displayed
descriptions = new ArrayList<>();
listView = (ListView)this.findViewById(R.id.myListView);
//Link 'descriptions' to the adapter
adapter = new ArrayAdapter<>(this, R.layout.list_json_text_view, descriptions);
listView.setAdapter(adapter);
this.addTextFilter();
this.addListViewClickListener();
//See my ApiGetRequest class below
request = new ApiGetRequest();
request.send(this.getContext(), getDataUrl(), this, "", REQUEST_TYPES.TEXT);
}
And my activity GET request callback
public void onSuccess(DescriptiveJSONArray items, REQUEST_TYPES type) {
descriptions.clear();
try {
for (int i = 0; i < items.length(); ++i) {
JSONObject obj = items.getJSONObject(i);
String desc = obj.optString("name", "") + " " + obj.optString("description", "");
//TODO: Remove debug code
System.out.println("Adding: "+desc);
descriptions.add(desc);
}
}
catch(JSONException e) {
e.printStackTrace();
//getJSONObject failed
}
}
And my ApiGetRequest methods
//My activity implements ApiGetCallback
public void send(Context context, String url, ApiGetCallback callback, String tag, REQUEST_TYPES type) {
StringRequest stringRequest = getStringRequest(url, callback, tag, type);
//Singleton wrapper for RequestQueue
AppRequestQueue queue = AppRequestQueue.getInstance(context);
queue.add(stringRequest);
}
//Inner class inside ApiGetCallback
class SuccessListener implements Response.Listener<String> {
ApiGetCallback callback;
REQUEST_TYPES type;
public SuccessListener(ApiGetCallback callback, REQUEST_TYPES type) {
this.callback = callback;
this.type = type;
}
#Override
public void onResponse(String response) {
try {
DescriptiveJSONArray jsonResp = new DescriptiveJSONArray(response);
callback.onSuccess(jsonResp, type);
}
catch(JSONException e) {
callback.onJsonException(e);
}
}
}
Any ideas what is happening?. I'm testing on Marshmallow and Nougat

You are missing a call to notifyDataSetChanged, after the onSuccess function is done.

you may need to override onStart and do update anything in it

adapter = new ArrayAdapter<>(this, R.layout.list_json_text_view, descriptions);
listView.setAdapter(adapter);
//See my ApiGetRequest class below
request = new ApiGetRequest();
request.send(this.getContext(), getDataUrl(), this, "", REQUEST_TYPES.TEXT);
use this part of code in onResume method.

Related

setting adapter gives null exception in retrofit 2 call back method?

I am new to android and I have been using retrofit 2 to get data from the server, in my app when activity starts or on create of activity, a method is called to fetch data from server and on-Response of this call I am getting data and setting the data to the spinner and it gets successful and the list of data is visible in user interface when running the app.
Now I have to add Autocomplete text-view so that on typing a character the spinner gives that list of data according to the typed characters in Autocomplete text-view.
I have added array adapter with data and I set it to the spinner it works, below this using same array adapter I set it to the Autocomplete text-view but gives an error of null pointer exception. So for my understanding adapter is somehow setting it early which points to the null error.
public void fetch_information() {
ApiInterface2 = ApiClient.getApiClient().create(Api.class);
Call<List<City>> call = ApiInterface2.GetCities();
call.enqueue(new Callback<List<City>>() {
#Override
public void onResponse(Call<List<City>> call, Response<List<City>> response) {
CityList = new ArrayList<City>();
CityList = response.body();
City myCustomObject = new City();
myCustomObject.setCityName("Select Country");
CityList.add(myCustomObject);
ArrayList<String> CityNames = new ArrayList<String>();
cities = new String[CityList.size()];
citiesid = new String[CityList.size()];
for (int i = 0; i < CityList.size(); i++) {
cities[i] = CityList.get(i).getCityName();
citiesid[i] = CityList.get(i).getCityId();
CityNames.add(CityList.get(i).getCityName());
}
city_adapter = new ArrayAdapter<String>(AddKhataPurchase.this, android.R.layout.simple_list_item_1, cities);
city_adapter.setDropDownViewResource(R.layout.spinner_dropdown_layout);
city_spinner.setAdapter(city_adapter);
city_spinner.setSelection(cities.length - 1);
// fetch_searchable_spinner_cities();
if(CityNames.size()> 1) {
ArrayAdapter myAdapter = new ArrayAdapter<String>(AddKhataPurchase.this,
android.R.layout.simple_spinner_dropdown_item, CityNames);
//here not able to set the adapter as null pointer exception happens
myAutoCompleteTextView.setAdapter(myAdapter);
}
}
#Override
public void onFailure(Call<List<City>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
Activity opens, the method (fetch_information) gets called and here onResponse city spinner after setting array adapter shows the list of cities in UI but when i try to do the same below it by setting array adapter to myAutoCompleteTextview null pointer exception although got the data but still error.
Can i set Autocomplete Textview after the response call finishes?

Avoid getting Race condition in Android Volley in Android app

I'm new to Android Development and I'm trying to develop my first Android app which gets data from some public APIs using Android Volley.
I'm using singleton Volley Request Queue which is initialized in the launcher activity. I am successfully able to parse the JSON contents and display them on a Fragment layout/view (uses RecyclerView & CardView) when I set my RecyclerView adapters INSIDE the Volley's JsonObjectRequest.
The following code does display data, but suffers from time race condition.
Note: RvJoiner is a library which merges multiple adapters and makes a single adapter ordered by FIRST COME FIRST SERVE basis.
My Fragment class is as follows:
public class Fragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.recylcer_main, container, false);
ParseJSON parseJSON = new ParseJSON(v);
parseJSON.makeRequest1();
parseJSON.makeRequest2();
return v;
}
}
My ParseJSON class is as follows
public class ParseJSON {
private static final String URL1 = "some url";
private static final String URL2 = "some other url";
private static final String TAG = "ParseJSON";
private RequestQueue requestQueue;
private boolean FLAG_REQUEST1_FETCHED;
private boolean FLAG_REQUEST2_FETCHED;
private ArrayList<status1> status1ArrayList;
private ArrayList<status2> status2ArrayList;
private Context context;
private RvJoiner rvJoiner;
private View view;
ProgressDialog pd;
ParseJSON (View v){
this.view= v;
this.context=v.getContext();
pd = ProgressDialog.show(v.getContext(), "Please Wait", "Getting Data from APIs", true);
requestQueue = AppController.getInstance(v.getContext()).getRequestQueue();
rvJoiner = new RvJoiner();
}
public void makeRequest1() {
JsonObjectRequest request1 = new JsonObjectRequest(Request.Method.GET, URL1,
null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
/* Parsing Stuff and storing it in status1ArrayList */
FLAG_REQUEST1_FETCHED=true;
Status1Adapter status1Adapter = new Status1Adapter(status1ArrayList);
RecyclerView recList = (RecyclerView) view.findViewById(R.id.cardList);
recList.setLayoutManager(new LinearLayoutManager(context));
rvJoiner.add(new JoinableAdapter(status1Adapter));
recList.setAdapter(rvJoiner.getAdapter());
pd.dismiss();
}
} catch (JSONException e) {}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {}
});
AppController.getInstance(context).addToRequestQueue(request1);
}
public void makeRequest2() {
JsonObjectRequest request2 = new JsonObjectRequest(Request.Method.GET, URL2,
null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
/* Parsing stuff and storing it inside ArrayList status2ArrayList */
FLAG_REQUEST2_FETCHED=true;
Status2Adapter status2Adapter = new Staus2Adapter(status2ArrayList);
RecyclerView recList = (RecyclerView) view.findViewById(R.id.cardList);
recList.setLayoutManager(new LinearLayoutManager(context));
rvJoiner.add(new JoinableAdapter(status2Adapter));
}
} catch (JSONException e) {}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {}
});
AppController.getInstance(context).addToRequestQueue(request2);
}
public boolean isStatusFetched(){
return FLAG_REQUEST1_FETCHED && FLAG_REQUEST2_FETCHED;
}
public ArrayList<status1> getstatus1ArrayList() {
return status1ArrayList;
}
public ArrayList<status2> getstatus2ArrayList() {
return status2ArrayList;
}
}
In the above code, I'm having a race condition. Since Volley network calls are asynchronous in nature, I have no control on which request will get completed and displayed on my Fragment CardView first. (i.e any of rvJoiner.add() requests can be executed first)
I would like to make my UI consistent i.e I want Request1 adapter to be added to RvJoiner first and then the Request2.
If possible, I would like to move all my code that sets adapters and joins them from JsonObjectRequest to my Fragment's onCreateView method. So, in this way, I have a control on the order of adapters. However, then I need a method which checks the value of FLAG_REQUEST1_FETCHED and FLAG_REQUEST2_FETCHED via isStatusFetched method continuously.
Code for the Fragment class will be
public class Fragment1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.recylcer_main, container, false);
ParseJSON parseJSON = new ParseJSON(v);
parseJSON.makeRequest1();
parseJSON.makeRequest2();
while(!parseJSON.isDataFetched()){
/* I want to wait here till both status1ArrayList and status2ArrayList gets populated with data in ParseJSON. In this way I can control the order in which adapters are added inside RvJoiner. If I don't wait here I will get NullPointerException on runtime since Volley calls are asynchronous and getStatus1ArrayList/getStatus2ArrayList will most probably return null. But how to wait here without consuming too much CPU power? */
}
ArrayList<status1> status1ArrayList = parseJSON.getstatus1ArrayList();
ArrayList<status2> status2ArrayList = parseJSON.getstatus2ArrayList();
Status1Adapter status1Adapter = new Status1Adapter(status1ArrayList);
Status2Adapter status2Adapter = new Status2Adapter(status2ArrayList);
RecyclerView recList = (RecyclerView) v.findViewById(R.id.cardList);
recList.setLayoutManager(new LinearLayoutManager(v.getContext()));
RvJoiner rvJoiner = new RvJoiner();
/* Problem solved as I'm adding adapters in the order I want */
rvJoiner.add(new JoinableAdapter(status1Adapter));
rvJoiner.add(new JoinableAdapter(status2Adapter));
recList.setAdapter(rvJoiner.getAdapter());
return v;
}
}
One solution can be using callbacks. I read somewhere about them, but I'm not sure if it solves my problem of 'multiple request at the same time while maintaining order'.
Another solution would be to restrict my Volley Queue to handle one request at one time only but that would increase the time taken to fetch and serve data. This is my last choice.
I am virtually out of ideas and would like someone to help me so that I can control the order of setting my adapters and maintain a consistent UI. If you need any other information, please tell me.
Thanks.
This is how avoiding race conditions for two requests work in general. You should work with callbacks. The implementation of your onResponse methods are callbacks because those methods are called after one request is done. Response handling works on the UI thread right ? So the responses can just be handled one by the other.
This means you just have to maintain order there. Extract the work you would like to do after getting one response. You need some boolean flags indicating whether your requests are done. Pseudocode would look like this:
request1Done = false;
request2Done = false;
doRequest1();
doRequest2();
onResponse1() {
doWorkForRequest1(); // always start handling the response
request1Done = true;
if (request2Done) { // if this is true, request2 was faster than request1
doWorkForRequest2();
}
};
onResponse2() {
request2Done = true;
if (request1Done) { // request1 did its work, no its request2's turn
doWorkForRequest2();
}
};
So basically you should fix your onReponse methods. Hope this will help you. :)

Save a ListView layout on Android?

I have a ListView containing news from RSS feeds in MainActivity, the problem is I have to stream the RSS feeds everytime I open the app because the items of the ListView are destroyed when I close the app.
I understand I can save it in SQLite temporarily, but is there a more simple way to save the ListView layout just so it would still be there next time I open the app?
Another option is to use SharePreferences and Gson to convert your datasource for the list view into a string for storage and then when the app is re-opened you can rebuild the list view with the stored data fairly quickly. I do something similar in one app where the data source for my list view is a ArrayList of LinkedHashMap items, so this would be the two methods for converting the ArrayList to a String and then back to an ArrayList when needed
public static String ArrayListToString(ArrayList<LinkedHashMap> list) {
return gson.toJson(list);
}
public static ArrayList<LinkedHashMap> StringToArrayList(String input) {
Type collectiontype = new TypeToken<ArrayList<LinkedHashMap>>(){}.getType();
ArrayList<LinkedHashMap> list = gson.fromJson(input, collectiontype );
return list;
}
I would also then suggest storing a timestamp so you can check if the stored list should be displayed or if an updated list needs to be retrieved
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.clientListView)
...whatever other setup you want to do here
CheckTimeStamp();
}
public static void CheckTimeStamp() {
String timeStamp = preferences.getString("keyClientTimeStamp", "");
Calendar calendar = Calendar.getInstance();
Date date = calendar.getTime();
String currentTime = HelperClass.GetSimpleDateFormat(date);
if (currentTime.equals(timeStamp)) {
String storedString = preferences.getString("keyStoredClients", "");
clientArrayList = HelperClass.StringToArrayList(decryptedArray);
//the setupView method is where I take my ArrayList
//and add it to my ListView's Adapter
SetupView();
}
else {
SharedPreferences.Editor editor = preferences.edit();
editor.putString(Constants.keySalesTimeStamp, currentTime);
editor.apply();
//for me this is a web service that get's a list of clients,
//converts that list to a String to store in SharedPreferences
//and then calls SetupView() to add the list to the ListView Adapter
GetClientList();
}
}
Hmm, I would use Volley library for caching requests, I guess it very simple: you do request and next time first of all get it from cache. You don't have to explicitly save and describe a model for storing data for this case.
Below, I gave the example of how it might look:
public class RSSProvider {
private final RequestQueue mRequestQueue;
// ...
private RSSProvider(Context context) {
mRequestQueue = Volley.newRequestQueue(context.getApplicationContext());
}
// ...
public void getSomething(final Response.Listener<String> responseListener, final Response.ErrorListener errorListener) {
if (mRequestQueue.getCache().get(<URL>) != null) {
responseListener.onResponse(new String(mRequestQueue.getCache().get(<URL>).data));
} else {
StringRequest request = new StringRequest(Request.Method.GET, <URL>, responseListener, errorListener);
request.setShouldCache(true);
mRequestQueue.add(request);
}
}
// ...
}

How to append an Item to a list?

I have a list that gets loaded from the server. Below is the task that does this:
class LoadActivities extends AsyncTask <String, String, String> {
protected String doInBackground(String ... args) {
final RestAdapter restAdapter = new RestAdapter.Builder().setServer("http://10.0.2.2:8080").build();
final MyService apiManager = restAdapter.create(MyService.class);
final Activity activity = apiManager.getActivity("some user", act_id);
//tasks in activity
for (Tasks t : activity.getTasks()) {
String r_id = t.getId()+"";
String name = t.getName();
HashMap<String, String> map = new HashMap<String, String>();
map.put("activity_id", act_id);
map.put("t_id", t_id);
map.put("t_name", name);
tasksList.add(map);
}
return null;
}
protected void onPostExecute(String file_url) {
runOnUiThread(new Runnable() {
public void run() {
ListAdapter adapter = new SimpleAdapter(
TaskActivity.this, tasksList,
R.layout.list_item_rec, new String[] { "act_id", "t_id", "t_name"}, new int[] {
R.id.act_id, R.id.task_id,R.id.task_name });
setListAdapter(adapter);
}
});
}
}
All of this is working fine. However, on another screen I am adding an item on the server and after that I come back to this screen to show the list again. At the time of coming back I want to refresh the list so that it reflects the newly added item.
Questions
Should I refresh the entire list? I have tried doing this by calling the above class again. like so:
public boolean onOptionsItemSelected(MenuItem menuItem) {
if (menuItem.getTitle().toString().equalsIgnoreCase("save")) {
new CreateTask(this,activityName.getText().toString(), actId).execute();
Intent returnIntent = new Intent();
setResult(RESULT_OK, returnIntent);
finish();
return true;
}
return true;
}
...back on this screen
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
Log.d("This is result", result);
new LoadActivities().execute();
}
}
}
problem with this is that It is repopulating the list. Meaning I have duplicates of every activity. How can I resolve this?
OR Is there a way so that I won't have to reload the entire list but rather just add an item(s) to the existing list?
First,in the method "onPostExecute", you don't need to call "runOnUiThread", because the "onPostExecute" was run in UI thread.
Second, if you want to refresh the ListView in front of the page, you can use "onActivityResult" in the front page, but if your server data was updated, just get data from server again and update your data set(list), then call adapter.notifyDataSetChanged().
Wish to help you!
You should us and ArrayAdapter and let it handle the list.
Create and set the ArrayAdapter right away, then add items to it as necessary. You'll have to override getView in the adapter, but for a simple view that won't be complex code.
The general structure will look like:
onCreate(...) {
// It's okay if the adapter is empty when you attach it to the ListView
setListAdapter(new ArrayAdapter<ListItemType>(...));
}
onPostExecute(...) {
// Once you've retrieved the list of items from the server, add them to
// the adapter
ArrayAdapter adapter = (ArrayAdapter) getListAdapter();
adapter.add([items retrieved from server]);
}
onActivityResult(..., Intent data) {
// Add the newly added item, either pass it back directly, or get the new
// list from the server and compare to see which item needs adding.
// For simplicity, we'll assume it was passed back by the activity
ListItemType newlyAddedItem = (ListItemType) data.getParcelableExtra("key");
ArrayAdapter adapter = (ArrayAdapter) getListAdapter();
adapter.add(newlyAddedItem);
}

Android: notifyDataSetChanged(); not working

I have a database in a server and from a Tablet I take some values from one table in the database. I load this information correctly into a list but I would like to know why when there is a change, nothing happens even if I use notifyDataSetChanged();. I must say that for loading the loading data y use the AsyncTaskClass
So, my problem is that I don't know if use the notifyDataSetChanged(); method correctly ,because if there's is a change I would like to refresh the image. Here is some part of the code of the class:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.all_candidatos);
candidatosList = new ArrayList<HashMap<String, String>>();
new CargarCandidatos().execute();
}
// public void timer(){
// new CountDownTimer(tiempo, 100) {
//
// public void onTick(long millisUntilFinished) {
//
// }
//
// public void onFinish() {
// // new CargarCandidatos().execute();
//
// }
// }.start();}
/**
* Background Async Task to Load all product by making HTTP Request
* */
class CargarCandidatos extends AsyncTask<String, String, String> {
/**
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute() {
super.onPreExecute();
pDialog = new ProgressDialog(Monitorizacion.this);
pDialog.setMessage("Loading ...");
pDialog.setIndeterminate(false);
pDialog.setCancelable(false);
pDialog.show();
}
/**
* getting All products from url
* */
protected String doInBackground(String... args) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
JSONObject json = jParser.makeHttpRequest(url_candidatos, "GET", params);
Log.d("Candidatos: ", json.toString());
try {
int success = json.getInt(TAG_SUCCESS);
if (success == 1) {
candidatos = json.getJSONArray(TAG_CANDIDATOS);
for (int i = 0; i < candidatos.length(); i++) {
JSONObject c = candidatos.getJSONObject(i);
// Storing each json item in variable
String nserie = c.getString(TAG_NSERIE);
String dni = c.getString(TAG_DNI);
String nombre = c.getString(TAG_NOMBRE);
String test = c.getString(TAG_TEST);
String pregunta = c.getString(TAG_PREGUNTA);
String bateria = c.getString(TAG_BATERIA);
// creating new HashMap
HashMap<String, String> map = new HashMap<String, String>();
// adding each child node to HashMap key => value
map.put(TAG_NSERIE, nserie);
map.put(TAG_DNI, dni);
map.put(TAG_NOMBRE, nombre);
map.put(TAG_TEST, test);
map.put(TAG_PREGUNTA, pregunta);
map.put(TAG_BATERIA, bateria);
// adding HashList to ArrayList
candidatosList.add(map);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url) {
pDialog.dismiss();
runOnUiThread(new Runnable() {
public void run() {
/**
* Updating parsed JSON data into ListView
* */
adapter = new SimpleAdapter(
Monitorizacion.this, candidatosList,
R.layout.list_item, new String[] { TAG_NSERIE,
TAG_DNI, TAG_NOMBRE, TAG_TEST, TAG_PREGUNTA, TAG_BATERIA},
new int[] { R.id.id, R.id.dni, R.id.nombre, R.id.test, R.id.pregunta, R.id.bateria});
setListAdapter(adapter);
adapter.notifyDataSetChanged();
// timer();
}
});
}
}
}
One of the main reasons notifyDataSetChanged() won't work for you - is,
Your adapter loses reference to your list.
When you first initialize the Adapter it takes a reference of your arrayList and passes it to its superclass. But if you reinitialize your existing arrayList it loses the reference, and hence, the communication channel with Adapter.
When creating and adding a new list to the Adapter. Always follow these guidelines:
Initialise the arrayList while declaring it globally.
Add the List to the adapter directly without checking for null and empty values. Set the adapter to the list directly (don't check for any condition). Adapter guarantees you that wherever you make changes to the data of the arrayList it will take care of it, but never
lose the reference.
Always modify the data in the arrayList itself (if your data is completely new then you can call adapter.clear() and arrayList.clear() before actually adding data to the list) but don't set the adapter i.e If the new data is populated in the arrayList than just adapter.notifyDataSetChanged()
Stay true to the Documentation.
The thing you need to edit is put your
runOnUiThread(new Runnable() {
public void run() {
/**
* Updating parsed JSON data into ListView
* */
adapter = new SimpleAdapter(
Monitorizacion.this, candidatosList,
R.layout.list_item, new String[] { TAG_NSERIE,
TAG_DNI, TAG_NOMBRE, TAG_TEST, TAG_PREGUNTA, TAG_BATERIA},
new int[] { R.id.id, R.id.dni, R.id.nombre, R.id.test, R.id.pregunta, R.id.bateria});
setListAdapter(adapter);
adapter.notifyDataSetChanged();
// timer();
}
});
into the OnCreate(). and return the list candidatosList from Asynctask. than set timer for updating candidatosList list.
It might be worth checking if you have an empty override for registerDataSetObserver(). Android Studio added one for me without implementing the call to super. Adding it in as follows was enough to get my listView working again:
#Override
public void registerDataSetObserver(DataSetObserver observer) {
super.registerDataSetObserver(observer);
}
An adapter define the comportement of the layout !
-> setListAdapter() : Define the adapter for a ListView/GridView/Gallery...
but you need to specify the data !
I recommend to you, to initialize 'setListAdapter' in the 'onCreate' or in the constructor.
After you set the data into the adapter (exemple : adapter.setItem(yourData))
And NOW ! You should to call notifyDataSetChanged !
Because you have changed the data but the view isn't refresh and notifydatasetchanged() reload the content of the view (ListView/GridView/Gallery...)
For a good practice and understand correctly I recommend to you to use a 'custom adapter' using 'baseAdapter'
Read and do this tutorial (I haver learn with this): http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/
Read the documentation : http://developer.android.com/reference/android/widget/BaseAdapter.html
The update function should be called from UI thread.
My answer is actually similar to #user1621629's answer with that difference that I am using rxJava, so here's working code that solve this problem for me:
this.subscriber = myAdapter.getSubscriber(); // keep for unsubscribe in destroy
dataSource.subscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread()).subscribe(this.subscriber);
So I set all execution in order to get data for the list to computation thread, but showing result in UI thread.
Here's how I create subscriber for this:
public class MyListAdapter extends RecyclerView.Adapter<LocationListAdapter.ViewHolder> {
private List<ListItem> mDataset = new ArrayList<>();
public Subscriber<ListItem[]> getSubscriber() {
return Subscribers.create(new Action1<ListItem[]>() {
#Override
public void call(ListItem[] listItems) {
mDataset.clear();
mDataset.addAll(Arrays.asList(listItems));
notifyDataSetChanged();
}
});
}
......
As Hissain describes above,
you need to maintain a reference to the list
Here's how I got it to work:
Let the list being sent to the adapter be set as an instance member in the activity
In the logic that performs a change to the data, make sure it updates the same list instance that the activity passed to the adapter
Then calling .notifyDataSetChanged(); worked
Remember that listView position starts at 1, so you will have to do (listViewPosition - 1) for your your java.util.List
I dont have much reputation to comment on Mr. Hissain answer.It is correct but I want to mention one more thing that reference to the list should not change. If data source underlying is changing, dont change the reference to new list. Actions only need to be done on the same list object. To do the same,clear the list using clear() and then add data to the same list using add() or addALL() and then call notifyDataSetChanged(). eg.
On first initialization of the list
list = dataSource.getList();
then one can add and remove the content from the list and call notifyDataSetChanged() it works fine but if in the code, one tries to change the reference to the other object. Like
list = dataSource.getList();
where getList() returns the new list everytime, hence the reference changes to some other list object and calling notifyDataSetChnaged does not have impact on the list.But if getList() returns the same list object, it works fine.
If everything you set fine and still not working then your list...
Is it Mutablekind of the List or not...!
private val demoList: MutableList<AnyClass> = mutableListOf()
once you define your list like above mutable manner then you can get the method
.add
.addAll
.remove
etc...
else if you have created normal list then that will not work as notifyDataSetChanged

Categories

Resources