I am using complex list item as following to lazy upload image i am loading every list item's image in separate asyntask so that it won't hang the application while loading image.
problem comes when i scroll down the list new lisitems image had replace image effect when i drill down i found that
for example if by default 5 listitems fit onto the screen when i scroll down next displayed listem is actually the recycle of the previous listitem which was displaying thats why also containing the image of the previous listitem untill new image loaded, after new image loaded from web image get replaced and user found replace image effect when doing scroll up or scroll down every time images are getting replaced due to recycling...
Is there a way i can ask android to do not recycle the listitem when i scroll down or up so that it will not have replace image effect.
any good suggestion is appriciated
alt text http://www.freeimagehosting.net/uploads/8237cbd584.jpg
My understanding is that you use getView() method from your adapter to provide your own layout for list item. So probably you have something like this:
public View getView(int position, View convertView, ViewGroup parent) {
if(null == convertView){
convertView = mInflater.inflate(R.layout.row, null);
//...
}
//... setting new values for your widgets
return convertView
}
If that is the case, then it is your call to recycle items. Such implementation prevents garbage collector to be called to often, but if it's not a problem and you can bear with GC, then you can just get rid of if(null==convertView) and construct your item from the scratch - every time getView method is invoked.
But probably that is not a good idea, it would be better if you retrieve your image widget from recycled item, set some temporary image and then start your async task to download the proper image.
Regads!
Related
I'm using a list view, and I'm using the View holder pattern and recycling views. All working great and as expected. The list scroll smoothly and I'm quite happy with the implementation, besides one thing.
I would like to retain the last 3 views that were scroll off the screen before I get their views to recycle. I would like to do that in order to achieve the following. I have images in my list and I'm loading them asynchronously and they have some fade in animation. It all good as long the user scroll to new items, but I would like that in case that the user scroll back again, that the images would be populated already and won't be loaded again. Of course that I don't want all the previous view to save, but the last few items (lets say three) is reasonable.
Any thoughts on how to achieve it?
Thanks
--EDIT--
To answer some comments. The fade in effect has no influence on the problem (it just more easy to identify it) without the fade in, the image just appear, but the user would see the place holder of the photo (it takes few milliseconds, and it is depends on the current state of the OS).
To solve the problem there should be some caching mechanism to the list view that hold the last X elements that were seen by the user and scrolled off already, and when the user scroll back to those elements the list view should send the convertView of the cached view it saved.
I managed to do that artificially for one element, just to test the concept, and it worked fine. But I have reason to assume that some kind of caching mechanism of elements in a list view it is something that probably implemented already in the ListView itself, or someone already implemented something like that already.
You can think of it as follow. Now the ListView holds the number of elements seen on the screen and the last one that was seen and when there is a need to draw the next element the list view sends the last seen element as the convert view. The solution means that the list view would hold the seen elements + the X elements of the caching and the last element that was scroll off and is not in the caching anymore. When a new element should be drawn the list view will give it the element that is shouldn't be cached anymore, unless the user scroll back to an element that exist in the caching, then the element from the caching would be sent as the convert view
--Edit 2--
public View getView(int position, View convertView, ViewGroup parent)
{
//Get the item
final ItemForListActivity item = itemsListHashtable.get(position);
ViewGroup rowLayout;
//In case that the view is new create it
if (item.getItemType() == Interface.LIST_ITEM_TYPE_CONTACT)
{
ListItemViewHolder viewHolder;
if (convertView == null)
{
rowLayout = (ViewGroup) LayoutInflater.from(context).inflate(R.layout.list_view_item, parent, false);
viewHolder = new ListItemViewHolder();
viewHolder.image1 = (ImageView) rowLayout.findViewById(R.id.image1);
viewHolder.image2 = (ImageView) rowLayout.findViewById(R.id.image2);
//We save the item id that is associate with the holder, for now nothing is associate with it.
viewHolder.itemId = -1;
rowLayout.setTag(viewHolder);
}
//Otherwise just use the converted view
else
{
rowLayout = (ViewGroup) convertView;
viewHolder = (ListItemViewHolder) rowLayout.getTag();
}
//Only in case that the item id is not the one that is already drawn, we need to draw the item
if (viewHolder.itemId != item.getItemId())
{
viewHolder.itemId = item.getItemId();
//Draw image1
drawImage1(viewHolder, item);
//Draw image2
drawImage2(viewHolder, item);
}
}
return rowLayout;
}
I've working on app that need custom list view...
List view should act like some kind of vertical gallery.
In gallery selected item is always on center.
In my app I use navigation keys only or remote controller.
So there is no fling or regular scrolling effect.
I need for example second element in my list to be selected always.
If I want to move up, all elements are scrolled one place up, and selected item has to be changed, but on same place on the screen like previous selected item.
It's the same thing like gallery only vertical.
Is there a simple way to do this?
Is there a vertical gallery implementation?
How can I make gallery widget vertical?
Or how to customize list view to act like that?
Tnx!
try setting onClick Listener on ListView
ListView listView1=(ListView) findViewById(R.id.listView1);
then implemt a class class SearchItemClickListener implements OnItemClickListener
and set listview.onItemClickListenr you will get position and view.
Hope this helps
You can get the current visible first postion by using,
mListView.getFirstVisiblePosition();
after getting the current visible position and its respective data, you can perform your desired task (i.e. display the large image after getting the thumbnail or something like this).
OR
If you don't want to depend on only first or last position and want to select some other visible row then you can do something like this,
public View getView(int position, View convertView, ViewGroup parent)
{
int visiblePosition = position % nObjects; // nObjects if the total number of objects to display
if(visiblePosition == yourDesiredPosition){
// do your custom work here.
}
}
Here is a demo for vertical slide show,
Hope this will give you some hint about implementing your functionality.
I've managed this using setSelected() method on ListView item...and managing key listener.
Didn't found smarter way to handle this.
I have an adapter for a ListView which dynamically gets relative layouts to display images and text loaded into the Adapter.
The problem is that when I scroll in its entirety, the Listview is ordered incorrectly. I tried using ViewHolder but that has not solved the problem.
I wonder if there is any other way to do it. Or integer Listview load without execute code that once adapter has already been loaded list.
The main problem is that the images of the other RelativeLayout layouts appear in the wrong list.
Now The problem I have now is that when you scroll through the list slowly shows fine, but if the scroll in the list is fast elements are dislodged. As if not process fast enough position parameter. public View getView(int position, View convertView, ViewGroup parent)
Check your ListAdapter's getView(); make sure that if convertView is null that you are creating it. In both cases you want to update convertView with new data, otherwise you'll have these issues (as Android will recycle those views but not update them).
In a custom adapter, how to know weither I need to reconfig the convertView or not?
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = (ImageView) mInflater.inflate(R.layout.avatar, parent, false);
// Should this 2 lines of code be here ?
User user = mUserList.getUserAt(position);
user.setAvatar(imageView);
} else {
imageView = (ImageView) convertView;
}
// or here ?
User user = mUserList.getUserAt(position);
user.setAvatar(imageView);
return imageView;
}
I would think that if it is recycled, I would not need to reset the user's avatar but I often see the configuration happening outside of the if block. Why is that?
I always configure the view outside of if block. This convertView that you get in the getView method might (and most probably will) be set up for another user by some previous call to getView because of ListView's policy to reuse item views when they go offscreen. If you do not set up a proper avatar you will have wrong one for this item. Of course you won't need to reset properties that are independent of the concrete position like background.
For example CursorAdapter separates getView into two parts: newView, that performs inflate and (mostly) position-independent setup and bindView that assigns actual position-dependent data.
It may also happen that you will get exactly the same view that you used for this position earlier. Of course you can avoid resetting a view in this case, but you need to check if data in this view are valid. Setting and then checking View's tag comes to my mind as a most obvious solution.
It is not truly recycled, it just means that you need to fill a "recycled" view with new data according to the its new position. If you don't do it this row will be filled with old data that shouln't be visible on the screen anymore since you scrolled away its position.
So in short you have to reconfigure view with fresh data each time getView() called (outside of if block in your code).
Your issue is only with the understanding of listview.
So here I'll make you clear how it works??
Let's say listview has to contain 20 items but your current screen can accommodate(show on screen) only 8 items(list items, in your case imageview).
When the listview tried to get items for 1----8th it will return you convertView as null because no recycling of objects happened yet.
but, when you try to scroll, in our case(scroll up!).
the 1st element of the list will be recycled when go out of screen, and will be supplied as convertView for 9th item.
In this way listview has to manage only 8th(in our case) to show any number of items.
The opposite will happen if we will scroll down wards.
So, on the basis of convert view (null or not) you have to design your logic either to create and fill or to fill.
Hope this will help you.
I have a ListView with a custom adapter, displaying information from a database.
When I start the app, the data is read from the database, given to the adapter, which is then attached to the ListView. A LayoutAnimationController is run on the ListView, displaying the contents smoothly.
this._lvList = (ListView)_v.findViewById(R.id.lvList);
this._lvList.setAdapter(new TableAdapter(getActivity(),R.layout.tablerow,Category.getCategories()));
LayoutAnimationController lac = new LayoutAnimationController(AnimationUtils.loadAnimation(getActivity(), R.anim.slide_in_left));
lac.setDelay(0.2f);
this._lvList.setLayoutAnimation(lac);
this._lvList.startLayoutAnimation();
No problem at all.
Now when I click on any entry of the ListView, the entries which were not clicked disappear, the clicked entry becomes the one and only entry in the list and is displayed at the top, as expected.
View v;
for(int i = 0;i<_lvList.getChildCount();i++) {
v = this._lvList.getChildAt(i);
if(v!=clickedView) {
Animation animSlideOut = AnimationUtils.loadAnimation(getActivity(), i%2==0?R.anim.slide_out_left:R.anim.slide_out_right);
animSlideOut.setStartOffset(i*50);
// some more stuff
}
v.startAnimation(animSlideOut);
}
This works as well, but now the problem, if I click again on that single list entry, I want the list to repopulate, displaying all items again.
I thought I could use the code from the start (the first snippet), as it works fine when starting the app, but...this time...it doesn't. No animation happening. The reason is, there are no views to animate in my ListView (except the one from the previous step).
Instead of creating a new Tableadapter I already tried to clear it, fill it new, called notifyDataSetChanged()... no use, as
_lvList.getChildCount();
stills returns 1 view, while the adapter holds all 18 entries.
I as well tried
requestLayout();
forceLayout();
invalidate();
invalidateViews();
to force the ListView to generate its child views before the animation, but it's not working. So atm the ListView just appears instantly, somewhen after my call to the layout animation.
Summary : my ListView contains no child views before the start of the layout animation, how can I force it to generate them?
Thx in advance :)
Hmm... have you done this yet? (below)
in your custom adapter, override the method:
#Override
public View getView(int position, View convertView, ViewGroup parent){
//the position of the item, the convertView is the view layout of what that row is
}
to change how the listview is updated. this getView method gets called by Android every once a while to refresh the view (you don't call it manually)
also, i think you only need to call
listView.notifiyDataSetChanged() and listView.invalidate() to force the update, after you repopulate the list
you can read more on listviews here:
http://www.vogella.com/articles/AndroidListView/article.html
http://developer.android.com/guide/topics/ui/layout/listview.html