Android listview using ViewHolder

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP


Android listview using ViewHolder



I have a problem. I'm attempting to change an icon in my listview after it has been clicked. It works correctly although are not modified only the clicked icons but also those who are not displayed. For example if I click the icon in the first item of the listview, also the fifth icon changes. This behaviour is repeated for all the following items (every five items of the listview).
This is my getView method:


public class AlphabeticalAdapter extends ArrayAdapter<String>
{
int layoutResourceId;
private final Context context;
private List<String> data;
private ProgressDialog mProgressDialog;
private ImageView downloadImageButton;


public AlphabeticalAdapter(Context context, int resource, List<String> data){
super(context, resource, data);
this.layoutResourceId = resource;
this.context = context;
this.data = data;
}

public View getView(int position, View convertView, ViewGroup parent) {

// View rowView = convertView;
final ViewHolder viewHolder;

if (convertView == null) {

LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);


convertView = inflater.inflate(R.layout.catalogslist_single_row, parent, false);

viewHolder = new ViewHolder();

viewHolder.catlogTitle=(TextView)convertView.findViewById(R.id.txtTitle);
viewHolder.icon=(ImageView)convertView.findViewById(R.id.imageView2);
viewHolder.downloadImageButton=(ImageView)convertView.findViewById(R.id.downloadImageButton);

//downloadImageButton = (ImageView)rowView.findViewById(R.id.downloadImageButton);

viewHolder.position = position;


viewHolder.downloadImageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("DOWNLOAD PRESSED");

viewHolder.downloadImageButton = (ImageView)v.findViewById(R.id.downloadImageButton);
viewHolder.downloadImageButton.setImageResource(R.drawable.icon_ok);
viewHolder.downloadImageButton.setTag("downloaded");
//rowView.setTag("downloaded");


}
});



convertView.setTag(viewHolder);

}

else{
viewHolder= (ViewHolder)convertView.getTag();
}

viewHolder.catlogTitle.setText(data.get(position));
viewHolder.catlogTitle.setTypeface(regularDin);
viewHolder.icon.setImageResource(R.drawable.cata);


if(viewHolder.downloadImageButton.getTag() == "downloaded"){
downloadImageButton = (ImageView)convertView.findViewById(R.id.downloadImageButton);
downloadImageButton.setImageResource(R.drawable.icon_ok);
}
else{
downloadImageButton = (ImageView)convertView.findViewById(R.id.downloadImageButton);
downloadImageButton.setImageResource(R.drawable.icon_download);
}


viewHolder.position = position;

return convertView;

} //close getView



...



And this is my ViewHolder class:


static class ViewHolder{
ImageView downloadImageButton;
TextView catlogTitle;
ImageView icon;
int position;
}




4 Answers
4



Change your code at below. I think you're missing that.


public class AlphabeticalAdapter extends ArrayAdapter<String> {
int layoutResourceId;
private final Context context;
private List<String> data;
private List<String> tags;
private ProgressDialog mProgressDialog;
private ImageView downloadImageButton;

public AlphabeticalAdapter(Context context, int resource, List<String> data) {
super(context, resource, data);
this.layoutResourceId = resource;
this.context = context;
this.data = data;
tags = new ArrayList<String>();
int size = data.size();
for (int i = 0; i < size; i++) {
tags.add("tag");
}
}

static class ViewHolder {
ImageView downloadImageButton;
TextView catlogTitle;
ImageView icon;
int position;
}

public View getView(final int position, View convertView, ViewGroup parent) {

// View rowView = convertView;
final ViewHolder viewHolder;

if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// convertView = inflater.inflate(R.layout.catalogslist_single_row,
// parent, false);
viewHolder = new ViewHolder();
viewHolder.position = position;
viewHolder.downloadImageButton
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("DOWNLOAD PRESSED");
viewHolder.downloadImageButton.setTag("downloaded");
tags.add(position, "downloaded");
}
});
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}

viewHolder.catlogTitle.setText(data.get(position));
viewHolder.catlogTitle.setTypeface(regularDin);
viewHolder.icon.setImageResource(R.drawable.cata);

if (tags.get(position) == "downloaded") {
downloadImageButton.setImageResource(R.drawable.icon_ok);
} else {
downloadImageButton.setImageResource(R.drawable.icon_download);
}

viewHolder.position = position;
return convertView;
} // close getView
}





I tried this solution but it doesn't work :-(
– Mark
Jul 21 '14 at 13:28





@Mark i edited my code, see this and tell its help you
– Divyang Metalia
Jul 21 '14 at 13:39





This is wrong. Read my answer to understand why. There number of ViewHolder objects not equals the number of the rows. What would be the meaning of the ViewHolder pattern otherwise?
– kupsef
Jul 21 '14 at 13:47





@kupsef yes you are right. Other different list one added then its work
– Divyang Metalia
Jul 21 '14 at 13:49





@Mark i updated code its work i think. You can also use array list to hashmap or TreeSet for selected position.
– Divyang Metalia
Jul 21 '14 at 13:56



There are as many convertViews as many row visible in the same time in your ListView (the system reuses it). So you actually have 5 convertView, and because of that you have 5 ImageView for the icons. The problem is that you use those ImageView's tag to store the "downloaded" information. That is 5 state, and that is why you see every fifth row downloaded while you scroll in the list.


convertViews


ListView


convertView


ImageView



I guess now you see that it won't work. You need to store the downloaded state for every item, so you have to change the underlying List<String> to List<ListItem>, where ListItem can store the downloaded state for the actual row.


List<String>


List<ListItem>


ListItem



After that, all you have to do is to update the convertView's ImageView (in getView()) to show the correct icon.


convertView


ImageView


getView()



Change your code like this. Add null check with convertView before your try block.


final MenuItem menuItem = getItem(position);
View view = convertView;
final ViewHolder viewHolder;

if (convertView == null) {

LayoutInflater inflater;

inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.menu_item, parent, false);
viewHolder = new ViewHolder();
// viewHolder.half = (TextView) view.findViewById(R.id.half);
viewHolder.name = (TextView) view.findViewById(R.id.name);
viewHolder.description = (TextView) view.findViewById(R.id.description);
viewHolder.price = (TextView) view.findViewById(R.id.price);
viewHolder.add = (Button) view.findViewById(R.id.add);
viewHolder.selectedView = view.findViewById(R.id.selectedView);
viewHolder.remove = (Button) view.findViewById(R.id.remove);
viewHolder.total = (TextView) view.findViewById(R.id.itemTotal);
viewHolder.quantity = (TextView) view.findViewById(R.id.quantity);
view.setTag(viewHolder);
}else{
viewHolder= (ViewHolder)convertView.getTag();
}


public class AndroidListViewActivity extends ListActivity
{
private ListView listView;
private String names = {
"HV CAPACITOR",
"LV CAPACITORCSS",

};

private String desc = {
"The Powerful Hypter Text Markup Language 5",
"Cascading Style Sheets",

};


private Integer imageid = {
R.drawable.hv_capacitor,
R.drawable.lv_capacitor,

};

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.capacitor_layout);
// storing string resources into Array
String product_name = getResources().getStringArray(R.array.product_name);
// Binding Array to ListAdapter
this.setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, R.id.label, product_name));

ListView lv = getListView();

// listening to single list item on click

lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {

// selected item
String product = ((TextView) view).getText().toString();

// Launching new Activity on selecting single List Item
Intent i = new Intent(getApplicationContext(), SingleListItem.class);
// sending data to new activity
i.putExtra("product", product);
startActivity(i);

}
});


CapacitorList capacitorList = new CapacitorList(this, names, desc, imageid);
listView = (ListView) findViewById(R.id.listView);
listView.setAdapter(capacitorList);

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {

// Launching new Activity on selecting single List Item
Intent i = new Intent(getApplicationContext(), CapacitorList.class);
// sending data to new activity

// startActivity(i);

@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(getApplicationContext(), "You Clicked " + names[i], Toast.LENGTH_SHORT).show();
}
});

}

@Override
public boolean onCreateOptionsMenu (Menu menu){
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected (MenuItem item){
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);


}
}






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Makefile test if variable is not empty

Will Oldham

Visual Studio Code: How to configure includePath for better IntelliSense results