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

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?

Related

Why does ArrayAdapter fail to update listview after activity restart?

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.

Dynamic Allocation of Array doesn't work for ArrayAdapter while assigning it for Spinner in Android

I am really facing a problem here to create a Spinner widget in Android. The goal is to populate a Spinner with data that i will dynamically retrieve from a source.
Now I am able to create a spinner with a data source that is implicitly declared in the program. But when ever i am trying to fetch the data from a dynamically created array, the apps throws a Force Close.
I will paste some demo examples to explain my problem here!
String[] SSID = new String[15];
String[] Data = {"Captain","America","Hulk","Ironman","Thor"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
addDevDialogue = new Dialog(this);
addDevDialogue.setContentView(R.layout.popup);
concat();
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
dialogue();
}
});
}
public void concat()
{
for(int i=0;i<5;i++)
{
SSID[i]=Data[i];
}
}
public void dialogue()
{
addDevDialogue.setTitle("Movies List");
addDevDialogue.setCancelable(true);
addDevDialogue.show();
spinList2 = (Spinner)addDevDialogue.findViewById(R.id.spinner2);
ArrayAdapter<String> listAdapter2 = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_checked, SSID);
spinList2.setAdapter(listAdapter2);
}
The above code throws an error when ever I try to open the dialogue box.
I have tried this same sample with a pre-defined data source in place of "SSID" which yields a error free output!
I cannot understand why 'SSID[]' array doesnt work when I define it to the ArrayAdapter.
Any Insight will help!!!
You are call calling show() before populating adapter so call
addDevDialogue.show();
after
spinList2.setAdapter(listAdapter2);
UPDATE :
Once change size of SSID
String[] SSID = new String[Data.length];
Hope this will helps you.
Your string array String[] Data = {"Captain","America","Hulk","Ironman","Thor"}; is declared with 5 elements (index 0-4)
In your loop you loop 6 times
public void concat()
{
for(int i=0;i<5;i++)
{
SSID[i]=Data[i];
}
}
Which probably causes an Index out of bounds exception. Change your loop to this
public void concat()
{
for(int i=0;i<4;i++)
{
SSID[i]=Data[i];
}
}
#swarna: You are allocating a fixed array of 15 elements, then populating only 5 elements. The array adapter is probably getting tripped with the other 10 elements which have not been initialized. Suggest you make your SSID array to have only 5 elements OR if this is a dynamically determined value, you could keep ArrayList and keep adding to it. Then, when setting up the adapter do this:
YourObjList.add("one")
YourObjList.add("two")
YourObjList.add("three")
String[] SSID = YourObjList.toArray(new YourObjList[YourObjList.size()]);
ArrayAdapter<String> listAdapter2 = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_checked, SSID);
spinList2.setAdapter(listAdapter2);
This will allow variable list sizes.
Hope this helps you

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

Sorting ArrayAdapter for Android

I have a MultiAutoCompleteTextView which lets you enter in multiple entries and shows you autocomplete suggestions. My issue arises when I submit my data. I am adding any entered strings to the drop down list, but my attempts to sort the data fail. The code that executes on submit:
final private Comparator<String> comp = new Comparator<String>() {
public int compare(String e1, String e2) {
return e1.toString().compareTo(e2.toString());
}
};
((ArrayAdapter<String>) autoCompleteView.getAdapter()).add(getString());
((ArrayAdapter<String>) autoCompleteView.getAdapter()).sort(comp);
The code for what happens on clicking the autoCompleteView:
view.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
adapter.getFilter().filter(null);
//adapter.sort(comp);
view.showDropDown();
}
});
Can anyone find anything wrong with what I'm doing?
EDIT: some more info, after incorporating changes from #Sam
private ArrayList<String> array = new ArrayList<String>();
private ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line,
array);
private void setUpAutoComplete(final MultiAutoCompleteTextView view)
{
array.add("test string"); // this is successfully added to the drop down list
Collections.sort(array, comp);
adapter.notifyDataSetChanged();
}
private void onSubmit()
{
array.add(getString()); // this fails to add
adapter.notifyDataSetChanged();
}
but my attempts to sort the data fail
This is a little vague. But I'll take a guess.
First you do something redundant:
return e1.toString().compareTo(e2.toString());
Since e1 and e2 are already Strings you don't need to call String#toString(). Also this basic String comparator already exists. So you don't need any of this.
A better technique is to sort the List, not the adapter. Simply use Collections' sorting method:
List<String> list = new ArrayList<String>();
list.add(getString());
...
Collections.sort(list);
adapter.notifyDataSetChanged();
Notice I changed adapter.add() to list.add(). I did this because adapter.add() calls list.add() and adapter.notifyDataSetChanged() but the adapter shouldn't be updated until after the new list is sorted.

how to append latest data to custom base adapter list view in android?

I have implemented an application with Custom base adapter for display data in list view.In my application i have displayed some content to list view by using Custom base adapter that content will come from web service.I have used a button for get the latest data from service.when i get the latest data from service then i would like to append the latest data to list view at bottom without deleting previous data in list view.
I have implemented my application as follows:
result = new ParseXml().convertMessages(new Model().getMessages("0"));
count = Integer.parseInt(result.get(0).getMessageID());
((Button)findViewById(R.id.oldMessagesButton)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
fetchNewMessages();
}
});
protected void fetchOldMessages() {
// TODO Auto-generated method stub
waitProgreess = ProgressDialog.show(this, "Please wait", "Loading...");
new Thread() {
public void run() {
try {
Thread.sleep(800);
} catch (InterruptedException e) {
}
newmessageHandler.sendEmptyMessage(0);
}
}.start();
}
private Handler newmessageHandler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.v("000000", "count :"+count);
if(count>0){
newmsgResult = new ParseXml().convertMessages(new Model().getNewMessages(""+count));
CustomeAdapter adapter = new CustomeAdapter(GetMsgsScreen.this,newmsgResult);
lst.setAdapter(adapter);
adapter.notifyDataSetChanged();
count=Integer.parseInt(newmsgResult.get(oldmsgResult.size()-1).getMessageID());
}
waitProgreess.dismiss();
}
};
I have implemented the above code based on user clicking on button count value will be send to service then get latest from service.When i get the latest list from servie the that list would like append to list view.
from the above code i can get only latest previous are deleting.
How can i append latest data(list) to listview in my above case?
please any body help me....
CustomeAdapter adapter = new CustomeAdapter(GetMsgsScreen.this,newmsgResult);
define this adapter andnewmsgResult at top of your class..not a local variable inside a class..
Now, whenever you want to update the data in list view, update the values/data in newmsgResult and call adapter.notifyDataSetChanged()
I think it is happening because you are creating a new custom adapter every time you are getting new messages.. in this line
CustomeAdapter adapter = new CustomeAdapter(GetMsgsScreen.this,newmsgResult);
dont do this and try to use adapter.add(Newmessage); you can use array list to make work easier
ok.. you should have an add function in your custom adapter class for that.. you can do so by adding this to your custom adapter class
private void customadd(String newmsg)
{
//ArrayList<String> msg=new List<String>; create this array list as a source to your adapter
msg.add(newmsg);
adapter.notifyDataSetChanged();
}
Initially load the your list view with customAdapter using result(By assumption result is arraylist)
CustomeAdapter adapter = new CustomeAdapter(GetMsgsScreen.this,result);
lst.setAdapter(adapter);
once you have new value no need to create new result and adapter object, simply append you result in your adapter object,
result.add("append your new result");
then simply adapter.notifyDataSetChanged();
Dont create a new adapter. Keep the original one and pass the new data to it for appending it.

Categories

Resources