I load data from Cursor to listview, but my Listview not really display "smooth". The data change when I drag up and down on the scollbar in my ListView. And some items look like duplicate display in my list.
I hava a "complex ListView" (two textview, one imageview) So I used newView(), bindView() to display data. Can someone help me?
I will describe you how to get such issue that you have. Possibly this will help you.
So, in list adapter you have such code:
public View getView(int position, View contentView, ViewGroup arg2)
{
ViewHolder holder;
if (contentView == null) {
holder = new ViewHolder();
contentView = inflater.inflate(R.layout.my_magic_list,null);
holder.label = (TextView) contentView.findViewById(R.id.label);
contentView.setTag(holder);
} else {
holder = (ViewHolder) contentView.getTag();
}
holder.label.setText(getLabel());
return contentView;
}
As you can see, we set list item value only after we have retrieved holder.
But if you move code into above if statement:
holder.label.setText(getLabel());
so it will look after like below:
if (contentView == null) {
holder = new ViewHolder();
contentView = inflater.inflate(R.layout.my_magic_list,null);
holder.label = (TextView) contentView.findViewById(R.id.label);
holder.label.setText(getLabel());
contentView.setTag(holder);
}
you will have your current application behavior with list item duplication.
Possibly it will help.
ListView is a tricky beast.
Your second question first: you're seeing duplicates because ListView re-uses Views via convertView, but you're not making sure to reset all aspects of the converted view. Make sure that the code path for convertView!=null properly sets all of the data for the view, and everything should work properly.
You'll want your getView() method to look roughly like the following if you're using custom views:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final MyCustomView v = convertView!=null ? (MyCustomView)convertView : new MyCustomView();
v.setMyData( listAdapter.get(position) );
return v;
}
If you're not using your own custom view, just replace the call to new MyCustomView() with a call to inflater.inflate(R.layout.my_layout,null)
As to your first question, you'll want to watch Romain's techtalk on ListView performance here: http://code.google.com/events/io/sessions/TurboChargeUiAndroidFast.html
From his talk and in order of importance from my own experience,
Use convertView
If you have images, don't scale your images on the fly. Use Bitmap.createScaledBitmap to create a scaled bitmap and put that into your views
Use a ViewHolder so you don't have to call a bunch of findViewByIds() every time
Decrease the complexity of the views in your listview. The fewer subviews, the better. RelativeLayout is much better at this than, say, LinearLayout. And make sure to use if you're implementing custom views.
I'm facing this problem as well, but in my case I used threads to fetch the external images. It is important that the current executing thread do not change the imageView if it is reused!
public View getView(int position, View vi, ViewGroup parent) {
ViewHolder holder;
String imageUrl = ...;
if (vi == null) {
vi = inflater.inflate(R.layout.tweet, null);
holder = new ViewHolder();
holder.image = (ImageView) vi.findViewById(R.id.row_img);
...
vi.setTag(holder);
} else {
holder = (ViewHolder) vi.getTag();
}
holder.image.setTag(imageUrl);
...
DRAW_MANAGER.fetchDrawableOnThread(imageUrl, holder.image);
}
And then on the fetching thread I'm doing the important check:
final Handler handler = new Handler() {
#Override
public void handleMessage(Message message) {
// VERY IMPORTANT CHECK
if (urlString.equals(url))
imageView.setImageDrawable((Drawable) message.obj);
};
Thread thread = new Thread() {
#Override
public void run() {
Drawable drawable = fetchDrawable(urlString);
if (drawable != null) {
Message message = handler.obtainMessage(1, drawable);
handler.sendMessage(message);
}
}};
thread.start();
One could also cancel the current thread if their view is reused (like it is described here), but I decided against this because I want to fill my cache for later reuse.
Just one tip: NEVER use transparent background of item layout - it slows performance greatly
You can see the reocurring text in multiple rows if you handle it in the wrong way. I've blogged a bit about it recently - see here. Other than that you might want to take a look at ListView performance optimization. Generally it's because of the view reuse and I've seen it few times already.
Related
I have a custom adapter that list my items. in each Item I check database and draw some circles with colors.
As you see in code I check if convertView==null defines new viewHolder and draw my items. but when I scroll listview very fast every drawn data ( not title and texts) show wrongs!
How I can manage dynamic View creation without showing wrong data?!
UPDATE
This is my attempts:
I used ui-thread to update my list but the result is same and data drawing go wrong.
in second I try to load all data with my object so that there is no need to check db in adapter. but it problem is still remains...
finally I create the HashMap<key,LinearLayout> and cache every drawn layout with id of its item. So if it's drawn before I just load its view from my HashMap and every dynamic layout will create just once. But it still shows wrong data on fast scrolling! Really I don't know what to do next!
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
final MenuStructureCase item = getItem(position);
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = this.mInflater.inflate(R.layout.fragment_menu_item, null);
viewHolder.menu_title = (TextView) convertView.findViewById(R.id.menu_title);
viewHolder.tag_list_in_menu_linear_layout = (LinearLayout) convertView.findViewById(R.id.tag_list_in_menu_linear_layout);
viewHolder.menu_delete = (ImageButton) convertView.findViewById(R.id.image_button_delete);
importMenuTags(viewHolder, getItem(position), viewHolder.tag_list_in_menu_linear_layout);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.menu_title.setText(item.getTitle());
}
return convertView;
}
and this is importMenuTags():
private void importMenuTags(ViewHolder viewHolder, MenuStructureCase item, LinearLayout layout) {
List<String> tags = db.getMenuTags(item.getTitle()); //this present list of string that contain my tags
for (String tag : tags) {
Drawable drawable = getContext().getResources().getDrawable(R.drawable.color_shape);
drawable.setColorFilter(Color.parseColor(each_tag_color), PorterDuff.Mode.SRC_ATOP);
RelativeLayout rl = new RelativeLayout(getContext());
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
lparams.setMargins(15, 15, 15, 15);
lparams.width = 50;
lparams.height = 50;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
rl.setBackground(drawable);
} else {
rl.setBackgroundDrawable(drawable);
}
rl.setLayoutParams(lparams);
layout.addView(rl);
}
}
You have to select data from db before adapter initialization. So that
getItem(position)
will return already a "ready" item-object.
You shouldn't set the values to Views inside
if (convertView == null) {
...
}
This code is only for a viewHolder initialization. You create a new one, if convertView is null or read it as tag.
Setting of values you have to do after viewHolder initialization, actually where you set the title.
But in order to increase a performance, you shouldn't select the values from db on each step of getView. You have to have everything prepared (already selected).
You can do this way:
First of all create method inside adapter class:
public void updateNewData(List<MenuStructureCase> newList){
this.currentList = newList;
notifyDataSetChanged();
}
Now call above method whenever you want to update ListView.
How to call with object of CustomAdapter:
mAdapter.updateNewData(YourNewListHere);
Hope this will help you.
Rendering of data takes times and may be that's causing the issue when you are scrolling fast.
You can restirct the scrolling ( like Gmail : use a pull to refresh ) so that a less amount to data is processed in a list view at single time .
use RecyclerView instead of listview for better performance
ListView recreates the view on scrolling .
May be you can explain more about your problem , then we can provide the inputs accordingly.
I have the following situation.
I have a ListView, each item of the ListView is comprised of different widgets (TextViews, ImageViews, etc...) inflated form a Layout in the getView() method of the custom adapter.
Now, I would like to achieve the following:
when a certain event is triggered I want to change the background of a View which is inside the item.
Please how do I do it?
This is the the Item Layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/cardlayout"
android:layout_width="320dp"
android:layout_height="130dp"
android:background="#android:color/transparent"
android:orientation="vertical"
android:paddingBottom="5dp"
android:paddingRight="5dp"
android:paddingTop="5dp" >
<FrameLayout
android:layout_width="320dp"
android:layout_height="117dp" >
<View
android:id="#+id/card"
android:layout_width="320dp"
android:layout_height="117dp"
android:background="#drawable/card_selector" />
</FrameLayout>
</LinearLayout>
I need to change the background of card
I have tried doing this:
View v=lv.getAdapter().getView(index, null, lv);
View card =(View)v.findViewById(R.id.card);
card.setBackgroundResource(R.drawable.pressed_background_card);
But no success :-((
When your event is triggered you should just call a notifyDataSetChanged on your adapter so that it will call again getView for all your visible elements.
Your getView method should take into account that some elements may have different background colors (and not forget to set it to normal color if the element doesn't need the changed background, else with recycling you would have many elements with changed background when you scroll)
edit :
I would try something like this :
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView == null)
{
convertView = LayoutInflater.from(getContext()).inflate(R.layout.card, parent, false);
}
//This part should also be optimised with a ViewHolder
//because findViewById is a costly operation, but that's not the point of this example
CardView cardView =(CardView)convertView .findViewById(R.id.card);
//I suppose your card should be determined by your adapter, not a new one each time
Card card = getItem(position);
//here you should check sthg like the position presence in a map or a special state of your card object
if(mapCardWithSpecialBackground.contains(position))
{
card.setBackgroundResource(specialBackground);
}
else
{
card.setBackgroundResource(normalBackground);
}
cardView.setCard(card);
return convertView;
}
And on the special event i would add the position of the item into the map and call notifyDataSetChanged.
Use the onitemclicklistener which has method onclicksomething..that takes four or five parameters. (View parent, View view, int position, int id). Use the view parameter to customize your background.
Update
Here's some of my code, If you don't understand I recommend to read about recycling and ViewHolder pattern.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
{
ViewHolder viewHolder;
// If convertView isn't a recycled view, create a new.
if(convertView == null){
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.row_gallery_frame, parent, false);
viewHolder = new ViewHolder();
// Here you must be able to find your Widget inside convertView and set a listener to it I guess?
viewHolder.nameHolder = (TextView) convertView.findViewById(R.id.nameTv);
// Set a reference to newly inflated view
convertView.setTag(viewHolder);
}
// If it is, then get the ViewHolder by tag
else{
viewHolder = (ViewHolder)convertView.getTag();
}
// Set the data
GalleryFrame galleryFrame = galleryFrameArrayList.get(position);
viewHolder.nameHolder.setText(galleryFrame.getName());
return convertView;
}
}
// Viewholder pattern which holds all widgets used
public static class ViewHolder{
public TextView nameHolder;
}
I assume you have a model object that you use to "draw" the list item , and for example the background color is determined based on a boolean or something.
All you need to do, is change the value on which you base your decision which background color should that TextView have.
Your getView() method should have code like that
if (myModelObj.isBrown()) {
myTextView.setBackgroundResource(R.drawable.brown_bg);
else
myTextView.setBackgroundResource(R.drawable.not_brown_bg);
All you should do when ur event is triggered, is set the value of the brown boolean in your model
and call notifyDataSetChanged() on your adapter
EDIT
If for some reason you don't wanna call nofitfyDataSetChanged(), althought it won't move the scroll position of your list and with the right recyclying it won't cause bad performance
You can find the View object that represent the list item you want to edit-if it's visisble-, and simply change the background in it, without refreshing the list at all.
int wantedPosition = 10; // Whatever position you're looking for
int firstPosition = listView.getFirstVisiblePosition() - listView.getHeaderViewsCount();
int wantedChild = wantedPosition - firstPosition
if (wantedChild < 0 || wantedChild >= listView.getChildCount()) {
// Wanted item isn't displayed
return;
}
View wantedView = listView.getChildAt(wantedChild);
then use wantedView to edit your background
This answer can be found here
try this one:
View v=lv.getAdapter().getView(index, null, lv);
View card =(View)v.findViewById(R.id.card);
card.setBackgroundResource(R.drawable.pressed_background_card);
card.invalidate();
v.invalidate();
those function force your views to redraw itself and they will render again.
look at invalidate()
What I normally do is this:
public static class EventDetailsRenderer {
private TextView title;
private TextView description;
private Event item;
public EventDetailsRenderer(View view) {
extractFromView(view);
}
private final void extractFromView(View view) {
title = (TextView) view.findViewById(R.id.EventTitle);
description = (TextView) view.findViewById(R.id.Description);
}
public final void render() {
render(item);
}
public final void render(Event item) {
this.item= item;
title.setText(item.getTitle());
description.setText(item.getDescription());
}
}
private class EventsAdapter
extends ArrayAdapter<Event> {
public EventsAdapter(Context context) {
super(context, R.layout.list_node__event_details, 0);
}
public void addAllItems(Event... services) {
for (int i = 0; i < services.length; i++) {
add(services[i]);
}
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Event event = getItem(position);
EventDetailsRenderer eventRenderer;
if (convertView != null && convertView.getTag() != null) {
eventRenderer = (EventDetailsRenderer) convertView.getTag();
} else {
convertView = getActivity().getLayoutInflater().inflate(R.layout.list_node__event_details, null);
eventRenderer = new EventDetailsRenderer(convertView);
convertView.setTag(eventRenderer);
}
eventRenderer.render(event);
return convertView;
}
}
NOTE: that this example might not compile I pasted it from some code I have and deleted some lines to show an example but the logic it the same.
And then when you want to render it, just get the children from the list, iterate over them, check if the renderer contains the card you want to flip and call its render method... then you render a specific item in the list without effecting the rest of the items.
Let me know if this works...
Adam.
User EasyListViewAdapters library https://github.com/birajpatel/EasyListViewAdapters
Features
Easier than implementing your own Adapter (ie handling
BaseAdaper#getView).Very Easier to provide multi-row support.
Library takes care of recycling all views, that ensures performance
& helps your list view scroll smoothly.
Cleaner code. By keeping different RowViewSetter classes for
different row-types makes your code easy to manage & easy to reuse.
No data browsing, Library takes care of browsing data through
data-structure when View is being drawn or event occurs so that
Users does not have to look for their data to take actions.
Just by passing correct row-types library will Auto-map your
data-types to row-types to render views. Row views can be created by
using XML or Java (doesn't restrict to XML-Only Approach).
Load More callbacks can be registered to implement paginatation
support to your list.
Handling children viewclicks, you can also register for
Children(present inside your rows) view click events.
All these Views are registered with single OnClickListner so that
this mechanism is very memory efficient when click event occurs
users you gets clickedChildView, rowData,int eventId as callback
params.
I'm having trouble with a listview in android. When I start scrolling down my List, it is very slow and I see that the GC is called. When I'm at the bottom of my List, everything works fine and smooth. I think that at this point my ViewHolder does the work.
But I can't find the source that is calling the GC. I searched which lead to:
DDMS 436816 byte[] 1 android.graphics.Bitmap nativeCreate
I can't interpret that line. My ArrayAdapter and it's getView method looks like this:
public class DiagnoseAdapter extends ArrayAdapter<Visualizer> {
#Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = TYPE_DEFAULT;
final Visualizer item = getItem(position);
switch(item.getType()){
case TYPE_DEFAULT:
convertView = DefaultTextView.getView(position, convertView, mlayoutInflater, item, parent);
break;
// more cases/types
}
return convertView;
}
}
which is calling the following getView Method of the class DefaultTextView
public class DefaultTextView{
public static View getView(int position, View convertView, LayoutInflater layoutInflater, Visualizer item, ViewGroup parent){
ViewHolder holder;
if (convertView == null || item.getReleatedObject() == null || convertView.getTag()!=TAG_DEFAULT) {
convertView = layoutInflater.inflate(R.layout.diagnose_item, null);
holder = new ViewHolder();
holder.value = (TextView) convertView.findViewById(R.id.diagnose_function_value);
holder.name = (TextView) convertView.findViewById(R.id.diagnose_function_setname);
holder.mLinLayout = (LinearLayout) convertView.findViewById(R.id.default_linlayout);
convertView.setTag(TAG_DEFAULT);
convertView.setTag(R.layout.diagnose_item,holder);
item.setReleatedObject(convertView);
} else {
holder = (ViewHolder) convertView.getTag(R.layout.diagnose_item);
}
holder.value.setText(item.toString());
holder.name.setText(item.getToolTip());
holder.mLinLayout.removeAllViews();
if (item.getUpdateFlag(4)) {
if (holder.back == null){
holder.back = new ImageView(convertView.getContext());
holder.back.setScaleType(ScaleType.FIT_CENTER);
holder.back.setAdjustViewBounds(true);
holder.back.setImageBitmap(bm1);
}
holder.mLinLayout.addView(holder.back);
}
if (item.getUpdateFlag(1)) {
if (holder.update == null){
holder.update = new ImageView(convertView.getContext());
holder.update.setScaleType(ScaleType.FIT_CENTER);
holder.update.setAdjustViewBounds(true);
holder.update.setImageBitmap(bm2);
}
holder.mLinLayout.addView(holder.update);
}
if (item.getUpdateFlag(2)) {
if (holder.timer == null){
holder.timer = new ImageView(convertView.getContext());
holder.timer.setScaleType(ScaleType.FIT_CENTER);
holder.timer.setAdjustViewBounds(true);
holder.timer.setImageBitmap(bm3)
}
holder.mLinLayout.addView(holder.timer);
}
if (item.getUpdateFlag(3)) {
if (holder.log == null){
holder.log = new ImageView(convertView.getContext());
holder.log.setScaleType(ScaleType.FIT_CENTER);
holder.log.setAdjustViewBounds(true);
holder.log.setImageBitmap(bm4);
}
holder.mLinLayout.addView(holder.log);
}
if (item.getUpdateFlag(0)) {
if (holder.forward == null){
holder.forward = new ImageView(convertView.getContext());
holder.forward.setScaleType(ScaleType.FIT_CENTER);
holder.forward.setAdjustViewBounds(true);
holder.forward.setImageBitmap(bm5);
}
holder.mLinLayout.addView(holder.forward);
}
return convertView;
}
static class ViewHolder {
TextView name, value;
ImageView back, update, timer, log, forward;
LinearLayout mLinLayout;
}
}
Even if I comment the LinearLayout out, so I just have a List with two TextViews.
So my Question. Do I miss anything. Some stupid thing? How do I get my ListView smoother?
BTW: I read in a different thread, that it is happening if the ListView has the attribute android:cacheColorHint="#00000000. I don't have this attribute.
I hope anyone has a solution. Thanks!
About the source of GC calls. If I'm understanding your code correctly, everytime your ListView items are recycled and you call removeAllViews(), a previously dynamically created ImageView is removed and its Bitmap is garbage collected. So, Maybe those GC calls would be avoided if you use the same ImageView declaring it in your xml layout and just replace the Bitmap according to your getUpdateFlag().
And two more things about ListViews and Images. First thing is that if the image is too big, your ListView is going to be laggy no matter what. You would need to scale the image down if that is the case( Loading Large Bitmaps Efficiently). And second, maybe you would also need to implement a Lazy List, which loads images on demand, there is a famous question about that --> How do I do a lazy load of images in ListView?
I've finally solved my Problem. Like above, I thought the problem was based on the images of my list items. But that wasn't the problem. I just didn't use my ViewHolders and the getItemViewType(int position) method correctly. I have a list with many different item layouts and I saw, that my code above created a new convertView and a new ViewHolder for every single item, which wasn't supposed to be. I found a great tutorial about how to use multiple item layouts (see link below):
Multiple List Item Layouts
We are working with list views in college at the moment. My lecturer gave us a simple application that displays mail messages in a list and when the user selects one it displays the content of the message in a new activity. I understand pretty much all of what is going on but there are a few grey areas I want to clear up!
Basically I am wondering what this section of code does?
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.inbox_row, null);
}
This method is located within a class that extends ArrayAdapter. Am I right in thinking that it is some form of recycling? for when views go on and off the screen?....
Any help is much appreciated. thanks.
it's exactly what you said, a form of recycling.
Inflating a layout takes a lot of memory and a lot of time, so for the efficiency sake, the system passes to you that just went off the screen and you can simply update its text and images and give them back to the UI.
So for example, if your list view is showing 6 items on its list (due to the height of it), it will only inflate 6 items and during scroll it just keeps recycling them.
there's some extra optimisations tricks that you should use and I'm sure that the video link that the commenter posted will explain them.
edit
that example is an ArrayAdapter of Store items, but you can make it to whatever you need.
the adapter does the match and separation layer between UI and data.
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = newView();
// Store is the type of this ArrayAdapter
Store store = getItem(position);
Holder h = (Holder) convertView.getTag();
// And here I get the data and address them to the UI
// as you can see, if the convertView is not null,
// I'm not creating a new one, I'm just changing text, images, etc
h.storeName.setText(store.getStoreName());
h.address.setText(store.getAddressLine1());
h.postcode.setText(store.getPostCode());
h.distance.setText(store.getDistance());
return convertView;
}
// I like to separate in a different method when the convertView is null
// but that's me being organisation obsessive
// but it also makes easy to see which methods are only being called the 1st time
private View newView() {
LayoutInflater inf = LayoutInflater.from(getContext());
View v = inf.inflate(R.layout.map_result_list, null);
Holder h = new Holder();
// here we store that holder inside the view itself
v.setTag(h);
// and only call those findById on this first start
h.storeName = (TextView) v.findViewById(R.id.txtLine1);
h.address = (TextView) v.findViewById(R.id.txtLine2);
h.postcode = (TextView) v.findViewById(R.id.txtLine3);
h.distance = (TextView) v.findViewById(R.id.txtDistance);
return v;
}
// this class is here just to hold reference to the UI elements
// findViewById is a lengthy operation so this is one of the optimisations
private class Holder {
TextView storeName;
TextView address;
TextView postcode;
TextView distance;
}
I have a ListView with custom ArrayAdapter. Each of the row in this ListView has an icon and some text. These icons are downloaded in background,cached and then using a callback, substituted in their respective ImageViews. The logic to get a thumbnail from cache or download is triggered every time getView() runs.
Now, according to Romain Guy:
"there is absolutely no guarantee on
the order in which getView() will be
called nor how many times."
I have seen this happen, for a row of size two getView() was being called six times!
How do I change my code to avoid duplicate thumbnail-fetch-requests and also handle view recycling?
Thanks.
Exactly, that could happen for example when you have
android:layout_height="wrap_content"
in your ListView definition. Changing it to fill_parent/match_parent would avoid it.
From api.
public abstract View getView (int position, View convertView,
ViewGroup parent)
convertView - The old view to reuse, if possible. Note: You should check that this view is non-null and of an appropriate type before using. If it is not possible to convert this view to display the correct data, this method can create a new view.
So if getView has already been called for this specific index then convertView will be the View object that was returned from that first call.
You can do something like.
if(!(convertView instanceof ImageView)){
convertView = new ImageView();
//get image from whereever
} else {} // ImageView already created
I m experiancing the same issue i change the layout_height of listView to match_parent resolve my issue.
My understanding is that you need to use the ViewHolder design pattern here. Just using a returned convertView can lead to reuse of a previous view (with some other image assigned in this case).
public class ImageAdapter extends ArrayAdapter<String> {
// Image adapter code goes here.
private ViewHolder {
public ImageView imageView;
public String url;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = null;
ViewHolder viewHolder;
String url = getUrl(position);
if (convertView == null) {
// There was no view to recycle. Create a new view.
view = inflator.inflate(R.layout.image_layout, parent, false);
viewHolder = new ViewHolder();
viewHolder.imageView = (ImageView) view.findViewById(R.id.image_view);
viewHolder.url = url;
view.setTag(viewHolder);
} else {
// We got a view that can be recycled.
view = convertView;
viewHolder = ((ViewHolder) view.getTag());
if (viewHolder.url.equals(url)) {
// Nothing to do, we have the view with the correct info already.
return view;
}
}
// Do work to set your imageView which can be accessed by viewHolder.imageView
return view;
}
}
The better would be to create a object with Thumbnail(bitmap) and the text. And read the thumbnail if its not available in the object.
Create an array of ImageView objects in your adapter and cache them as you retrive them (whether from cache or web). For example, in getView, before you fetch the ImageView, check if it's already in your local array, if so, use it, if not fetch, once received store in your local ImageView array for future use.
My Fragment.xml has a ListView, the layout setting of this ListView was android:layout_height="wrap_content", and this ListView will bind to SimpleCursorAdapter later. Then I have the same issue in ViewBinder be called 3 times. The issue resolved after I change the layout_height="wrap_content" to "95p". I do consider that the "wrap_content" height cause this issue.
Trying to modify your Fragment.xml and I guess the 3 times called issue will no longer exist.