I have a ListView in which i am showing some data. I am requesting the data from the server which is in the form of JSON. Also, the data is paginated.
When the API is called for the first time it is loading n items.
I have implemented the logic that after the list end is reached API would be call again to fetch n more data.
API Calls are working fine as I have seen the result in the Logcat.
The Issue is the ListView is not updating properly on scroll after the API has been called for the second time.
Eg: Suppose I am calling API to fetch 7 items at a time. Then in the ListView i would see something like this:
Item1
Item2
..
Item7
Item1
Item2
....
JsonObjectRequest jo = new JsonObjectRequest(Request.Method.GET, url, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject jsonObject) {
pDialog.dismiss();
pDialog = null;
try {
JSONArray ja = jsonObject.getJSONArray("resultset"); // id, title, content, guid
for (int i = 0; i < ja.length(); i++) {
JSONObject jo = ja.getJSONObject(i);
SearchListItem ri = new SearchListItem();
ri.setId(jo.getInt("id"));
ri.setTitle(jo.getString("title"));
ri.setContent(jo.getString("content"));
listy.add(ri);
}
} catch (JSONException ex) {
Toast.makeText(getApplicationContext(), "json ex" + ex.getMessage(), Toast.LENGTH_SHORT).show();
ex.printStackTrace();
}
searchAdapter.notifyDataSetChanged();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
pDialog.dismiss();
pDialog = null;
Log.d(TAG, "!!!! ERROR " + volleyError.getMessage());
}
});
//Toast.makeText(ListActivity.this, jo.toString().toCharArray(), Toast.LENGTH_SHORT).show();
AppController.getInstance().addToRequestQueue(jo);
//Adapter
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
import com.o.R;
import com.o.SearchListItem;
import java.util.List;
public class SearchListAdapter extends BaseAdapter {
Context context;
List<SearchListItem> items;
//ImageLoader imageLoader = AppController.getInstance().getImageLoader();
public SearchListAdapter(Context context, List<SearchListItem> items)
{
this.context = context;
this.items = items;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return items.indexOf(getItem(position));
}
class ViewHolder
{
TextView txtTitle;
TextView txtContent;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);// creates the objects of all views
if(convertView == null)
{
convertView = inflater.inflate(R.layout.style_row, null);
holder = new ViewHolder();
holder.txtTitle = (TextView) convertView.findViewById(R.id.txtAbout);
holder.txtContent = (TextView) convertView.findViewById(R.id.txtDetail);
}
else
{
holder = (ViewHolder) convertView.getTag();
}
try {
SearchListItem rowItem = (SearchListItem) getItem(position);
holder.txtTitle.setText(rowItem.getTitle());
holder.txtContent.setText(rowItem.getContent().substring(0,20));
}
catch (Exception e){
//Toast.makeText(SearchListAdapter.this,e.printStackTrace(),Toast.LENGTH_SHORT).show();
}
return convertView;
}
}
You have not set the tag on the view , do convertview.setTag(holder) ...
if(convertView == null)
{
convertView = inflater.inflate(R.layout.style_row, null);
holder = new ViewHolder();
holder.txtTitle = (TextView) convertView.findViewById(R.id.txtAbout);
holder.txtContent = (TextView) convertView.findViewById(R.id.txtDetail);
convertView.setTag(holder)
}
Your code seems fine , problem might be that you are requesting multiple times from your code on the server and ArrayList listy getting filled multiple times in the onResponse method of jsonRequest or your server might be returning multiple entries , seems nothing wrong with the posted code.
Related
I am fetching JSON Array and trying to show it in a list view. I debugged and got that I recieve the array as required but when i run my list view is blank on android screen. When I run no error is shown in syntax or while in emulator. Here is my code:
I have used volley to call the api and fetch json data.
DashBoard.java
public class Dashboard extends AppCompatActivity {
private RequestQueue queue;
ListView listView;
ArrayList<rowitem> arrayList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
queue = Volley.newRequestQueue(this);
listView = (ListView) findViewById(R.id.myListView);
arrayList = new ArrayList<>();
String url = "https://api.rootnet.in/covid19-in/stats/latest";
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
// Display the first 500 characters of the response string.
try {
JSONObject result = new JSONObject(response).getJSONObject("data");
JSONArray jsonArray = result.getJSONArray("regional");
for(int i=0; i<jsonArray.length(); i++)
{
JSONObject json_data = jsonArray.getJSONObject(i);
String location = json_data.getString("loc");
String totalcase = json_data.getString("totalConfirmed");
String recovered = json_data.getString("discharged");
String deaths = json_data.getString("deaths");
rowitem model = new rowitem();
model.setLocation(location);
model.setTotalcase(totalcase);
model.setRecovered(recovered);
model.setDeaths(deaths);
arrayList.add(model);
}
} catch (JSONException e) {
Toast.makeText(Dashboard.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(Dashboard.this, "Server is not responding (Covid-19 Tracker)", Toast.LENGTH_LONG).show();
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
customlistviewadapter adapter = new customlistviewadapter(this, arrayList);
listView.setAdapter(adapter);
}
}
customlistviewadapter.java
package com.example.maps;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.View;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;
public class customlistviewadapter extends BaseAdapter {
Context context;
ArrayList<rowitem> arrayList;
public customlistviewadapter(Context context,ArrayList<rowitem> arrayList) {
this.context = context;
this.arrayList = arrayList;
}
#Override
public int getCount() {
return arrayList.size();
}
#Override
public Object getItem(int position) {
return arrayList.get(position);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.row, parent, false);
}
TextView location, totalcase, recovered, deaths;
location = (TextView) convertView.findViewById(R.id.location);
totalcase = (TextView) convertView.findViewById(R.id.cases);
recovered = (TextView) convertView.findViewById(R.id.healthy);
deaths = (TextView) convertView.findViewById(R.id.deaths);
location.setText(arrayList.get(position).getLocation());
totalcase.setText(arrayList.get(position).getTotalcase());
recovered.setText(arrayList.get(position).getRecovered());
deaths.setText(arrayList.get(position).getDeaths());
return convertView;
}
}
I'm trying to fill a ListView with a custom Adapter.
I want to use the NotifyDatasetChanged to refresh the layout, but it doesn't work.
I retrieve some JSON data from an HTTP request, and I manipulate the result string then I fill the ListView
What's wrong in my code?
UPDATE THE CODE (For the advice)
public class CalendarioFragment extends Fragment {
ListView listView;
ArrayList<Calendario> calenList;
CalendarioAdapter adapter;
String json;
ArrayList<String> arrayId = new ArrayList<String>();
ArrayList<String> arrayData = new ArrayList<String>();
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.calendario_layout, null);
listView = (ListView) view.findViewById(R.id.listCale);
listView.setEmptyView(view.findViewById(R.id.emptyElement));
calenList = new ArrayList<Calendario>();
adapter = new CalendarioAdapter(getActivity(), calenList);
listView.setAdapter(adapter);
execQuery("query", "0");
return view;
}
private void execQuery(final String query, final String taskId) {
final MyAsyncTask asyncTask = new MyAsyncTask(new AsyncResponse() {
#Override
public void onTaskCompleted(String output) {
if (output == null) {
Toast.makeText(getActivity(), "Nessuna connessione internet attiva!", Toast.LENGTH_SHORT).show();
} else {
json = output;
try {
ParseJson();
calenList.clear();
String[] ids = arrayId.toArray(new String[arrayId.size()]);
String[] date = arrayData.toArray(new String[arrayData.size()]);
for (int i = 0; i < ids.length; i++) {
Calendario calendario = new Calendario();
calendario.setId(ids[i]);
calendario.setData(date[i]);
calenList.add(calendario);
adapter.updateData(calenList);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}, getActivity());
asyncTask.execute(query, taskId);
}
private void ParseJson() throws JSONException {
JSONObject jsonObject = new JSONObject(json);
JSONArray jsonArray = jsonObject.getJSONArray("risposta");
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject JO = jsonArray.getJSONObject(i);
arrayId.add(JO.getString("ID"));
arrayData.add(JO.getString("DATA"));
}
}
}
This is the CustomAdapterCode:
import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;
public class CalendarioAdapter extends BaseAdapter {
private ArrayList listData;
private LayoutInflater layoutInflater;
final static String TAG = "sb.dl";
public CalendarioAdapter(Context context, ArrayList listData) {
Log.d(TAG, "CalendarioAdapter");
this.listData = listData;
layoutInflater = LayoutInflater.from(context);
}
#Override
public int getCount() {
return listData.size();
}
#Override
public Object getItem(int position) {
return listData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d(TAG, "CalendarioAdapter.getView");
ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.calendario_row_layout, null);
holder = new ViewHolder();
holder.edId = (TextView) convertView.findViewById(R.id.edId);
holder.edData = (TextView) convertView.findViewById(R.id.edData);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Calendario newsItem = (Calendario) listData.get(position);
holder.edId.setText(newsItem.getId());
holder.edData.setText(newsItem.getData());
return convertView;
}
static class ViewHolder {
TextView edId;
TextView edData;
}
public void updateData(ArrayList<Calendario> updatedData) {
listData = updatedData;
this.notifyDataSetChanged();
}
}
Problem:
listData is the variable inside of the Cale class. When you set new values to it the variable inly in Cale class is getting modified. The rest of the code where the duplicate of the data was set are not getting changed. In other words the data inside of the CalendarioAdapter is not updated when you are modifing the listData which is in Cale class.
Solution:
You need to pass the updated listData to adapter again after it was changed. In order to do that you need to create a method inside of your CalendarioAdapter adapter which will be responsible for updating the data inside of adapter and then calling notifyDataSetChanged().
Add this method to your CalendarioAdapter adapter:
public void updateData(ArrayList<Calendario> updatedData) {
listDataInYourAdapter = newData;
this.notifyDataSetChanged();
}
Now to use that method, in your Cale class replace this:
adapter.notifyDataSetChanged();
with this:
updateData(listData);
After these three lines
listData = new ArrayList<Calendario>();
adapter = new CalendarioAdapter(getActivity(), listData);
listView.setAdapter(adapter);
You should never re-assign listData. Once you do, the reference of the list within the adapter has become detached, and it no longer can be notified for updates.
So, instead, you need to clear(), then addAll() on the list.
For example,
ParseJson(); // You should really pass 'output' as a parameter here
listData = getListData(); // Don't re-assign
Do this instead, then notify
ParseJson();
listData.clear();
listData.addAll(getListData());
Alternatively, why call listData.addAll(getListData());? You already have this method which does the correct thing of clearing, adding, and notifying.
private ArrayList<Calendario> getListData() {
listData.clear(); // Cleared
String[] ids = arrayId.toArray(new String[arrayId.size()]);
String[] date = arrayData.toArray(new String[arrayData.size()]);
for (int i = 0; i < ids.length; i++) {
Calendario calendario = new Calendario();
calendario.setId(ids[i]);
calendario.setData(date[i]);
listData.add(calendario); // Adding
}
adapter.notifyDataSetChanged(); // Notify
return listData;
}
So, realistically, you only need these two lines in the AsyncTask (and again, should add output as a parameter of ParseJson, you don't need to save the json = output).
ParseJson();
getListData();
I solved!
That was a stupid thing!
In parseJson sub, I need to clear the arrays before to fill them!
private void ParseJson() throws JSONException {
JSONObject jsonObject = new JSONObject(json);
JSONArray jsonArray = jsonObject.getJSONArray("risposta");
arrayId.clear();
arrayData.clear();
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject JO = jsonArray.getJSONObject(i);
arrayId.add(JO.getString("ID"));
arrayData.add(JO.getString("DATA"));
}
}
Reassign the list again-> this is not recommended behavior.
Here is the correct way how to do it:
Inside the adapter: public void update(List<ItemMessage> updatedList){
list.clear();
list.addAll(updatedList);
notifyDataSetChanged();}
Don't forget to do it using a Handler
I have been trying to learn how to make custom ArrayAdapters to use in some of my Android apps by using this tutorial, but adapting it slightly so that I could fit it with my own application.
I've tested it a couple times now on my phone, but I've found the performance speed to be incredibly slow (when loading and scrolling through the listview). The other activities which do not use this custom ArrayAdapter have a normal performance speed.
I'm not really sure what the problem could be or where in my code it would be, so below, I've posted all of my custom ArrayAdapter class:
package com.mycompany.myapp;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.ArrayList;
public class CustomArrayAdapter extends ArrayAdapter<String> {
private static class ViewHolder {
TextView tv_Id;
TextView tv_Name;
TextView tv_Group;
}
private Context context;
private ArrayList<String> arr_items;
public CustomArrayAdapter(Context context, ArrayList<String> arr_items) {
super(context, R.layout.listview_advanced, arr_items);
this.context = context;
this.arr_items = arr_items;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Person person = new Person(context, arr_items.get(position));
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // View lookup cache stored in tag
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.listview_advanced, parent, false);
viewHolder.tv_Id = (TextView) convertView.findViewById(R.id.lvAdv_text1);
viewHolder.tv_Name = (TextView) convertView.findViewById(R.id.lvAdv_text2);
viewHolder.tv_Group = (TextView) convertView.findViewById(R.id.lvAdv_text3);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
// Populate the data into the template view using the data object
viewHolder.tv_Id.setText(person.getIDAsString());
viewHolder.tv_Name.setText(person.getName());
if (person.getGroup().equals("")) {
viewHolder.tv_Group.setText("");
} else {
viewHolder.tv_Group.setText("(" + person.getGroup() + ")");
}
// Return the completed view to render on screen
return convertView;
}
}
Any help would be much appreciated. Thanks.
UPDATE:
Also, before calling the CustomArrayAdapter, I add data to an ArrayList<String> by going through rows of a .csv file and getting that data. At the moment, when reading the .csv file, I have this:
...
ArrayList<String> arr_person = new ArrayList<>(); // Global variable
...
// In a method:
String data = inputStream.nextLine();
String[] line = data.split(",");
if (line.length >1) {
arr_person.add(line[1]);
}
...
CustomArrayAdapter adapter = new CustomArrayAdapter(getActivity(), arr_person);
lv_main.setAdapter(adapter);
How would I adapt this for objects?
UPDATE 2:
My Person object works like this:
private Context context;
private String person, group, someAttribute, ... ;
private int idNumber, scoreOne, scoreTwo, scoreThree, scoreFour, scoreFive, scoreSix, scoreTotal, ... ;
private double ... ;
public Person(Context context, String person) {
this.context = context;
this.person = person;
loadInformation();
}
private void loadInformation() {
InputStreamReader inputStreamReader;
try {
inputStreamReader = new InputStreamReader(context.getAssets().open("PersonsList.csv"));
Scanner inputStream = new Scanner(inputStreamReader);
inputStream.nextLine(); // Ignores the first line
while (inputStream.hasNext()) {
String data = inputStream.nextLine(); // Gets a whole line
String[] line = data.split(","); // Splits the line up into a string array
if (line.length > 1) {
if (line[1].equals(person)) {
idNumber = Integer.parseInt(line[0]);
person = line[1];
group = line[2];
someAttribute = line[3];
scoreOne = Integer.parseInt(line[4]);
scoreTwo = Integer.parseInt(line[5]);
scoreThree = Integer.parseInt(line[6]);
scoreFour= Integer.parseInt(line[7]);
scoreFive = Integer.parseInt(line[8]);
scoreSix = Integer.parseInt(line[9]);
scoreTotal = scoreOne + scoreTwo + scoreThree + scoreFour + scoreFive + scoreSix;
// Same code pattern for defining about 10 more attributes
}
}
}
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public int getID() {
return idNumber;
}
public String getIDAsString() {
return format(idNumber);
}
private String format(int number) {
String str_num = String.valueOf(number);
switch (str_num.length()) {
case 1:
str_num = "00" + str_num;
break;
case 2:
str_num = "0" + str_num;
break;
case 3:
// Leave it how it is;
break;
}
return str_num;
}
public String getName() {
return person;
}
public String getGroup() {
return group;
}
public String getSomeAttribute() {
return someAttribute;
}
public int getScoreOne() {
return scoreOne;
}
public int getScoreTwo() {
return scoreTwo;
}
...
Base your array and ArrayAdapter on Person instead of String and make a list of Persons before you set up the adapter. This way you only run the Person constructor once instead of every time you display its view.
ArrayList<Person> arr_person = new ArrayList<>(); // Global variable
...
String data = inputStream.nextLine();
String[] line = data.split(",");
if (line.length > 1) {
Person person = new Person(context, line[1]);
arr_person.add(person);
}
...
CustomArrayAdapter adapter = new CustomArrayAdapter(getActivity(), arr_person);
lv_main.setAdapter(adapter);
...
package com.mycompany.myapp;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.ArrayList;
public class CustomArrayAdapter extends ArrayAdapter<Person> {
private static class ViewHolder {
TextView tv_Id;
TextView tv_Name;
TextView tv_Group;
}
private Context context;
private ArrayList<Person> persons;
public CustomArrayAdapter(Context context, ArrayList<Person> persons) {
super(context, R.layout.listview_advanced, arr_items);
this.context = context;
this.persons = persons;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
Person person = persons.get(position);
// Check if an existing view is being reused, otherwise inflate the view
ViewHolder viewHolder; // View lookup cache stored in tag
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.listview_advanced, parent, false);
viewHolder.tv_Id = (TextView) convertView.findViewById(R.id.lvAdv_text1);
viewHolder.tv_Name = (TextView) convertView.findViewById(R.id.lvAdv_text2);
viewHolder.tv_Group = (TextView) convertView.findViewById(R.id.lvAdv_text3);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
// Populate the data into the template view using the data object
viewHolder.tv_Id.setText(person.getIDAsString());
viewHolder.tv_Name.setText(person.getName());
if (person.getGroup().equals("")) {
viewHolder.tv_Group.setText("");
} else {
viewHolder.tv_Group.setText("(" + person.getGroup() + ")");
}
// Return the completed view to render on screen
return convertView;
}
}
By getting data from server displaying custom list-view. List item is having one button and one textview. Here i'm trying to display another listview with selected listview data.Here selection is done when when user click on button in list item.I'have done upto getting selected data from first Listview and storing it in arraylist.after this how to use this arraylist(Which is present in adapter class) and display a listview below the first listview.How to proceed.Here is my firstlistview adapter class
package com.spatel.slantright.adapter;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.parse.FindCallback;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseUser;
import com.parse.SaveCallback;
import com.spatel.slantright.R;
import com.spatel.slantright.model.ManagerModel;
import com.spatel.slantright.model.UserDetials;
public class ManagersAdapter extends BaseAdapter {
Context context;
private List<UserDetials> rowItem = null;
private List<UserDetials> notFollowing = null;
LayoutInflater mInflater, notFollowinflater;
boolean state[] = { true, true, true, true };
public ManagersAdapter(Context context, List<UserDetials> alFollowings) {
this.context = context;
this.rowItem = alFollowings;
this.mInflater = (LayoutInflater) context
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
}
/* private view holder class */
private class ViewHolder {
ImageView ivBlack;
TextView tvUsername;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final UserDetials row_pos;
row_pos = rowItem.get(position);
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.row_managers, null);
holder = new ViewHolder();
holder.ivBlack = (ImageView) convertView.findViewById(R.id.ivBlack);
holder.tvUsername = (TextView) convertView
.findViewById(R.id.tvUsername);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
// setting the image resource and title
//holder.ivBlack.setImageResource(rowItem.get(position).getIcon());
holder.tvUsername.setText(rowItem.get(position).getUserName());
holder.ivBlack.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (state[position]) {
row_pos.setIcon(R.drawable.black_circle);
ParseQuery<ParseObject> query = new ParseQuery<ParseObject>(
"UserDetails");
query.whereEqualTo("userId", ParseUser.getCurrentUser()
.getObjectId().toString());
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> object,
ParseException e) {
ParseObject objects = object.get(0);
if (e == null) {
try {
JSONArray jArray = objects
.getJSONArray("followings");
JSONArray notfollowingJArray = new JSONArray();
final JSONArray followingJArray = new JSONArray();
if (jArray != null && jArray.length() > 0) {
// alFollowings = new
// ArrayList<UserDetials>();
for (int i = 0; i < jArray.length(); i++) {
// Excluding the item at position
System.out
.println("POSITION ::::::::::::::::"
+ position);
if (i != position) {
followingJArray.put(jArray.get(i));
}else {
System.out
.println("RRRRRRRRRRRRRRRRRRRRROOOOOOOOOOOOOOOOOOOOWWWWWWWWWWWWWWW" + rowItem.get(i).getUserName().toString());
followingJArray.put(jArray.get(i));
ArrayList<String> listdata = new ArrayList<String>();
if (jArray != null) {
for (int j=0;j<jArray.length();j++){
listdata.add(jArray.get(j).toString());
}
}
}
ParseQuery<ParseObject> query = new ParseQuery<ParseObject>(
"UserDetails");
query.whereEqualTo("userId", ParseUser.getCurrentUser()
.getObjectId().toString());
query.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> object,
ParseException e) {
ParseObject objects = object.get(0);
objects.put(
"followings",
followingJArray);
objects
.saveInBackground();
}
});
}
}
} catch (JSONException e1) {
e1.printStackTrace();
}
}
}
});
state[position] = false;
rowItem.remove(position);
notifyDataSetChanged();
} else if (!state[position]) {
row_pos.setIcon(R.drawable.black_circle);
state[position] = true;
notifyDataSetChanged();
}
}
});
return convertView;
}
#Override
public int getCount() {
return rowItem.size();
}
#Override
public Object getItem(int position) {
return rowItem.get(position);
}
#Override
public long getItemId(int position) {
return rowItem.indexOf(getItem(position));
}
}
The short version of my question: If I have a listview where each item has a button and that button causes a textview on that listitem to be updated, how do I make changes I make to the list row from a onClickListener in getView permanent?
Detailed description of problem: I have a listview and that listview has a "bump" button (equivalent to a "like" button on Facebook) and when a user presses this like button 3 things happen:
A async request is made to my api which records the "bump" by adding a record to one of my db tables. If the request is successful the api issues a response with 2 fields ( is_bumped(boolean) & bump_count(int) )
If the api request was successful then we grab bumpCount from the response and use it to update the bumpTv to reflect the new total number of times that list item has been "bumped".... example: "3 Bumps"
If the API request was successful and isBumped=true then we update the image resource to a version of my bump icon that looks selected/pressed.
All of this works just fine at first glance, but if you "bump" a list item and then scroll all the way to the bottom of the list and then back to the top, the list item you just bumped will no longer appear to be bumped unless you refresh the entire activity. I know this has to have something to do with the data that I bind to the adapter not being updated, but how do I go about updating without refreshing the entire update?
package com.quothor.helpers;
import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
public class NewLazyAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<HashMap<String, String>> data;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
public FragmentManager frag_manager;
static class ViewHolder {
TextView name;
TextView div3;
TextView div2;
TextView bumpTv;
TextView message;
TextView commentsTv;
SmartImageView thumb_image;
ImageButton bumpBtn;
ImageButton requestBtn;
ImageButton settingsBtn;
TextView created ;
int position;
}
public NewLazyAdapter(Activity a, ArrayList<HashMap<String, String>> d) {
activity = a;
data=d;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public NewLazyAdapter(Activity a, ArrayList<HashMap<String, String>> d, FragmentManager manager) {
activity = a;
frag_manager=manager;
data=d;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
HashMap<String, String> update = new HashMap<String, String>();
update = data.get(position);
if(convertView==null){
convertView = inflater.inflate(R.layout.list_row, null);
holder = new ViewHolder();
holder.name = (TextView)convertView.findViewById(R.id.name); // title
holder.div3 = (TextView)convertView.findViewById(R.id.divider3); // title
holder.div2 = (TextView)convertView.findViewById(R.id.divider2); // title
holder.bumpTv = (TextView)convertView.findViewById(R.id.bump); // title
holder.message = (TextView)convertView.findViewById(R.id.message); // artist name
holder.commentsTv = (TextView)convertView.findViewById(R.id.comments); // artist name
holder.thumb_image = (SmartImageView) convertView.findViewById(R.id.list_image);
holder.bumpBtn= (ImageButton)convertView.findViewById(R.id.bump_btn);
holder.requestBtn = (ImageButton)convertView.findViewById(R.id.hidden_btn);
holder.settingsBtn = (ImageButton)convertView.findViewById(R.id.settings_btn);
holder.created = (TextView)convertView.findViewById(R.id.created); // duration
holder.bumpBtn.setTag(holder);
holder.bumpBtn.setOnClickListener(new Bump(position, update));
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Log.i("LazyAdapter data", String.valueOf(position)+" "+update.toString());
if(update.get("bump_count") != null){
holder.bumpBtn.setVisibility(holder.bumpBtn.VISIBLE);
//holder.bumpBtn.setOnClickListener(new Bump(position, update));
String bump_count=update.get("bump_count");
String is_bumped=update.get("is_bumped");
//sets bump textview
if(bump_count.equals("0")){
}else if(bump_count.equals("1")){
holder.div3.setVisibility(holder.div3.VISIBLE);
holder.bumpTv.setVisibility(holder.bumpTv.VISIBLE);
holder.bumpBtn.setVisibility(holder.bumpBtn.VISIBLE);
holder.bumpTv.setText(bump_count+" bump");
}else{
holder.div3.setVisibility(holder.div3.VISIBLE);
holder.bumpTv.setVisibility(holder.bumpTv.VISIBLE);
holder.bumpBtn.setVisibility(holder.bumpBtn.VISIBLE);
holder.bumpTv.setText(bump_count+" bumps");
}
if(is_bumped.equals("true")){
holder.bumpBtn.setImageResource(R.drawable.quothor_thumb_blue);
//bumpBtn.setBackgroundResource(R.drawable.quothor_bump_btn_bg_black);
}else{
holder.bumpBtn.setImageResource(R.drawable.quothor_bump_icon_black);
//bumpBtn.setBackgroundResource(android.R.drawable.btn_default);
}
}
if(update.get("relationship_view")!=null){
if(update.get("uid")!=TabHostFragmentActivity.loggedin_uid){
if(update.get("relation_to_user")!=null){
holder.requestBtn.setVisibility(holder.requestBtn.VISIBLE);
String relation= update.get("relation_to_user");
if(relation.equals("Friend")){
holder.settingsBtn.setVisibility(holder.settingsBtn.VISIBLE);
holder.requestBtn.setImageResource(R.drawable.friend_btn);
}else if(relation.equals("Familiar")){
holder.settingsBtn.setVisibility(holder.settingsBtn.VISIBLE);
holder.requestBtn.setImageResource(R.drawable.familiar_btn);
}
holder.requestBtn.setOnClickListener(new myOnClickListener(position));
holder.settingsBtn.setOnClickListener(new myOnClickListener(position));
}
}
}
if(update.get("created") != null){
TextView created = (TextView)convertView.findViewById(R.id.created); // duration
String str_created=update.get("created");
long created_l = Long.parseLong(str_created);
String time_ago=TimeAgo.fromPhpTime(created_l);
created.setVisibility(convertView.VISIBLE);
created.setText(time_ago);
}
if(update.get("comment_count")!=null){
holder.div2.setVisibility(holder.div2.VISIBLE);
holder.commentsTv.setVisibility(holder.commentsTv.VISIBLE);
String comments = update.get("comment_count");
if(comments.equals("0")){
holder.commentsTv.setText("no comments");
}else if(comments.equals("1")){
holder.commentsTv.setText("1 comment");
}else{
holder.commentsTv.setText(comments+ " comments");
}
}else{
holder.commentsTv.setVisibility(holder.commentsTv.INVISIBLE);
}
// Setting all values in listview
holder.name.setText(update.get("name"));
if(update.get("message") != null){
holder.message.setText(update.get("message"));
}else{
holder.message.setVisibility(holder.message.INVISIBLE);
}
holder.thumb_image.setImageUrl(update.get("thumb_img"));
/*
name.setOnClickListener(new myOnClickListener(position));
thumb_image.setOnClickListener(new myOnClickListener(position));
*/
return convertView;
}
public class myOnClickListener implements OnClickListener{
private int position;
private String clicked_uid;
public myOnClickListener(int position){
this.position=position;
}
#Override
public void onClick(View v) {
HashMap<String, String> update = new HashMap<String, String>();
update = data.get(position);
Log.i("Update Position:", update.toString());
clicked_uid=update.get("uid");
Log.d("Clicked UID:", clicked_uid+"");
String relation= update.get("relation_to_user");
String uid = update.get("uid");
String name = update.get("name");
String thumb_img = update.get("thumb_img");
FragmentManager fm = frag_manager;
EditRelationshipDialog editRelationshipDialog = new EditRelationshipDialog().newInstance(uid,relation,name,thumb_img);
editRelationshipDialog.show(fm, "relationshipsdialog");
}
}
public class Bump implements OnClickListener{
private int position;
private String clicked_uid;
public Bump(int position, HashMap<String, String> update){
this.position=position;
}
#Override
public void onClick(View v) {
HashMap<String, String> update = new HashMap<String, String>();
update = data.get(position);
final View theview=v;
Log.i("Update Position:", update.toString());
String msg_id=update.get("msg_id");
//ViewHolder mH = (ViewHolder) theview.getTag();
// mH.message.setText("clicked");
RequestParams params = new RequestParams();
params.put("msg_id", msg_id);
params.put("loggedin_uid", TabHostFragmentActivity.loggedin_uid);
RestClient.post(TabHostFragmentActivity.token,"http://api/bump", params, new JsonHttpResponseHandler() {
#Override
public void onFailure(Throwable arg0, JSONObject arg1) {
// TODO Auto-generated method stub
super.onFailure(arg0, arg1);
Log.i("bump request failed in lazy adapter", arg1.toString());
Toast.makeText(activity.getApplicationContext(), arg1.toString() , Toast.LENGTH_LONG).show();
}
#Override
public void onSuccess(JSONObject json) {
ViewHolder mH = (ViewHolder) theview.getTag();
try {
String is_bumped=json.getString("is_bumped");
String bump_count=json.getString("bump_count");
if(bump_count != null){
if(bump_count.equals("0")){
}else if(bump_count.equals("1")){
mH.div3.setVisibility(mH.div3.VISIBLE);
mH.bumpTv.setVisibility(mH.bumpTv.VISIBLE);
mH.bumpBtn.setVisibility(mH.bumpBtn.VISIBLE);
mH.bumpTv.setText(bump_count+" bump");
}else{
mH.div3.setVisibility(mH.div3.VISIBLE);
mH.bumpTv.setVisibility(mH.bumpTv.VISIBLE);
mH.bumpBtn.setVisibility(mH.bumpBtn.VISIBLE);
mH.bumpTv.setText(bump_count+" bumps");
}
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
String is_bumped=json.getString("is_bumped");
if(is_bumped.equals("true")){
mH.bumpBtn.setImageResource(R.drawable.quothor_thumb_blue);
//bumpBtn.setBackgroundResource(R.drawable.quothor_bump_btn_bg_black);
}else{
mH.bumpBtn.setImageResource(R.drawable.quothor_bump_icon_black);
//bumpBtn.setBackgroundResource(android.R.drawable.btn_default);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
}
EDIT: After taking the advice in the comments i was sure my list would begin functioning the way I had intended it to , but it only opened up a whole new can of hell....now for some reason if a user clicks the "bump" button anywhere in the first 6 items it works just fine, but beyond that something weird starts happening. when a user hits the bump button on one of the list items below the first 6 items it sends the wrong position and its a position somewhere between 0 and 6 ?!?!?
package com.quothor.helpers;
import java.util.ArrayList;
import java.util.HashMap;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
public class NewLazyAdapter extends BaseAdapter {
private Activity activity;
private ArrayList<HashMap<String, String>> data;
private static LayoutInflater inflater=null;
public ImageLoader imageLoader;
public FragmentManager frag_manager;
static class ViewHolder {
TextView name;
TextView div3;
TextView div2;
TextView bumpTv;
TextView message;
TextView commentsTv;
SmartImageView thumb_image;
ImageButton bumpBtn;
ImageButton requestBtn;
ImageButton settingsBtn;
TextView created ;
int position;
}
public NewLazyAdapter(Activity a, ArrayList<HashMap<String, String>> d) {
activity = a;
data=d;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public NewLazyAdapter(Activity a, ArrayList<HashMap<String, String>> d, FragmentManager manager) {
activity = a;
frag_manager=manager;
data=d;
inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
imageLoader=new ImageLoader(activity.getApplicationContext());
}
public int getCount() {
return data.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
HashMap<String, String> update = new HashMap<String, String>();
update = data.get(position);
Log.i("position being scrolled over", String.valueOf(position));
if(convertView==null){
convertView = inflater.inflate(R.layout.list_row, null);
holder = new ViewHolder();
holder.name = (TextView)convertView.findViewById(R.id.name); // title
holder.div3 = (TextView)convertView.findViewById(R.id.divider3); // title
holder.div2 = (TextView)convertView.findViewById(R.id.divider2); // title
holder.bumpTv = (TextView)convertView.findViewById(R.id.bump); // title
holder.message = (TextView)convertView.findViewById(R.id.message); // artist name
holder.commentsTv = (TextView)convertView.findViewById(R.id.comments); // artist name
holder.thumb_image = (SmartImageView) convertView.findViewById(R.id.list_image);
holder.bumpBtn= (ImageButton)convertView.findViewById(R.id.bump_btn);
holder.requestBtn = (ImageButton)convertView.findViewById(R.id.hidden_btn);
holder.settingsBtn = (ImageButton)convertView.findViewById(R.id.settings_btn);
holder.created = (TextView)convertView.findViewById(R.id.created); // duration
holder.bumpBtn.setTag(holder);
holder.bumpBtn.setOnClickListener(new Bump(position, update));
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Log.i("LazyAdapter data", String.valueOf(position)+" "+update.toString());
if(update.get("bump_count") != null){
holder.bumpBtn.setVisibility(holder.bumpBtn.VISIBLE);
//holder.bumpBtn.setOnClickListener(new Bump(position, update));
String bump_count=update.get("bump_count");
String is_bumped=update.get("is_bumped");
//sets bump textview
if(bump_count.equals("0")){
}else if(bump_count.equals("1")){
holder.div3.setVisibility(holder.div3.VISIBLE);
holder.bumpTv.setVisibility(holder.bumpTv.VISIBLE);
holder.bumpBtn.setVisibility(holder.bumpBtn.VISIBLE);
holder.bumpTv.setText(bump_count+" bump");
}else{
holder.div3.setVisibility(holder.div3.VISIBLE);
holder.bumpTv.setVisibility(holder.bumpTv.VISIBLE);
holder.bumpBtn.setVisibility(holder.bumpBtn.VISIBLE);
holder.bumpTv.setText(bump_count+" bumps");
}
if(is_bumped.equals("true")){
holder.bumpBtn.setImageResource(R.drawable.quothor_thumb_blue);
//bumpBtn.setBackgroundResource(R.drawable.quothor_bump_btn_bg_black);
}else{
holder.bumpBtn.setImageResource(R.drawable.quothor_bump_icon_black);
//bumpBtn.setBackgroundResource(android.R.drawable.btn_default);
}
}
if(update.get("relationship_view")!=null){
if(update.get("uid")!=TabHostFragmentActivity.loggedin_uid){
if(update.get("relation_to_user")!=null){
holder.requestBtn.setVisibility(holder.requestBtn.VISIBLE);
String relation= update.get("relation_to_user");
if(relation.equals("Friend")){
holder.settingsBtn.setVisibility(holder.settingsBtn.VISIBLE);
holder.requestBtn.setImageResource(R.drawable.friend_btn);
}else if(relation.equals("Familiar")){
holder.settingsBtn.setVisibility(holder.settingsBtn.VISIBLE);
holder.requestBtn.setImageResource(R.drawable.familiar_btn);
}
holder.requestBtn.setOnClickListener(new myOnClickListener(position));
holder.settingsBtn.setOnClickListener(new myOnClickListener(position));
}
}
}
if(update.get("created") != null){
TextView created = (TextView)convertView.findViewById(R.id.created); // duration
String str_created=update.get("created");
long created_l = Long.parseLong(str_created);
String time_ago=TimeAgo.fromPhpTime(created_l);
created.setVisibility(convertView.VISIBLE);
created.setText(time_ago);
}
if(update.get("comment_count")!=null){
holder.div2.setVisibility(holder.div2.VISIBLE);
holder.commentsTv.setVisibility(holder.commentsTv.VISIBLE);
String comments = update.get("comment_count");
if(comments.equals("0")){
holder.commentsTv.setText("no comments");
}else if(comments.equals("1")){
holder.commentsTv.setText("1 comment");
}else{
holder.commentsTv.setText(comments+ " comments");
}
}else{
holder.commentsTv.setVisibility(holder.commentsTv.INVISIBLE);
}
// Setting all values in listview
holder.name.setText(update.get("msg_id"));
if(update.get("message") != null){
holder.message.setText(update.get("message"));
}else{
holder.message.setVisibility(holder.message.INVISIBLE);
}
holder.thumb_image.setImageUrl(update.get("thumb_img"));
/*
name.setOnClickListener(new myOnClickListener(position));
thumb_image.setOnClickListener(new myOnClickListener(position));
*/
return convertView;
}
public class myOnClickListener implements OnClickListener{
private int position;
private String clicked_uid;
public myOnClickListener(int position){
this.position=position;
}
#Override
public void onClick(View v) {
HashMap<String, String> update = new HashMap<String, String>();
update = data.get(position);
Log.i("Update Position:", update.toString());
clicked_uid=update.get("uid");
Log.d("Clicked UID:", clicked_uid+"");
String relation= update.get("relation_to_user");
String uid = update.get("uid");
String name = update.get("name");
String thumb_img = update.get("thumb_img");
FragmentManager fm = frag_manager;
EditRelationshipDialog editRelationshipDialog = new EditRelationshipDialog().newInstance(uid,relation,name,thumb_img);
editRelationshipDialog.show(fm, "relationshipsdialog");
}
}
public class Bump implements OnClickListener{
private int position;
private String clicked_uid;
public Bump(int position, HashMap<String, String> update){
this.position=position;
}
#Override
public void onClick(View v) {
HashMap<String, String> update = new HashMap<String, String>();
update = data.get(position);
final View theview=v;
Log.i("POSITION BEING CLICKED",String.valueOf(position));
Log.i("Update Position:", update.toString());
String msg_id=update.get("msg_id");
Log.i("msg_id",msg_id);
//ViewHolder mH = (ViewHolder) theview.getTag();
// mH.message.setText("clicked");
RequestParams params = new RequestParams();
params.put("msg_id", msg_id);
params.put("loggedin_uid", TabHostFragmentActivity.loggedin_uid);
RestClient.post(TabHostFragmentActivity.token,"http://api/content/bump", params, new JsonHttpResponseHandler() {
#Override
public void onFailure(Throwable arg0, JSONObject arg1) {
// TODO Auto-generated method stub
super.onFailure(arg0, arg1);
Log.i("bump request failed in lazy adapter", arg1.toString());
Toast.makeText(activity.getApplicationContext(), arg1.toString() , Toast.LENGTH_LONG).show();
}
#Override
public void onSuccess(JSONObject json) {
ViewHolder mH = (ViewHolder) theview.getTag();
HashMap<String, String> latestUpdate = new HashMap<String, String>();
latestUpdate = data.get(position);
Log.i("list item being edited", latestUpdate.toString());
try {
String bump_count=json.getString("bump_count");
if(bump_count != null){
latestUpdate.put("bump_count", bump_count);
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
String is_bumped=json.getString("is_bumped");
latestUpdate.put("is_bumped", is_bumped);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
data.remove(position);
data.add(position, latestUpdate);
notifyDataSetChanged();
}
});
}
}
}
In the question's comments, it was established that even though the UI was being updated (based on user interaction), the underlying data used by adapter's getView() weren't.
Solution: Update the data source and call notifyDataSetChanged() on the adapter. Let getView() handle UI updates.
Read through the comments for more information.