Using Picasso Library with Listview

How to use Picasso with listview?

I would recommend using the ViewHolder pattern.
more information can be found here :

https://developer.android.com/training/improving-layouts/smooth-scrolling.html

take note that you do have to handle a case where your img_url is indeed empty, you need to set a default image for that case, if not, when that cell is recycled the image will remain unchanged and then you will experience that duplication you were talking about.

example code below :

@Override
public View getView(int position, View convertView, ViewGroup viewGroup) {

ViewHolder holder;

if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.widgets_list, null);
holder = new ViewHolder();
holder.mImageView = (ImageView) convertView.findViewById(R.id.iconImageView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}

//fetching your data object with the current position
Example example = mExamples[position];

String img_url = example.get(i).img;

if (!img_url.equals("")){
Picasso.with(context).load(img_url).into(holder.mImageView);
} else {
//todo - implement a default image in case img_url is indeed empty
Picasso.with(context).load(defaultImage).into(holder.mImageView);
}

return convertView;
}

private static class ViewHolder {
ImageView mImageView;
}

Using Picasso library with ListView

There are 2 things you need to change:
1) History.icon should be the String url of the icon, not a Picasso object. You can also use a File, Uri, or int, but a String url is probably what you want.

2) Modify your Adapter's getView() method to load the icon using Picasso (see the last line before getView() returns the convertView):

public class HistoryAdapter extends ArrayAdapter<History> {

Context context;
int layoutResId;
History data[] = null;

public HistoryAdapter(Context context, int layoutResId, History[] data) {
super(context, layoutResId, data);
this.layoutResId = layoutResId;
this.context = context;
this.data = data;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
HistoryHolder holder = null;

if(convertView == null)
{
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
convertView = inflater.inflate(layoutResId, parent, false);

holder = new HistoryHolder();
holder.imageIcon = (ImageView)convertView.findViewById(R.id.icon);
holder.textTitle = (TextView)convertView.findViewById(R.id.gameType);
holder.textScore = (TextView)convertView.findViewById(R.id.score);

convertView.setTag(holder);
}
else
{
holder = (HistoryHolder)convertView.getTag();
}

History history = data[position];
holder.textScore.setText(history.score);
holder.textTitle.setText(history.gametype);
Picasso.with(this.context).load(history.icon).into(holder.imageIcon)

return convertView;
}

static class HistoryHolder
{
ImageView imageIcon;
TextView textTitle;
TextView textScore;
}
}

How to correctly implement a custom listview with images using Picasso library?

1. Is there an example of how to do this properly?

Your code looks pretty close to perfect. The Adapter's getView method is usually the critical path to optimize. Compare for example Picasso's own example SampleListDetailAdapter.java. The important points it (as well as your code) does

  • check for & re-use already inflated views, inflation is expensive.
  • use ViewHolder so you don't have to call findViewById every time. Not terribly expensive on simple views. Also cached afaik.
  • Picasso.with(context).load(url)... each time you need to display an image. This should finish instantly but still use caches and other magic.

There are some minor optimizations you can add, but I doubt that there are noticeable or even measurable changes:

pure style change: use BaseAdapter#getItem(position). This method
exists for you only. The framework doesn't use it.

   @Override
public MyMenuItem getItem(int position) { // << subclasses can use subtypes in overridden methods!
return menuItems.get(position);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
...
MyMenuItem row_pos = getItem(position);

Use a sane id method

@Override
public long getItemId(int position) {
return menuItems.indexOf(getItem(position));
}

is equivalent to

@Override
public long getItemId(int position) {
return position;
}

but now infinitely faster. indexOf(Object) scales really badly with the number of objects.

Cache objects that don't change:

MenuAdapter(Context context, List<MyMenuItem> menuItems) {
this.mLayoutInflater = LayoutInflater.from(content);
this.mPicasso = Picasso.with(context);
}
..
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.menu_item, null);
...
mPicasso
.load(row_pos.getItem_image_url())
.into(holder.ivMenu);

2. Is there a way to prevent listview items being destroyed when they are out of screen?

No(*).

..(*) well you can essentially cache the result of getView e.g. in LruCache(position, View) or LruCache(MyMenuItem, View), then don't touch the convertView - they need to remain unconverted or you would kill those views in your cache. Also

@Override
public int getItemViewType(int position) {
return Adapter.IGNORE_ITEM_VIEW_TYPE;
}

seemed to be required because the standard adapter using code assumes that views it removes from visibility are gone. They are not and messing with them messes with your cache and caused weird display problems for me.

3. If so, will it cause problems to keep too many items?

Yes. This behavior is not intendend / expected. There is also more or less nothing you gain. You might be able to save you the call to holder.tvMenuHeader.setText(). Likewise the one to Picasso but both of them should complete instantly. Picasso should have your image cached already. By caching all Views you essentially add another cache that also contains all the images. I would rather check that the picasso cache works as intended and holds most items. The only reason you may want to do it with view caching is for cases that require complicated setup of the view, so it becomes worth caching the completely constructed view rather than just some content parts.

Profile

Profiling can actually tell you where you can / need / should improve. The first to look at IMO is traceview. You'll see if code blocks the main thread which results in choppy list scrolling. If you're doing complicated views and you see that the draw methods are executed most of the time, profile them as well.

  • http://www.curious-creature.org/docs/android-performance-case-study-1.html
  • http://blog.venmo.com/hf2t3h4x98p5e13z82pl8j66ngcmry/performance-tuning-on-android
  • http://www.vogella.com/tutorials/AndroidTools/article.html

when i using Picasso in customadapter for listview image, when scroll down and back previous place listview, my image mix other position items

You need add else for

if (!worldParametrs.get(position).getPic1().equals("") ) {
//your code
} else {
holder.Imgmelk.setDrawable(null)
}

Android Picasso - Image doesn't load to listview

It seems that this problem arise due to the IP address of server that storse the images. I should have placed the IP address in numeric (such as 192.168.x.x) instead of localhost.



Related Topics



Leave a reply



Submit