I have a listView which build up using a custom ListAdapter. I need to access to all the child item in list view in order to get all the text of TextView in in each item in ListView.
private void calcTotalSales ()
{
View v;
TextView tx;
String abc="";
ListView myList = (ListView) findViewById(R.id.todaySalesListView);
for (int i = 0; i < myList.getChildCount(); i++)
{
v = (View) myList.getChildAt(i);
tx = (TextView) v.findViewById(R.id.quantityTB);
Log.d ("Text", tx.getText().toString());
}
}
Part in my listAdapter :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
final int _position = position;
if (convertView == null) {
convertView = l_Inflater.inflate(R.layout.activity_today_sales_list_view, null);
holder = new ViewHolder();
holder.txt_itemName = (TextView) convertView.findViewById(R.id.itemNameTB);
holder.txt_itemPrice = (TextView) convertView.findViewById(R.id.priceTB);
holder.txt_itemQuantity = (TextView) convertView.findViewById(R.id.quantityTB);
holder.txt_plusBtn = (Button) convertView.findViewById(R.id.addBtn);
holder.txt_minusBtn = (Button) convertView.findViewById(R.id.minusBtn);
holder.txt_plusBtn.setTag(holder);
holder.txt_minusBtn.setTag(holder);
convertView.setTag(holder);
holder.txt_plusBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Set Text of TextView
}
});
holder.txt_minusBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Set Text of TextView
}
});
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.txt_itemName.setText(itemDetailsrrayList.get(position).getItemName());
holder.txt_itemPrice.setText("RM "+itemDetailsrrayList.get(position).getItemCost());
return convertView;
}
Here is the problem i am facing :
I could not access to the first item in the listview
The access to the item is not consistent (meaning sometime it success and sometime it failed)
Anyone can helps ?
Related
I have a listview which has two image buttons in the row. Tapping image button change the background color. When I tap on the first list item, image button background change and the view is saved but scrolling bottom of the listview, background color of image button of another list item changes as well. Below is the getView of Custom Adapter. How can I avoid this problem?
public View getView(final int i, View view, ViewGroup viewGroup) {
ViewHolder holder = new ViewHolder();
holder = null;
//view=null;
if (inflater == null) {
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (view == null) {
view = inflater.inflate(R.layout.student_list, null);
holder = new ViewHolder();
holder.presentButton = (ImageButton) view.findViewById(R.id.imageView);
holder.absentButton = (ImageButton) view.findViewById(R.id.imageView2);
holder.presentButton.setBackgroundColor(0);
holder.absentButton.setBackgroundColor(0);
view.setTag(holder);
}
else {
holder = (ViewHolder) view.getTag();
}
final SQLiteStudents db1 = new SQLiteStudents(activity.getApplicationContext());
final TextView tvName = (TextView) view.findViewById(R.id.tv_name);
final TextView tvRoll = (TextView) view.findViewById(R.id.tv_roll);
final studentInfo s = students.get(i);
tvRoll.setText(s.getRoll() + ".");
tvName.setText(s.getName());
final Integer roll = Integer.parseInt(s.getRoll());
//ivpresent.setBackgroundColor(0);
final ViewHolder finalHolder1 = holder;
//final ViewHolder finalHolder = holder;
holder.presentButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
db1.updateUser(roll,"present");
finalHolder1.presentButton.setBackgroundColor(GREEN);
finalHolder1.absentButton.setBackgroundColor(0);
//v1.setTag(v.getTag());
//Log.d("present","Roll No: "+String.valueOf(roll));
finalHolder1.presentButton.setTag(Integer.toString(i));
notifyDataSetChanged();
}
});
holder.absentButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
db1.updateUser(roll,"absent");
finalHolder1.presentButton.setBackgroundColor(0);
finalHolder1.absentButton.setBackgroundColor(RED);
//view=null;
//Log.d("absent","Roll No: "+String.valueOf(roll));
finalHolder1.presentButton.setTag(Integer.toString(i));
notifyDataSetChanged();
}
});
return view;
}
public static class ViewHolder {
public ImageButton presentButton;
public ImageButton absentButton;
}
public class CustomListAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater;
private List<studentInfo> students;
private boolean presentButton = false;
private boolean absentButton = false;
You need to add an else accordingly in your code, see below:
public View getView(final int i, View view, ViewGroup viewGroup) {
ViewHolder holder = new ViewHolder();
holder = null;
//view=null;
if (inflater == null) {
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
if (view == null) {
view = inflater.inflate(R.layout.student_list, null);
holder = new ViewHolder();
holder.presentButton = (ImageButton) view.findViewById(R.id.imageView);
holder.absentButton = (ImageButton) view.findViewById(R.id.imageView2);
holder.presentButton.setBackgroundColor(0);
holder.absentButton.setBackgroundColor(0);
view.setTag(holder);
}
else {
holder = (ViewHolder) view.getTag();
}
final SQLiteStudents db1 = new SQLiteStudents(activity.getApplicationContext());
final TextView tvName = (TextView) view.findViewById(R.id.tv_name);
final TextView tvRoll = (TextView) view.findViewById(R.id.tv_roll);
final studentInfo s = students.get(i);
tvRoll.setText(s.getRoll() + ".");
tvName.setText(s.getName());
final Integer roll = Integer.parseInt(s.getRoll());
//ivpresent.setBackgroundColor(0);
final ViewHolder finalHolder1 = holder;
//final ViewHolder finalHolder = holder;
holder.presentButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
db1.updateUser(roll,"present");
presentButton = true;
}
});
holder.absentButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
db1.updateUser(roll,"absent");
absentButton = true;
}
});
if(presentButton){
finalHolder1.presentButton.setBackgroundColor(GREEN);
finalHolder1.absentButton.setBackgroundColor(0);
//v1.setTag(v.getTag());
//Log.d("present","Roll No: "+String.valueOf(roll));
finalHolder1.presentButton.setTag(Integer.toString(i));
notifyDataSetChanged();
} else {
//set button to some default button like black
}
if(absentButton){
finalHolder1.presentButton.setBackgroundColor(0);
finalHolder1.absentButton.setBackgroundColor(RED);
//view=null;
//Log.d("absent","Roll No: "+String.valueOf(roll));
finalHolder1.presentButton.setTag(Integer.toString(i));
notifyDataSetChanged();
} else {
//set button to some default button like black
}
return view;
}
This is my listview, my problem is why I'm trying to click on my ImageButton(in listview) but the textview(in listview) never change the value that what I want. It's take my whole morning, can someone please help me, Thanks.
public class ViewAdapter extends BaseAdapter implements Filterable {
LayoutInflater mInflater;
public ViewAdapter() {
mInflater = LayoutInflater.from(getActivity());
}
#Override
public int getCount() {
return pl.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.listproduct, null);
}
final TextView ProductCode = (TextView) convertView.findViewById(R.id.productcode);
ProductCode.setText("" + pl.get(position).getProductCode());
final TextView Qty = (TextView) convertView.findViewById(R.id.qty);
Qty.setText("" + pl.get(position).getQty());
final TextView Description = (TextView) convertView.findViewById(R.id.description);
Description.setText("" + pl.get(position).getProductName());
final String p = Description.getText().toString();
final TextView Price = (TextView) convertView.findViewById(R.id.price);
Price.setText("" + pl.get(position).getProductPrice());
final ImageButton minus = (ImageButton) convertView.findViewById(R.id.minus);
minus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Qty.setText("testing")// textview never change to "testing"
}
});
notifyDataSetChanged();
return convertView;
}
Please try your code with holder just like follows
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_entry, null);
holder = new ViewHolder();
holder.nameTextView = (TextView) convertView.findViewById(R.id.person_name);
holder.surnameTextView = (TextView) convertView.findViewById(R.id.person_surname);
holder.personImageView = (ImageView) convertView.findViewById(R.id.person_image);
convertView.setTag(holder);
}
else {
holder = (ViewHolder) convertView.getTag();
}
Person person = getItem(position);
holder.nameTextView.setText(person.getName());
holder.surnameTextView.setText(person.getSurname());
//holder.personImageView.setImageBitmap(person.getImage());
return convertView;
}
Then click as follows
holder.personImageView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.nameTextView.setText("testing")// textview never change to "testing"
}
});
Use this code :
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.listproduct, null);
}
final TextView ProductCode = (TextView) convertView.findViewById(R.id.productcode);
ProductCode.setText("" + pl.get(position).getProductCode());
final TextView Qty = (TextView) convertView.findViewById(R.id.qty);
final TextView Description = (TextView) convertView.findViewById(R.id.description);
Description.setText("" + pl.get(position).getProductName());
final String p = Description.getText().toString();
final TextView Price = (TextView) convertView.findViewById(R.id.price);
Price.setText("" + pl.get(position).getProductPrice());
ImageButton minus = (ImageButton) convertView.findViewById(R.id.minus);
minus.setTag(position);
minus.setOnClickListener(clickButton);
return convertView;
}
private View.OnClickListener clickButton = new View.OnClickListener() {
#Override
public void onClick(View v) {
Object tag =v.getTag();
if(tag!=null)
{
int pos = (Integer)tag;
Qty.setText(pl.get(position).getQty());
notifyDataSetChanged();
}
}
} ;
As far as i understand from your question this cannot be done because once you populate the data in custom listView you have recreate it or call the custom listView again with text display you want to change!
Basically you should use RecyclerView,but if you still want to use the listView, use the view-holder approach as in this link below:
Optimize list view
And you should never ever call notifyDataSetChanged() in getView method as its called multiple time as your listview scrolls
I am working on a module that populates cart. I used ListView and extended BaseAdapter to populate the cart items.
With each of the item in ListView, I embedded two button(inc and dec) to increment and decrement quantity of item in cart.
ListView is correctly updated, but increment/decrement button on quick click/tapping shows abrupt behaviour.
Whenever I quickly tap any of inc or dec button, the corresponding inc or dec button of an item next to current item in ListView is automatically clicked(along with current item btn).
In other words, whenever I quickly tap inc btn of ith item in ListView, inc btn of i+1 th item in ListView is automatically clicked( along with inc btn of ith item).
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_cart, parent, false);
holder = new ViewHolder();
holder.baseItem = (TextView) convertView.findViewById(R.id.qnt_tv);
holder.qntInc = (TextView) convertView.findViewById(R.id.inc_btn);
holder.qntDec = (TextView) convertView.findViewById(R.id.dec_btn);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final CartModel cm = mCart.get(position);
holder.baseItem.setText(cm.getmTitle());
holder.qntSel.setText(String.valueOf(cm.getmQnt()));
holder.qntInc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (qntSpinnerCb != null)
qntSpinnerCb.changeQuantityOfSelectedItemInCart(cm.getmIid(), INCREASE_QNT);
}
});
holder.qntDec.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (qntSpinnerCb != null) {
qntSpinnerCb.changeQuantityOfSelectedItemInCart(cm.getmIid(), DECREASE_QNT);
}
}
});
return convertView;
}
Interface for callback
public interface CartQntSpinnerListenerCallBack {
void changeQuantityOfSelectedItemInCart(String iId, char changeType);
}
Tried debugging, unable to figure out this weird behavior.
You are not selecting correct CartModel inside onClick, if you want to get correct object inside onclick then you have to tag position to button
holder.qntInc.setTag(position);
in onClick:
#Override
public void onClick(View view) {
if (qntSpinnerCb != null) {
CartModel cm= mCart.get((Integer)view.getTag);
qntSpinnerCb.changeQuantityOfSelectedItemInCart(cm.getmIid(), INCREASE_QNT);
}
}
Follow the same for qntDec.
If you want to keep your current implementation, do this.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_cart, parent, false);
}
ViewHolder holder = new ViewHolder();
holder.baseItem = (TextView) convertView.findViewById(R.id.qnt_tv);
holder.qntInc = (TextView) convertView.findViewById(R.id.inc_btn);
holder.qntDec = (TextView) convertView.findViewById(R.id.dec_btn);
...
Without seeing the rest of your code, however, I think you may want to reconsider how you are implementing BaseAdapter. Traditionally, they are used in conjunction with a list view to recycle views for efficiency. This means when a list scrolls a view out of frame, instead of redrawing the view - if(convertView == null) - it reuses it. What I think you are experiencing is that you are putting a click listener for two different objects on the same button.
What I would recommend doing is something more like this:
#Override
public int getCount() {
return objects.size();
}
#Override
public CartModel getItem(int position) {
// mCart sounds like a single item but is a list? I advise you to rename
return objects.get(position);
}
#Override
public long getItemId(int position) {
// Implement with id. Copied the id call from your code..
return getItem(position).getmIid();
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_cart, parent, false);
}
CartModel cm = getItem(position);
TextView baseItem = (TextView) convertView.findViewById(R.id.qnt_tv);
baseItem.setText(cm.getmTitle());
// Organize like items together for readability
TextView qntInc = (TextView) convertView.findViewById(R.id.inc_btn);
qntInc.setTag(mCartKey, cm);
qntInc.setOnClickListener(this);
// readability
TextView qntDec = (TextView) convertView.findViewById(R.id.dec_btn);
qntDec.setTag(mCartKey, cm);
qntDec.setOnClickListener(this);
// Defined in convert view?
qntSel.setText(String.valueOf(cm.getmQnt()));
return convertView;
}
#Override
public void onClick(View v) {
switch(v.getId()){
case R.id.R.id.inc_btn:
CartModel cm = (CartModel)v.getTag(mCartKey);
// What is this?
if (qntSpinnerCb != null)
qntSpinnerCb.changeQuantityOfSelectedItemInCart(cm.getmIid(), INCREASE_QNT);
break;
case R.id.dec_btn:
CartModel cm = (CartModel)v.getTag(mCartKey);
if (qntSpinnerCb != null) {
qntSpinnerCb.changeQuantityOfSelectedItemInCart(cm.getmIid(), DECREASE_QNT);
}
break;
}
}
NOTE: BaseAdapter class must implement OnClickListener
store the position of your view in your viewholder and use it every where in the setonclicklistner from viewHolder that position you will not see any absurd behaviour it will work as you want
static class ViewHolder {
TextView baseItem, qntInc, qntDec;
....../* your code goes here */
int position
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_cart, parent, false);
holder = new ViewHolder();
holder.baseItem = (TextView) convertView.findViewById(R.id.qnt_tv);
holder.qntInc = (TextView) convertView.findViewById(R.id.inc_btn);
holder.qntDec = (TextView) convertView.findViewById(R.id.dec_btn);
holder.position=position
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
holder.position=position;
}
CartModel cm = mCart.get(holder.position);
holder.baseItem.setText(cm.getmTitle());
holder.qntSel.setText(String.valueOf(cm.getmQnt()));
holder.qntInc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
CartModel cm = mCart.get(holder.position);
qntSpinnerCb.changeQuantityOfSelectedItemInCart(cm.getmIid(), INCREASE_QNT);
}
});
holder.qntDec.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
CartModel cm = mCart.get(holder.position);
qntSpinnerCb.changeQuantityOfSelectedItemInCart(cm.getmIid(), DECREASE_QNT);
}
});
return convertView;
}
also check the value of INCREMENT_QNT and DECREACE_QNT
Your getter method getmIid() in CartModel has problem i suspect. This method is returning wrong index and so your next/previous button is updating
I suggest to use position integer in your callback changeQuantityOfSelectedItemInCart() first parameter. Because the position will always come correctly with getView() callback
SOLUTION :
holder.qntInc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (qntSpinnerCb != null)
qntSpinnerCb.changeQuantityOfSelectedItemInCart(position+"", INCREASE_QNT);
}
});
holder.qntDec.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (qntSpinnerCb != null)
qntSpinnerCb.changeQuantityOfSelectedItemInCart(position+"", DECREASE_QNT);
}
});
The position is int type and your callback needs String so we should use position+""
Hope this will work for you
There is an issue in the code.
if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item_cart, parent, false);
holder = new ViewHolder();
holder.baseItem = (TextView) convertView.findViewById(R.id.qnt_tv);
holder.qntInc = (TextView) convertView.findViewById(R.id.inc_btn);
holder.qntDec = (TextView) convertView.findViewById(R.id.dec_btn);
// This should be added in the code
holder.qntSel = (TextView) convertView.findViewById(R.id.sec_txt);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
I am creating an app where there are images in a custom Listview. The database put a picture or another. Clicking the image has to change. But when I click the image is not changed at the correct ImageView. It changes other ImageView.
I need any help. thanks
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
final MonumentosHolder holder = new MonumentosHolder();
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.listview_row, null);
TextView tv = (TextView) v.findViewById(R.id.textoListviewNombre);
ImageView img3 = (ImageView) v.findViewById(R.id.imagenVisto);
holder.texto = tv;
holder.imagenVisto = img3;
v.setTag(holder);
}
else {
holder = (MonumentosHolder) v.getTag();
}
holder.position = position;
final MonumentosObj p = monumentosLista.get(position);
holder.texto.setText(p.getNombre());
if (p.getVisto().equals("n")) {
holder.imagenVisto.setImageResource(R.drawable.vistorojo);
} else {
holder.imagenVisto.setImageResource(R.drawable.vistoverde);
}
holder.imagenVisto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MonumentosObj p1 = monumentosLista.get(holder.position);
if (p1.getVisto().equals("s")) {
holder.imagenVisto.setImageResource(R.drawable.vistorojo);
p1.setVisto("n");
} else {
holder.imagenVisto.setImageResource(R.drawable.vistoverde);
p1.setVisto("s");
}
MonumentosAdapter.updateMonumentos(p1);
MonumentosObj p = monumentosLista.get(holder.position);
}
});
imageLoader.displayImage(imageUrls[position], holder.imagen, options, animateFirstListener);
return v;
}
EDIT
Onclick not change the correct image. But when I scroll the listview to below and and return to top, the correct image has changed...
I think that bit of code maybe wrong :
holder.imagenVisto.setOnClickListener(new View.OnClickListener() {
private int pos = position;
#Override
public void onClick(View v) {
MonumentosObj p1 = monumentosLista.get(pos);
Try to store position inside holder object and when assigning it in onClick Listener instead
private int pos = position;
do
private int pos = holder.getPosition()
Remember that android ListViews are not loading all items at one time it just loads thats what is visible and few items beyond that,
Also remember that positions of your items in your Arrays doesn't change you may use it somehow :)
try to change
holder.imagenVisto.setOnClickListener(new View.OnClickListener()
by
holder.imagenVisto.setOnClickListener(new OnClickListener()
I think your click listener affect early items in your list!
do this:
put a long item in your viewHolder:
class MonumentosHolder {
.
.
.
long position;
}
and then in your getView, set the holder positon:
View v = convertView;
--EDITED--
final MonumentosHolder holder = new MonumentosHolder();
--END EDITED--
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.listview_row, null);
TextView tv = (TextView) v.findViewById(R.id.textoListviewNombre);
ImageView img3 = (ImageView) v.findViewById(R.id.imagenVisto);
holder.texto = tv;
holder.imagenVisto = img3;
v.setTag(holder);
}
else {
holder = (MonumentosHolder) v.getTag();
}
**holder.postion = position;**
and then click listener would be like this:
EDITED
holder.imagenVisto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MonumentosObj p1 = monumentosLista.get(**holder.position**);
if (p1.getVisto().equals("s")) {
holder.imagenVisto.setImageResource(R.drawable.vistorojo);
p1.setVisto("n");
} else {
holder.imagenVisto.setImageResource(R.drawable.vistoverde);
p1.setVisto("s");
}
MonumentosAdapter.updateMonumentos(p1);
MonumentosObj p = monumentosLista.get(holder.position);
}
});
EDIT 4
remove the final modifier from holder, define another MonumentosHolder named finalHolder like this:
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
MonumentosHolder holder = new MonumentosHolder();
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = inflater.inflate(R.layout.listview_row, null);
TextView tv = (TextView) v.findViewById(R.id.textoListviewNombre);
ImageView img3 = (ImageView) v.findViewById(R.id.imagenVisto);
holder.texto = tv;
holder.imagenVisto = img3;
v.setTag(holder);
}
else {
holder = (MonumentosHolder) v.getTag();
}
holder.position = position;
final MonumentosObj p = monumentosLista.get(position);
holder.texto.setText(p.getNombre());
if (p.getVisto().equals("n")) {
holder.imagenVisto.setImageResource(R.drawable.vistorojo);
} else {
holder.imagenVisto.setImageResource(R.drawable.vistoverde);
}
final MonumentosHolder finalHolder = holder;
holder.imagenVisto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MonumentosObj p1 = monumentosLista.get(finalHolder.position);
if (p1.getVisto().equals("s")) {
holder.imagenVisto.setImageResource(R.drawable.vistorojo);
p1.setVisto("n");
} else {
holder.imagenVisto.setImageResource(R.drawable.vistoverde);
p1.setVisto("s");
}
MonumentosAdapter.updateMonumentos(p1);
MonumentosObj p = monumentosLista.get(finalHolder.position);
}
});
imageLoader.displayImage(imageUrls[position], holder.imagen, options, animateFirstListener);
return v;
}
I have found a mysterious problem that may be a bug!
I have a list in my fragment. Each row has a button. List shouldn't respond to click however buttons are clickable.
In order to get which button has clicked I have created a listener and implement it in my fragment. This is the code of my adapter.
public class AddFriendsAdapter extends BaseAdapter {
public interface OnAddFriendsListener {
public void OnAddUserClicked(MutualFriends user);
}
private final String TAG = "*** AddFriendsAdapter ***";
private Context context;
private OnAddFriendsListener listener;
private LayoutInflater myInflater;
private ImageDownloader imageDownloader;
private List<MutualFriends> userList;
public AddFriendsAdapter(Context context) {
this.context = context;
myInflater = LayoutInflater.from(context);
imageDownloader = ImageDownloader.getInstance(context);
}
public void setData(List<MutualFriends> userList) {
this.userList = userList;
Log.i(TAG, "List passed to the adapter.");
}
#Override
public int getCount() {
try {
return userList.size();
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
holder = new ViewHolder();
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf");
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.tvUserName.setTypeface(font);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
holder.btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.e(TAG, "Item: " + position);
listener.OnAddUserClicked(userList.get(position));
}
});
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvUserName.setText(userList.get(position).getName());
imageDownloader.displayImage(holder.ivPicture, userList.get(position).getPhotoUrl());
return convertView;
}
public void setOnAddClickedListener(OnAddFriendsListener listener) {
this.listener = listener;
}
static class ViewHolder {
TextView tvUserName;
ImageView ivPicture;
Button btnAdd;
}
}
When I run the app, I can see my rows however since my list is long and has over 200 items when i goto middle of list and click an item then returned position is wrong (it's something like 7, sometimes 4 and etc.).
Now what is the mystery?
If I active on item listener of list from my fragment and click on row then correct row position will be displayed while on that row if I click on button then wrong position will be displayed.
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Log.e(TAG, "item " + position + " clicked.");
}
});
Result in logcat:
05-09 10:22:25.228: E/AddFriendsFragment(20296): item 109 clicked.
05-09 10:22:34.453: E/*** AddFriendsAdapter ***(20296): Item: 0
Any suggestion would be appreciated. Thanks
Because the convertView and holder will be recycled to use, move your setOnClickListener out of the if else statement:
if (convertView == null) {
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
holder = new ViewHolder();
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf");
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.tvUserName.setTypeface(font);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
Log.e(TAG, "Item: " + position);
listener.OnAddUserClicked(userList.get(position));
}
});
It is not the best solution for that,because there will be some performance issue. I suggest you create a Map for your view and create a new view for your item then just use the relative view for each view.
I think it will be a better solution with best performance:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
holder = new ViewHolder();
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf");
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.tvUserName.setTypeface(font);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
holder.btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Integer pos = (Integer)v.getTag();
Log.e(TAG, "Item: " + pos);
listener.OnAddUserClicked(userList.get(pos));
}
});
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tvUserName.setText(userList.get(position).getName());
imageDownloader.displayImage(holder.ivPicture, userList.get(position).getPhotoUrl());
holder.btnAdd.setTag(position);
return convertView;
}
You can also manage your view by yourself. Create every unique view for your item, don't recycle view.
//member various
private Map<Integer, View> myViews = new HashMap<Integer, View>();
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View view = myViews.get(position);
if (view == null) {
view = myInflater.inflate(R.layout.list_add_friends_row, null);
//don't need use the holder anymore.
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf");
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.tvUserName.setTypeface(font);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
holder.btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Integer pos = (Integer)v.getTag();
Log.e(TAG, "Item: " + pos);
listener.OnAddUserClicked(userList.get(pos));
}
});
holder.tvUserName.setText(userList.get(position).getName());
imageDownloader.displayImage(holder.ivPicture,
userList.get(position).getPhotoUrl());
myViews.put(position, view);
}
return view;
}
Did you tried doing something like this:
holder.btnAdd.setTag(Integer.valueOf(position));
And then retrieve wich row was clicked in the callback for the button, like this:
public void btnAddClickListener(View view)
{
position = (Integer)view.getTag();
Foo foo = (Foo)foos_adapter.getItem(position); //get data of row(position)
//do some
}
Another approach I found useful (if you are using the ViewHolder pattern of course) is to set the index on a separate attribute whenever getView() is called, then inside your onClickListener you just have to reference your holder's position attribute, something like this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView == null){
convertView = View.inflate(mContext, R.layout.contact_picker_row,null);
holder = new ViewHolder();
holder.body = (RelativeLayout)convertView.findViewById(R.id.numberBody);
convertView.setTag(holder);
}else{
holder = (ViewHolder)convertView.getTag();
}
holder.position = position;
holder.body.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(mContext,"Clicked on: "+holder.position,Toast.LENGTH_LONG).show();
}
});
return convertView;
}
private class ViewHolder{
RelativeLayout body;
int position;
}