I'm trying to populate a ListView with various objects obtained from a RESTApi.
To test if the connection, and the objects are actually recieved, I invoke this method:
protected void onPostExecute(String result) {
try {
JSONArray users = new JSONArray(result);
JSONObject user = users.getJSONObject(1);
Toast.makeText(MainActivity.this, user.getString("name"), Toast.LENGTH_LONG).show();
jsonList = users;
} catch (JSONException e) {
e.printStackTrace();
}
}
The Toast does show me the name of the current object, but whenever I try to populate my ListView, I get a NullPointerException.
I made a TextView to test if the JSON is actually there aswell, but here I get another NullPointerException:
textView.setText(user.getString("name"));
Why does it give me a NullPointer here, when the JSONObject works fine in the Toast?
As far as your code seems
You are accessing the first element of users object
You have to specify list view's first index as well,
you are accessing 1st element and passing to the whole listview
Please follow the basic programming of Listview
http://www.wingnity.com/blog/android-json-parsing-and-image-loading-tutorial/
Create a Bean class with getters and setters then populate the Bean with JSON data and use it as the data source to your listView adapter!
Related
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
I have a custom listView with 200 values coming from Cloud DB. All the values are received properly and added in ArrayList(hotelList). When I am passing to the ArrayList(hotelList) to Custom Adapter class, showing all the values as last element. Many stackOverflow questions found here related to this I tried them too. None worked for me. How should I fix this?
The code is,
#Override
protected void onPostExecute(String result) {
try {
Log.d("Inside Post", "message");
root = new JSONObject(result);
validation = root.getInt("response");
message = root.getString("message");
JSONArray data=root.getJSONArray("data");
data.length();
if(validation==1) {
hgs=new HotelGetSetter();//Getting and setting class
for(int i=0;i<data.length();i++){
hgs.setName(data.getJSONObject(i).getString("hotel_name"));
hgs.setPlace(data.getJSONObject(i).getString("city"));
hotelList.add(hgs);//While Debugging, all the 200 values from db received and added in the ArrayList(hotelList) values also there correctly
}
//But after the loop all the values here changed to last element value(debugged).
mainMethod();
}
}
catch (JSONException e) {
e.printStackTrace();
}
}
public void mainMethod(){
HotelCustomAdapter hca=new HotelCustomAdapter(HotelMaster.this,hotelList);
list_hotel.setAdapter(hca);
//list_hotel.setAdapter(new HotelCustomAdapter(this, hotel_id1,hotel_name1,hotel_place1));
}
you initialize variable hgs=new HotelGetSetter(); out of for loop and then you are always editing the same instance
You are changing the values for the same object hsg through the loop instead of creating different objects for each set of values. Therefore the 200 same objects.
Just move the hgs=new HotelGetSetter(); into the loop and it should work.
I have a structure made from ListView - Customised as seen in Image Below
I can add New Products on Click of AddProduct Button
A new Category gets added with EditText of Product and Quantity
On that I have a (+) and (-) button which adds subcategory for Enter Dealer and Quantity with remove button on it .
The structure has been good .
But the data maintenance on it difficult . Like when I scroll , the focus gets lost and the data goes else where on ListView .
Kindly suggest some code for this .
I suggest to you to change the navigation structure.
You can keep the delete feature on the listView maybe implementing the swipe as it happens for example in GMail.
I would remove the add button and use FAB (Floating action Button) as Material guide lines suggest.
Hope it will help you!
I have implemented this thing with the following structure.
First of all I have created Model class for maintain data of Product.
I am saving data in JSONArray with collection of JSONObject of quantity added. (I have used JSON because I can save easily as a String in Preference.)
In onCreate of Activity I am creating SparseArray of Saved Products from JSONArray like:
try {
JSONArray savedProdJSONArray = new JSONArray(productSharedPref.getString("productArray", new JSONArray().toString()));
Log.i(TAG, "Saved ARRAY Count : " + productSharedPref.getInt("count", -1));
for (int i = 0; i < savedProdJSONArray.length(); i++) {
ProductItems tempPItems = new ProductItems();
tempPItems.setProdId(savedProdJSONArray.getJSONObject(i).getInt("prod_id"));
tempPItems.setProdQty(savedProdJSONArray.getJSONObject(i).getDouble("qty"));
tempPItems.setProdName(savedProdJSONArray.getJSONObject(i).getString("name"));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
Log.e(TAG, "Json Err variant:"+e.getLocalizedMessage());
}
Now in getView() of Adapter I am checking with current ProductId and Saved ProductId is matched. If it is then fill up EditText with saved data like:
/** First check whether value of Saved product Array is >0 or not..*/
ProductItems savedProdTemp = prodItemsSavedList.get(holder.prodId, null);
if(savedProdTemp != null)
{
productQtyValue = savedProdTemp.getProdQty();
holder.prodQtyView.setText(""+productQtyValue);
} else {
holder.prodQtyView.setText("");
}
Here you also have to addTextChangeListner for EditText to save data in ArrayList and then you have to Convert in JSONArray when Activity is Stop or Pause
I think it is little bit confusing but I thought it is only helps you. I tried many times to find out solution but didn't better than this.
Hope it will help you.
Welcomes you if you have any question. (Sorry If you found English Mistake :D)
I'm unable to properly fetch a ParseObject that contains a field of type 'Object' : after changing manually the 'Object' field value in the Parse DataBrowser and then fetch the ParseObject from the app, the fetched ParseObject still provide the old value for the 'Object' field, but provide the right new value for the 'String' field.
Here is the sample code I use :
public class MainActivity extends ActionBarActivity {
ParseObject object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
object = ParseObject.createWithoutData("Test", "tvgTg8jAXz");
}
#Override
protected void onResume() {
super.onResume();
object.fetchInBackground().onSuccess(new Continuation<ParseObject, Object>() {
#Override
public Object then(Task<ParseObject> task) throws Exception {
JSONObject data = task.getResult().getJSONObject("data");
String name = task.getResult().getString("name");
Log.d("OBJECT", data.toString());
Log.d("OBJECT", name);
return null;
}
}).continueWith(new Continuation<Object, Object>() {
#Override
public Object then(Task<Object> task) throws Exception {
if (task.getError() != null) {
Log.e("OBJECT", task.getError().getLocalizedMessage());
}
return null;
}
});
}
}
After I change both 'data' and 'name' fields in the DataBrowser, if 'onResume()' is called without a previous call to 'onCreate()' (after locking/unlocking screen for example) then the logs shows the old value for 'data' and the new value for 'name'.
This is a simple code example to highlight the problem I encounter in a bigger project.
Is this a known issue of the Parse Android SDK ? Is there a workaround ?
Thanks
Now that I learned that you have turned on the local datastore I can come with an, at least partial, answer.
Turning on the local datastore has some side effects. One being that only one instance of each object exists locally. So when you call fetchInBackground the second time, object is already populated with data. The problem then (i think) is that the API no longer override 'complex' types (pointers, objects, arrays), perhaps because it could mess up internal relationships in the data store. Since the fact that the data store will recursively save an object (and pointers) so suddenly swapping a pointer might leave objects 'hanging'. (again, only guessing).
Now I must admit that it still confuses me a bit looking at your code, cause it does not seem that you at any point write your object to the data store, however..
What should work is to unpin the object before 'refreshing' it:
object.unpinInBackground.onSuccess(new Continuation<>{
...
// when done call fetch
});
According to Parse, this is a known issue that they will not fix for now : https://developers.facebook.com/bugs/1624269784474093/
We must use the following methods to retrieve JSON objects/arrays fields from a ParseObject :
getMap() instead of getJSONObject()
getList() instead of getJSONArray()
These methods will return Map and List objects respectively.
I found that managing Map and List in my project instead of JSONObjet and JSONArray is not a problem and is even clearer.
i made a listview with all the posts in the list.
what i want is when i click the child in the list i want another activity to be opened showing that specific post and the related comments
the question is how to know which item is clicked and how to show that particular post ParseObject in next activity
as they do in messaging app in which you click the message from the listview and subsequent messages are shown in the next activity
i might be very thankful to you if you solve this for me!!
Please Try this code:
Please implement your object class with Serializable
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View v, int position,
long arg3) {
try
{
Log.v("position",position); // hear is your list item position
MyClass obj = new MyClass(); // Class must be implements with Serializable
Intent showintent = new Intent(context,<activity class to open>);
showcontactintent.putExtra("obj",obj);
startActivity(showintent);
}
catch(Exception e)
{
e.printStackTrace();
}
}
});
Use: Relational Data
Objects can have relationships with other objects. To model this behavior, any ParseObject can be used as a value in other ParseObjects. Internally, the Parse framework will store the referred-to object in just one place, to maintain consistency.
For example, each Comment in a blogging app might correspond to one Post. To create a new Post with a single Comment, you could write:
// Create the post
ParseObject myPost = new ParseObject("Post");
myPost.put("title", "I'm Hungry");
myPost.put("content", "Where should we go for lunch?");
// Create the comment
ParseObject myComment = new ParseObject("Comment");
myComment.put("content", "Let's do Sushirrito.");
// Add a relation between the Post and Comment
myComment.put("parent", myPost);
// This will save both myPost and myComment
myComment.saveInBackground();
You can also link objects using just their objectIds like so:
// Add a relation between the Post with objectId "1zEcyElZ80" and the comment
myComment.put("parent", ParseObject.createWithoutData("Post", "1zEcyElZ80"));
By default, when fetching an object, related ParseObjects are not fetched. These objects' values cannot be retrieved until they have been fetched like so:
fetchedComment.getParseObject("post")
.fetchIfNeededInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject post, ParseException e) {
String title = post.getString("title");
// Do something with your new title variable
}
});
You can also model a many-to-many relation using the ParseRelation object. This works similar to List, except that you don't need to download all the ParseObjects in a relation at once. This allows ParseRelation to scale to many more objects than the List approach. For example, a User may have many Posts that they might like. In this case, you can store the set of Posts that a User likes using getRelation. In order to add a post to the list, the code would look something like:
ParseUser user = ParseUser.getCurrentUser();
ParseRelation<ParseObject> relation = user.getRelation("likes");
relation.add(post);
user.saveInBackground();
You can remove a post from the ParseRelation with something like:
relation.remove(post);
For more read: https://parse.com/docs/android/guide#objects-relational-data
^why did I copy all the words here instead of just providing the link? Because parse links are broken sometimes and doesn't direct you to the section you need (instead it just sends you to https://parse.com/docs/android/guide and because the doc is so large, you won't be able to find it.