ListView - Wrong Recycling behavior with ViewHolder Design - android

Description:
I'm currently implementing a custom ListViewAdapter using the ViewHolder design pattern. The adapter is using a layout with a button that toggles the visibility of a RelativeLayout.
Problem:
When I click the button and the RelativeLayout is shown, everything seems to work fine until I scroll down. When I scroll down, I see that other listitems have been effected by the previous button click, so the RelativeLayout is visible on other (wrong) items. The item that is effected by the button click seems to be the one that is recycled when I scroll down. Please note that everything else works correct, texts etc. are set at the correct position.
Code:
CustomListAdapter
public class ItemsListAdapter extends ArrayAdapter<Item> {
private Context mContext;
private List<Item> mItems;
public ItemsListAdapter(Context context, int resource, List<Item> objects) {
super(context, resource);
mContext = context;
mItems = objects;
}
static class ViewHolder {
View mView;
boolean mExpanded;
public ViewHolder(View view, boolean expanded)
{
mView = view;
mExpanded = expanded;
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
final Item item = getItem(position);
final ViewHolder holder;
if (v == null) {
LayoutInflater mInflater = (LayoutInflater)
mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
v = mInflater.inflate(R.layout.custom_list_item, null);
holder = new ViewHolder(v, false);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
if(item != null)
{
TextView tvName = (TextView) holder.mView.findViewById(R.id.tv_name);
tvName.setText(item.getName());
RelativeLayout rlExpand = (RelativeLayout) holder.mView.findViewById(R.id.rl_expand);
if(holder.mExpanded) {
rlExpand.setVisibility(View.VISIBLE);
}
else {
rlExpand.setVisibility(View.GONE);
}
TextView tvDescription = (TextView) holder.mView.findViewById(R.id.tv_description);
if(tvDescription != null)
tvDescription.setText(item.getDescription());
final ImageButton ibExpand = (ImageButton) holder.mView.findViewById(R.id.ib_expand);
ibExpand.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RelativeLayout rlExpand = (RelativeLayout) holder.mView.findViewById(R.id.rl_expand);
if (rlExpand.getVisibility() == View.GONE) {
v.setVisibility(View.INVISIBLE);
rlExpand.setVisibility(View.VISIBLE);
holder.mExpanded = true;
}
}
});
ImageButton ibCollapse = (ImageButton) holder.mView.findViewById(R.id.ib_collapse);
ibCollapse.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RelativeLayout rlExpand = (RelativeLayout) holder.mView.findViewById(R.id.rl_expand);
ibExpand.setVisibility(View.VISIBLE);
rlExpand.setVisibility(View.GONE);
holder.mExpanded = false;
}
});
}
return v;
}
#Override
public int getCount() {
return mItems.size();
}
#Override
public Truism getItem(int position) {
return mItems.get(position);
}
Any help is highly appreciated! Thank you in advance!

Try to change:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
final Item item = getItem(position);
final ViewHolder holder;
if (v == null) {
LayoutInflater mInflater = (LayoutInflater)
mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
v = mInflater.inflate(R.layout.custom_list_item, null);
holder = new ViewHolder(v, false);
v.setTag(holder);
} else {
holder = (ViewHolder) v.getTag();
}
if(item != null)
{
TextView tvName = (TextView) holder.mView.findViewById(R.id.tv_name);
tvName.setText(item.getName());
RelativeLayout rlExpand = (RelativeLayout) holder.mView.findViewById(R.id.rl_expand);
if(item.isExpanded) {
rlExpand.setVisibility(View.VISIBLE);
}
else {
rlExpand.setVisibility(View.GONE);
}
TextView tvDescription = (TextView) holder.mView.findViewById(R.id.tv_description);
if(tvDescription != null)
tvDescription.setText(item.getDescription());
[...]
}
return v;
}
Change holder.mExpanded to item.isExpanded

Related

ListView's adapter only shows text of first item after fragment resumes from backstack

I have a ListView adapter that shows a list of cards with words on them. These cards can be dragged out or dropped in, with a call to notifyDataSetChanged(). When I return to this fragment's instance from the back stack, only the first card in the list has it's textview set with the word text. I should add that when I call notifyDataSetChanged() in the adapter's getView() before I set the text on the ViewHolder object, the text appears after a noticeable delay (a little less than .5 seconds). Here is the adapter:
public class ItemListAdapter extends ItemBaseAdapter {
public ItemListAdapter(Context context, List<WordCard> wordCards) {
super(context, wordCards);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View rowView = convertView;
// reuse views
if (rowView == null) {
LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
rowView = inflater.inflate(R.layout.item_word, null);
ViewHolder viewHolder = new ViewHolder();
viewHolder.text = (TextView) rowView.findViewById(R.id.word_text);
viewHolder.mCloseBtn = (ImageView)rowView.findViewById(R.id.close_btn);
rowView.setTag(viewHolder);
} else {
ViewHolder holder = (ViewHolder) rowView.getTag();
holder.text.setText(mWordCards.get(position).getWord());
holder.mCloseBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mWordCards.remove(position);
notifyDataSetChanged();
}
});
rowView.setOnDragListener(new ItemDragListener(mWordCards.get(position)));
}
return rowView;
}
static class ViewHolder {
TextView text;
ImageView mCloseBtn;
}
}
It is set in the fragment's onCreateView:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
setHasOptionsMenu(true);
View rootView = inflater.inflate(R.layout.fragment_game, container, false);
init(rootView);
return rootView;
}
#Override
public void onResume() {
super.onResume();
setRetainInstance(true);
}
private void init(View rootView) {
mGamePresenter = new GamePresenter(this);
mListView1 = (ListView) rootView.findViewById(R.id.listview1);
mListView2 = (ListView) rootView.findViewById(R.id.listview2);
mListView3 = (ListView) rootView.findViewById(R.id.listview3);
mListView4 = (ListView) rootView.findViewById(R.id.listview4);
mGridView = (GridView) rootView.findViewById(R.id.card_grid);
mGridEmptyStateLl = (LinearLayout) rootView.findViewById(R.id.grid_empty_state_ll);
mCodePanel1 = (LinearLayoutAbsListView) rootView.findViewById(R.id.code_panel1);
mCodePanel1.setOnDragListener(new ViewDragListener(mGamePresenter));
mCodePanel1.setAbsListView(mListView1);
mCodePanel2 = (LinearLayoutAbsListView) rootView.findViewById(R.id.code_panel2);
mCodePanel2.setOnDragListener(new ViewDragListener(mGamePresenter));
mCodePanel2.setAbsListView(mListView2);
mCodePanel3 = (LinearLayoutAbsListView) rootView.findViewById(R.id.code_panel3);
mCodePanel3.setOnDragListener(new ViewDragListener(mGamePresenter));
mCodePanel3.setAbsListView(mListView3);
mCodePanel4 = (LinearLayoutAbsListView) rootView.findViewById(R.id.code_panel4);
mCodePanel4.setOnDragListener(new ViewDragListener(mGamePresenter));
mCodePanel4.setAbsListView(mListView4);
mGridPanel = (LinearLayoutAbsListView) rootView.findViewById(R.id.grid_panel);
mGridPanel.setOnDragListener(new ViewDragListener(mGamePresenter));
mGridPanel.setAbsListView(mGridView);
mGridView.setOnItemLongClickListener(new GridItemLongClickListener(mGamePresenter));
if (mGridList != null) {
onWordListComplete(mGridList);
}
if (mPanel1List == null) {
mPanel1List = new ArrayList<>();
}
if (mPanel2List == null) {
mPanel2List = new ArrayList<>();
}
if (mPanel3List == null) {
mPanel3List = new ArrayList<>();
}
if (mPanel4List == null) {
mPanel4List = new ArrayList<>();
}
mItemListAdapter1 = new ItemListAdapter(getContext(), mPanel1List);
mItemListAdapter2 = new ItemListAdapter(getContext(), mPanel2List);
mItemListAdapter3 = new ItemListAdapter(getContext(), mPanel3List);
mItemListAdapter4 = new ItemListAdapter(getContext(), mPanel4List);
mListView1.setAdapter(mItemListAdapter1);
mListView1.setOnItemLongClickListener(new ListItemLongClickListener());
mListView2.setAdapter(mItemListAdapter2);
mListView2.setOnItemLongClickListener(new ListItemLongClickListener());
mListView3.setAdapter(mItemListAdapter3);
mListView3.setOnItemLongClickListener(new ListItemLongClickListener());
mListView4.setAdapter(mItemListAdapter4);
mListView4.setOnItemLongClickListener(new ListItemLongClickListener());
}
Screenshot before returning to fragment instance:
Screenshot after returning to fragment instance:
Got it! The problem was that I was finding the IDs in the getView() method itself, instead of extending the RecyclerView.ViewHolder in the ViewHolder class and getting IDs that way:
public View getView(final int i, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
// reuse views
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_word, null);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
if (i <= mWordCards.size()-1){
viewHolder.text.setText(mWordCards.get(i).getWord());
convertView.setOnDragListener(new ItemDragListener(mWordCards.get(i)));
}
return convertView;
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView text;
ImageView mCloseBtn;
public ViewHolder(View itemView) {
super(itemView);
text = (TextView) itemView.findViewById(R.id.word_text);
mCloseBtn = (ImageView)itemView.findViewById(R.id.close_btn);
}

Confusion with getview android imagebuttons

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;
}

Dynamically change custom list item background

I am creating a custom list view with favorite functionality, but I don't know how to change favorite image background on click. When I simply change the background of favorite icon than it automatically change background of another favorite image background at the time of scrolling. Please check below code :
public class CustomArrayAdapter extends BaseAdapter {
private Activity activity;
private LayoutInflater inflater = null;
ArrayList<Customlist> list;
DecimalFormat formatter = new DecimalFormat("#,##,###");
public CustomArrayAdapter(Activity a, ArrayList<Customlist> list) {
activity = a;
inflater = (LayoutInflater) activity
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.list = list;
}
public int getCount() {
return list.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView,
final ViewGroup parent) {
TextView txt_unit, txt_state, txt_price, term_left, customr;
TextView install_date;
final ImageView fav;
View view = convertView;
if (convertView == null)
view = inflater.inflate(R.layout.list_item, null);
customr = (TextView) view.findViewById(R.id.customr);
txt_state = (TextView) view.findViewById(R.id.txt_state);
install_date = (TextView) view.findViewById(R.id.install_date);
term_left = (TextView) view.findViewById(R.id.term_left);
txt_price = (TextView) view.findViewById(R.id.txt_price);
fav = (ImageView) view.findViewById(R.id.fav);
txt_unit = (TextView) view.findViewById(R.id.txt_unit);
fav.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
// fav = (ImageView)v.findViewById(R)
fav.setBackgroundResource(R.drawable.favourite_select);
Toast.makeText(activity, "click", 1).show();
}
});
// set values
customr.setText(list.get(position).getCUSTOMER());
txt_state.setText(list.get(position).getSTATE_NAME());
install_date.setText(list.get(position).getINSTALL_DATE());
term_left.setText(list.get(position).getTREM_LEFT());
String price = formatter.format(Integer.parseInt(list.get(position)
.getRUPEES()));
return view;
}
}
First, you need to implement the adapter on ViewHolder pattern:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHoldler holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(ctx).inflate(
R.layout.frag_home_gridview_item, null, false);
holder = new ViewHoldler();
holder.iv = (ImageView) convertView
.findViewById(R.id.gridview_item_label);
holder.tv = (TextView) convertView
.findViewById(R.id.gridview_item_name);
convertView.setTag(holder);
} else {
holder = (ViewHoldler) convertView.getTag();
}
holder.tv.setText(getItem(position));
holder.iv.setImageResource(this.ids[position]);
return convertView;
}
private class ViewHoldler {
ImageView iv;
TextView tv;
}
Second, use partial refreshment mechanism to change the target View's background:
private void refreshPartially(int position){
int firstVisiblePosition = listview.getFirstVisiblePosition();
int lastVisiblePosition = listview.getLastVisiblePosition();
if(position>=firstVisiblePosition && position<=lastVisiblePosition){
View view = listview.getChildAt(position - firstVisiblePosition);
if(view.getTag() instanceof ViewHolder){
ViewHolder vh = (ViewHolder)view.getTag();
//holder.play.setBackgroundResource(resId);//Do something here.
...
}
}
}
Third, add AdapterView.OnItemClickListener to your ListView:
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
refreshPartially(position);
}
});
You need to implement the adapter on ViewHolder pattern:
http://www.vogella.com/tutorials/AndroidListView/article.html

Android, List Adapter returns wrong position in getView

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;
}

Set OnClick Listener on button inside list view in android

I have a list view in my screen, every list item contains 2 text views and one button.
On button click i want to take the list item selected index to get some data from a vector.
This is my List Custom Adapter.But i don't know how to do that.
private class CustomAdapter extends ArrayAdapter<ServicesItems> {
public CustomAdapter(Context context, int resource,
int textViewResourceId, List<ServicesItems> objects) {
super(context, resource, textViewResourceId, objects);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
TextView item = null;
TextView description = null;
Button subNowBtn;
ServicesItems ii = getItem(position);
if (null == convertView) {
convertView = mInflater.inflate(
R.layout.list_of_servics_item_2, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
holder = (ViewHolder) convertView.getTag();
// cat_name = holder.gettitle();
Resources resources = getResources();
ServicesItems bean1 = (ServicesItems) servicesVector
.elementAt(position);
String cat_name_str = bean1.getService_name().toString();
String descreption = bean1.getDescription().toString();
item = holder.getItem();
item.setText(bean1.getDescription());
description = holder.getDescription();
description.setText(bean1.getService_name());
subNowBtn=holder.getSubButton();
return convertView;
}
private class ViewHolder {
private View mRow;
private TextView description = null;
private TextView item = null;
private Button sub = null;
public ViewHolder(View row) {
mRow = row;
}
public TextView getDescription() {
if (null == description) {
description = (TextView) mRow
.findViewById(R.id.category_tv);
}
return description;
}
public TextView getItem() {
if (null == item) {
item = (TextView)
mRow.findViewById(R.id.descreption_tv);
}
return item;
}
public Button getSubButton(){
if(null==sub){
sub=(Button)findViewById(R.id.subscribe_now_btn);
}
return sub;
}
}
}
In your Adapter try this:
#Override
public View getView(final int position, View convertView, ViewGroup parent)
{
View row = convertView;
YourWrapper wrapper = null;
if (row == null)
{
row = inflater.inflate(R.layout.layout, parent, false);
wrapper = new YourWrapper (row);
row.setTag(wrapper);
}
else
wrapper = (YourWrapper) row.getTag();
wrapper.getButton().setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
// What you want
}
});
return row;
}
EDIT
Your wrapper class:
public class YourWrapper
{
private View base;
private Button button;
public YourWrapper(View base)
{
this.base = base;
}
public Button getButton()
{
if (button == null)
{
button = (Button) base.findViewById(R.id.your_button);
}
return (button`);
}
}
Change sub=(Button)findViewById(R.id.subscribe_now_btn); into sub=(Button) mRow.findViewById(R.id.subscribe_now_btn);
You can get the index of list-view on button click, here are two examples:--
You can write these in your onClick method of listener.
Example 1
View parentRow = (View) v.getParent();
ListView listView = (ListView) parentRow.getParent();
final int position = listView.getPositionForView(parentRow);
Example 2
Set position using setTag when creating view
mybutton.setTag(position);
Get position in the listener
int position = (Integer) view.getTag();
Hope this help :)

Categories

Resources