Update value on adapter - android

I wonder if there is any way to update an information of a single item within a listview. Basically I press the button inside the adapter and it makes a new request, the request will set this returns the value of the adapter. It is a system of "like".
I do not want to call the asynchronous method that gets the list, it takes much again. The code to get all the items in the database is this:
protected ArrayList<Feed> doInBackground(MyTaskParams... params) {
page = params[0].page;
mFilter = params[0].filter;
backgroundItems = new ArrayList<Feed>();
ParseQuery<ParseObject> query = ParseQuery.getQuery("FeedPost");
ParseObject parseObject;
try {
responseList = query.find();
for (int i = 0; i < responseList.size(); i++) {
parseObject = responseList.get(i);
backgroundItems.add(new Feed(parseObject.getObjectId(),
parseObject.getString("Title"),
parseObject.getString("Description"),
parseObject.getString("CompleteText"),
parseObject.getString("imageURL"),
parseObject.getString("Link_on_Site"),
parseObject.getNumber("like_count")));
}
} catch (ParseException e) {
exceptionToBeThrown = e;
}
return backgroundItems;
}

You didn't show your adapter code or your task for updating the like count in the database, so I'm going to assume you're using an AsyncTask. In the getView or bindView function is where you need to update it.
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if (convertView == null)
{
//inflate layout and initialize holder
}
else
{
holder = (ViewHolder)convertView.getTag();
}
//get item at position
MyItem item = getItem(position);
if (item != null)
{
holder.likeButton.setOnClickListener(
new OnClickListener()
{
#Override
public void onClick(View v)
{
//update database and set new value
IncrementLikeCountTask task = new IncrementLikeCountTask()
{
#Override
public void onPostExecute(int newValue)
{
item.setLikeCount(newValue);
notifyDataSetChanged();
}
}.execute(position);
}
}
);
}
return convertView;
}

So I assume you're persisting a list of Feed objects in your adapter.
What you need to do is after communicating with the server modify the like_count field of the appropriate Feed object in your adapter and call notifyDataSetChanged() on the adapter.
This will trigger a refresh of the ListView and render your list item with the updated value.

Related

Modify custom BaseAdaptor position attribute in Android

I have an array of 30 items which i need to display in a listview. However, per my layout I'll be displaying 2 items side by side in one row of listview.
Since there are now going to be only 15 rows to display (30/2), how do i modify the position attribute of Adapter such that i see only 15 rows.
I tried doing position++, in getView and also modify getCount() to return 15, but that does not work either.
Rather than do it this way, a better method may be to simply count two items as one row. The getView() method returns a View that is a representation of each item returned by getItem(). In your case, each item contains two elements. So just put the logic in that would retrieve two elements at a time. May be easier to encapsulate them in a class like for example:
ArrayList<Row> mItems = new ArrayList<Row>();
private class Row {
Object obj1;
Object obj2;
}
public void addItem(Object obj) {
Row useRow;
if(mItems.isEmpty()) {
useRow = new Row();
} else {
useRow = mItems.get(mItems.size() - 1);
if(useRow.obj2 != null) {
useRow = new Row();
}
}
if(useRow.obj1 == null) {
useRow.obj1 = obj;
} else {
useRow.obj2 = obj;
}
mItems.add(useRow);
}
In this case, your BaseAdapter is backed by a List of Row objects. Each Row object contains two of your elements. Every time you add an element, you add it to the first, then to the second, else you create a new Row object and add it.
EDIT:
In order to have clickability, you'll have to implement an OnClickListener to each item's View. Something like this may work:
public interface ItemClickListener {
public void onItemClick(Object obj);
}
private ItemClickListener mClickListener;
public void setItemClickListener(ItemClickListener listener) {
mClickListener = listener;
}
#Override
public boolean areAllItemsEnabled() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewGroup root;
if(convertView == null) {
root = buildRootView();
View item1View = buildFirstView();
View item2View = buildSecondView();
...
item1View.setOnClickListener(mItemListener);
item2View.setOnClickListener(mItemListener);
...
// Put both Views in your top level root view if they are not there already
}
Row row = getItem(position);
item1View.setTag(row.obj1);
item2View.setTag(row.obj2);
}
private View.OnClickListener mItemListener = new View.OnClickListener {
#Override
public void onClick(View v) {
Object obj = (Object) v.getTag();
if(mClickListener != null) {
mClickListener.onItemClick(obj);
}
}
}
So basically, you disable the clicking by overriding "areAllItemsEnabled()" and "isEnabled()" to return "false". Then, the click listener in the adapter will activate each time the user clicks on a row. Since you put the Object of the row in the View's tag, you can retrieve it on click. It will be swapped to a new Object even when the ListView recycles because it calls getView() each time. Then create an object that inherits from the click interface to retrieve the object and do whatever you need.

iterate through ListView and delete list items based on their content

I have a ListView setup which I dynamically add items to with each item having different data. One of the pieces of data is a time/date which is shown in a TextView in my custom List item layout. In the onResume() method of my Fragment I would like to remove items that have a date shown which is before the current date.
The List item layout is as follows:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pi_rl"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/pi_tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp" />
<TextView
android:id="#+id/pi_tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/pi_tv_title"
android:textSize="24sp" />
</RelativeLayout>
The idea that I had to solve this was to create a loop, which iterated through all the items checking the content of the date TextView for each item. Then, if the date was before the current date, it would remove the item.
I am currently able to delete items based on there position, but I can't work out how to delete individual list items if they have a certain content.
This is what I have so far:
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
int items = DataModel.getInstance().getPendingItemList().size(); // Number of items in the list
if (items > 0) {
for (int i = 1; i <= items; i++) { // This loops through all my items. Do I want to use position instead?
Log.i("for loop", "" + i);
// This doesn't work because it tries to get the content for all the items not just one.
TextView tv_date = (TextView) getView()
.findViewById(R.id.pi_tv_date);
CharSequence c = tv_date.getText(); // Causes force close
// Here I will have my if(date shown != current date){
//deleteItem
//}
}
}
}
EDIT: This is my adapter code:
private class PendingAdapter extends BaseAdapter { // This happens whenever
// onCreate is called.
private List<Map<String, Object>> mPendingItemList;
public PendingAdapter() {
mPendingItemList = DataModel.getInstance().getPendingItemList();
}
#Override
public int getCount() {
return mPendingItemList.size();
}
#Override
public Object getItem(int position) {
return mPendingItemList.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = LayoutInflater.from(getActivity()).inflate(
R.layout.pending_item, null);
// Log.i("convertView", "was null");
}
TextView tv_title = (TextView) convertView
.findViewById(R.id.pi_tv_title);
TextView tv_date = (TextView) convertView
.findViewById(R.id.pi_tv_date);
HashMap<String, String> itemDataHashMap = (HashMap<String, String>) getItem(position);
tv_title.setText(itemDataHashMap.get("planet"));
tv_date.setText(itemDataHashMap.get("date"));
return convertView;
}
}
EDIT 2:
As per Ali AlNoaimi's answer, I have added this inside my Adapter:
public void refreshMyAdapter() {
if(mPendingItemList.size() > 0) {
List<Map<String, Object>> newPendingList = mPendingItemList;
for (int i = 0; i < mPendingItemList.size(); i++) {
Map<String, Object> item = mPendingItemList.get(i);
TextView tv_title = (TextView) histView
.findViewById(R.id.pi_tv_title);
String s_title;
s_title = tv_title.getText().toString();
Log.i("s_title", s_title);
if(s_title == "foo") {
newPendingList.remove(item);
}
}
mPendingItemList = newPendingList;
}
}
and this to the onResume():
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
final Handler handler = new Handler(); new Thread(new Runnable() {
#Override
public void run() {
while(true) {
handler.post(new Runnable() {
#Override
public void run() {
simpleAdpt.refreshMyAdapter();
simpleAdpt.notifyDataSetChanged();
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
This still does not work.
FINAL EDIT: Ali AlNoaimi's answer did work, I was just comparing strings incorrectly.
I hope this makes sense! Thank you
I'm not sure you understand how ListView works. A ListView is the visual representation of the data in your adapter, so you should never try to edit, add or delete views of a ListView directly, but instead edit the data and then call notifyDataSetChanged() on your adapter.
The ListView will then check for changes in your adapter's data and refresh the views accordingly.
For your specific problem, it means that you should work directly with mPendingItemList, find the items that have a specific content, remove them and then call notifyDataSetChanged().
Add this method to your adapter:
public void refreshMyAdapter() {
private List<Map<String, Object>> newPendingList;
for (int i = 0; i < mPendingItemList.size(); i++) {
Map<String, Object> item = mPendingItemList.get(i);
if(it matches) {
newPendingList.add(item);
}
}
mPendingItemList = newPendingList;
}
and in your main activity, fragment
final Handler handler = new Handler();
new Thread(new Runnable() {
#Override
public void run() {
while(true) {
handler.post(new Runnable() {
#Override
public void run() {
yourPendingAdapter.refreshMyAdapter();
yourPendingAdapter.notifyDataSetChanged();
}
});
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
Bedst way is to remove the items in the ArrayList you used when creating the adapter then call notifyDataSetChanged(); on the adabter to update the listview.
to have a coherent implementation, keep data processing on the DataModel.
add to DataModel class this method:
public void clearObsoleteItems(){
//TODO: add you implementation to clear the obsolete items from the List of items holded by the singleton class.
}
add on your adapter class PendingAdapter a setter for items list:
public void setPendingItemList(List<Map<String, Object>> mPendingItemList) {
this.mPendingItemList = mPendingItemList;
notifyDataSetChanged();
}
from the activity onResume call:
DataModel.getInstance().clearObsoleteItems();
mAdapter.setPendingItemList(DataModel.getInstance().getPendingItemList());

Refresh listView with multiple view types after delete/remove item

I have a ListView with a custom adapter that extends BaseAdapter and has 2 view types. When I run my adapter.removeRow(position) method, the data for the adapter is correctly updated, and the list reflects this, but the view types are not correctly updated. The Adapter is backed by
ArrayList<Map<String, String>> rows = new ArrayList<Map<String, String>>();
and I have a subset
List<Integer> flashSet = new ArrayList<Integer>();
which is a list of all the positions that are of ViewType 1 (as opposed to the standard view type 0).
Here is my adapter removeRow(position) method:
public void removeRow(int position) {
if (getItemViewType(position) == TYPE_FLASH) {
flashSet.remove(position);
}
for (int flashPosition:flashSet) {
System.out.println(tag+"is "+flashPosition+" going to be moved?");
if (flashPosition > position) {
flashPosition -= 1;
System.out.println(tag+"Yes! It's been moved to "+flashPosition);
}
}
rows.remove(position);
notifyDataSetChanged();
}
Here is my getView method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
FlashHolder flashHolder;
ClipHolder clipHolder;
int type = getItemViewType(position);
if (convertView == null) {
if (type == TYPE_CLIP) {
convertView = rowInflater.inflate(R.layout.clip_note_row_layout, null);
clipHolder = new ClipHolder();
flashHolder = null;
clipHolder.textView = (TextView)(convertView.findViewById(R.id.clip_text));
convertView.setTag(clipHolder);
} else {
convertView = rowInflater.inflate(R.layout.flash_row_layout, null);
clipHolder = null;
flashHolder = new FlashHolder();
flashHolder.front = (TextView)(convertView.findViewById(R.id.flash_text));
flashHolder.back = (TextView)(convertView.findViewById(R.id.clip_text));
convertView.setTag(flashHolder);
}
} else {
if (type == TYPE_CLIP) {
clipHolder = (ClipHolder)convertView.getTag();
flashHolder = null;
} else {
clipHolder = null;
flashHolder = (FlashHolder)convertView.getTag();
}
}
if (type == TYPE_CLIP) {
clipHolder.textView.setText(rows.get(position).get("clip"));
} else {
flashHolder.front.setText(rows.get(position).get("flash_text"));
flashHolder.back.setText(rows.get(position).get("clip"));
}
return convertView;
}
I know that I could create a new adapter, give it the updated ArrayList and call listView.setAdapter(adapter) but this seems total overkill when I'm simply trying to remove one item from a potentially long list. See pics for a before and after deleting:
Then I delete the first item. The word "which" was hidden behind the "Let's watch it" item and now the "inspired by…" item is hidden behind a blank item 3.
So, data is updating, view types aren't. Thanks for the help!
I figured it out. This will be useful to no one as I don't expect others to make the same mistake.
I naively thought that by doing this
for (int flashPosition:flashSet) {
System.out.println(tag+"is "+flashPosition+" going to be moved?");
if (flashPosition > position) {
flashPosition -= 1;
System.out.println(tag+"Yes! It's been moved to "+flashPosition);
}
}
I was changing the actual value stored in the List<Integer> flashSet = new ArrayList<Integer>();
In fact, I need to do the following instead:
for (int flashPosition:flashSet) {
System.out.println(tag+"is "+flashPosition+" going to be moved?");
if (flashPosition > position) {
flashSet.remove((Object)flashPosition);
flashPosition -= 1;
flashSet.add(flashPosition);
System.out.println(tag+"Yes! It's been moved to "+flashPosition);
}
}
Try this, After delete or add item you need to call adapter refresh.
Youradapter.notifyDataSetChanged();
use yourlistview.invalidateViews()
instead of
notifyDataSetChanged();
it works for me.

How to use notifyDataSetChanged

My project simplify as below:
First, I use application method Data.java to save data.
It contain the data:
private ArrayList<String> data = new ArrayList<String>();
public int getsize() {
return this.data.size();
}
public String getdata(int i) {
return this.data.get(i);
}
public void adddata(String s) {
return this.data.add(s);
}
My AActivity class onCreate as below:
Data d = (Data)this.getApplication();
String test = new String[d.getsize()];
for(i = 0; i < d.getsize(); i++) {
test[i] = d.getdata(i);
}
//to show in list
DataAdapter = new DataAdapter (this, test);
setListAdapter(DataAdapter);
And when button is click, startActivity the BActivity class.
In BActivity class, the code as below:
Data d = (Data)this.getApplication();
d.adddata("newdata");
finish();
And AActivity class onResume() as below:
#Override
public void onResume(){
super.onResume();
this.DataAdapter.notifyDataSetChanged();
}
But why the list is not update?
I confirm the data has be save.
My DataAdapter:
public DataAdapter(Context ctxt, String[] d) {
this.data = new String[d.length];
myInflater = LayoutInflater.from(ctxt);
int i;
for(i = 0; i < d.length; i++) {
data[i] = d[i];
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewTag viewTag;
if(convertView == null) {
convertView = myInflater.inflate(R.layout.row_bookmark_list, null);
viewTag = new ViewTag((TextView)convertView.findViewById(R.id.tv));
convertView.setTag(viewTag);
}
else {
viewTag = (ViewTag) convertView.getTag();
}
viewTag.tv.setText(data[position]);
}
class ViewTag {
TextView tv;
public ViewTag(TextView t) {
this.tv = t;
}
}
Add your new data directly to the adapter not to 'd'. The adapter keeps its own internal data which means that whatever changes you apply to your 'd' has no impact on the adapter.
For example:
List<String> itemsList = new ArrayList<String>();
ArrayAdapter aa = new ArrayAdapter(..., itemsList);
...
itemsList.add("new item"); --> wrong!
aa.notifyDataSetChanged(); --> nothing changes, you wrongly added the item to itemsList
you have to deal directly with the adapter:
aa.add("new item"); --> correct
aa.notifyDataSetChanged(); --> the adapter will reflect the change
You can't access the notifyDataSetChanged as a static method
( thats what you are doing in your example ).
If you have a ListActivity: you have access to the method getListAdapter().
Thats the right reference to your dataset.
So in short:
getListAdapter().notifyDataSetChanged();
will do the trick. If you don't have a ListActivity then you will have to find your listview thru View.findViewById([id of listview]) and get the Adapter there.
Hope this helps a bit :-)
I think your problem is that your DataAdapter is referenced to the array test, but test never changes. Referencing data to the DataAdapter instead of test should work.
OK, after looking at the Adapter code it will not work. Why are you copying your data? The adapter will never notice a change in the data element, because it is only working with a copy of that element at construction time. If copying the data is necessary, you should make sure the adapter updates its content, too.

Android: how to remove an item from a listView and arrayAdapter

I have a collection of items in an ArrayList. I add them to a customer adapter as follows:
this.m_adapter = new MyAdapter(this, R.layout.myitem,
itemCart.m_items);
I have a delete button for each of these items in my list, but I am not sure how to connect the delete button's onClick() with the original item in the ArrayList. Can someone please explain how to do this or point me to a tutorial where I can read up on this? Non-sarcastic/non-condescending responses are greatly appreciated.
You can call the remove() method on your ArrayList
itemCart.m_items.remove(<index of element to remove>);
this.m_adapter.notifyDataSetChanged();
And then you need to call notifyDataSetChanged(); on your adapter to update the ListView
You can get the index of the element by simply noticed that a list view is a collection of child views (the rows of the list).
You can do something like this in your code:
(inside the getView() method, for example)
row.setOnLongClickListener(new OnLongClickListener()
{
#Override
public boolean onLongClick(View view) {
remove(listView.indexOfChild(view));
return true;
}
}
That is, the solution is simply use indexOfChild(View) method to get index of child view that user (long) pressed.
Here's my solution so far:
In the getView() method I do something like this:
deleteButton.setTag(position);
It looks like getTag() returns an Object. So I converted the position int into an Integer object first. It appears to be working.
In the OnClickListener() I do the following:
items.remove(index.intValue());
So far, so good.
Following works for me:
/* Read values from resource into an array */
String[] strColorValues = getResources().getStringArray(R.array.colors);
ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < strColorValues.length; i++) {
list.add(strColorValues[i]);
}
ArrayAdapter adapterColors = new ArrayAdapter(getActivity(), android.R.layout.simple_spinner_item, list);
adapterColors.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerColors.setAdapter(adapterPermissionLevels);
spinnerColors.setOnItemSelectedListener(this);
/* Remove first element from the adapter and notify dataset changed. */
String item = spinnerColors.getItemAtPosition(0).toString();
adapterColors.remove(item);
adapterColors.notifyDataSetChanged();
Here's my Code.
transfer.setItemPosition(position, items.get(position).getAddMode());
the transfer here is the instance of the main class. everytime i click the deletebutton, it then pass the position of the that item on the list in this line.
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
final Context context = getContext();
LayoutInflater vi = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.listviewitem_layout, null);
}
ItemEntry item = items.get(position);
if (item != null) {
TextView textViewName = (TextView) v.findViewById(R.id.textViewItemName);
ImageView imageViewDelete = (ImageView) v.findViewById(R.id.imageViewDeleteIcon);
imageViewDelete.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
transfer.showDialog(4);
transfer.setItemPosition(position, items.get(position).getAddMode());
}
});
if (textViewName != null) {
textViewName.setText(item.getItemName());
}
if(imageViewDelete != null) {
imageViewDelete.setImageResource(R.drawable.delete);
}
}
return v;
}
}
Remove by position:
mainAdapter.remove(mainAdapter.getItem(position));
Such as the last one:
mainAdapter.remove(mainAdapter.getItem(mainAdapter.getCount() - 1));
Try these codes of lines it was very helpful for me
holder.image.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
list.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, list.size());
}
});
If you use context menu, then you can get
AdapterContextMenuInfo and this structure gives index and id of clicked element.
It seems that you can get the index (or position) of a clicked item in the ListView as follows:
listview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
listview.remove(listview.getItem(position).toString());
}
}
So you need to listen for clicks on Views and then take the index from that.

Categories

Resources