I am wondering why ListView or GridView used old items during scroll? I have a list view more than 500 products i am showing in list view . Each list item has 4 columns the last column is a ImageView showing status as active or inactive. Whenever i marked ImageView as active then after scrolling periodically every item automatically changed its ImageView as active.
Let suppose if i clicked on 6th Items and make its ImageView as active then during scroll i see that 12th , 18th , 24th and so on also changed as active
In order to optimize the scrolling experience, ListView and GridView will re-use item views to avoid having to inflate/instantiate a view for every list item.
That's why getView has a parameter of type View called convertView. When convertView is null, you need to inflate a new view for the item. When it is not null, that means you can re-use this view to avoid the overhead of inflation.
The downside is that this "recycled" item view will have garbage in it from the last time it was displayed, and you have to reset everything in the view to match the list item.
So a common mistake that new Android developers make is to not have a model representation of everything in the view. For example, if your list item can show a status of active or inactive, then the model for your list item should probably have a boolean property called mActive.
The model for the list has to have the entire current state of the list at any given time, so that it can be recreated whenever the ListView decides it needs to redisplay its list items.
So what you need to do is basically four things:
Add the property to your list item model:
boolean mActive; // this can be private with getter/setter
Create an adapter method for changing the state. For example:
public void toggleItemActive(int position) {
mListItem.get(position).mActive = ! mListItem.get(position).mActive;
notifyDataSetChanged();
}
Calling notifyDataSetChanged() here is very important.
Use this property in your getView override:
imageView.setImageResource(item.mActive ? R.drawable.active : R.drawable.inactive); // or however you are doing it
Set the property from your event handler:
listView.setOnItemClickListener(new OnItemClickListener) {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
MyAdapter adapter = (MyAdapter) parent.getAdapter();
adapter.toggleItemActive(position);
}
});
Now your ListView will correctly display your list items.
I think you are using viewholder inside getView() method of your custom adapter. When you are using view holder you will be reusing the views. From your description it looks like, your device can diaply 6 list items at a time. So 6th, 12th (6th position + 6-size of screen), 18th (12th position + 6-size of screen), 24th etc will all be using the same view. Therefore, when 6th item is changed the related 12th, 18th, 24th etc items will also be changed.
Related
I have an expandable list view with checkboxes. When i click a child, an alertdialog and i choose the quantity and then the textView of a child changes. BUT when i scroll down the list and this child disappers from view , the list forget changed textview and set the old one. What's the reason?
ListView (and its descendent ExpandableListView) does NOT create and store the views forever; instead it creates them on-the-fly as needed.
Imagine a scenario where you have a ListView with a list containing 1000 items; but the views for only any 5 items can be visible on the screen at a given time. Do you think that ListView would create and maintain 1000 different views on the screen? That would be a waste of memory, and might cause the UI to lag.
Instead, ListView internally calls getView() function to obtain the view for each item and shows it on the screen. It does this every time the item is brought into the screen display, and only for those many number of items which can fit into the screen at a given time (ListView handles these things internally so you do not have to worry about this)
All you need to do is set the text in the corresponding list item, and use this text to populate the textview in getView(). Maintain a text String and create some form of getText() and setText(String) methods in whatever Object type you are using as an Item.
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
......//initialize text view for this position
Item item = getItem(position);
textView.setText(item.getText());
.......
}
Once you set the text in the list item via alertDialog, Call notifyDataSetChanged() on the adapter to indicate that getView needs to be called again for all the views currently in display.
In your Listener, pass a reference to the adapter of the ListView. When you set the quantity in the alertDialog, just use
{
......
adapter.getItem(position).setText(quantityText);
adapter.notifyDataSetChanged();
......
}
The first line sets the text in the item; the second line tells the adapter that the information in the items have changed and it needs to create the views again.
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
What is the correct way to modify all child elements (not only the visible ones) of a listview.
I have an image which is set, by default, to visibilty gone. I wish to make it visible after the user clicks a button (for all items).
Thanks!
What is the correct way to modify all child elements (not only the visible ones) of a listview.
One thing to understand about a ListView is that not all of the list items are generated (inflated/populated) at any given time.
Suppose, for example, your list Adapter has 1000 items in it but the ListView can only display 10 at once. It would be a very bad waste of resources (e.g., memory) to create all 1000 list items.
Instead, only the 10 visible items are created and each time you scroll one off the top or bottom of the screen, the one which has disappeared is re-cycled by being passed as convertView into the Adapter's getView method.
getView (int position, View convertView, ViewGroup parent)
To do what you are asking you should extend whatever Adapter type you wish to use and override the getView method. In that method check if convertView is null or not. If it is, inflate your own instance of your list item layout. If it is not null then re-use the UI elements (TextView, ImageView etc).
To have all ImageView elements visible, use a global Boolean such as showImageView which will be toggled by the button press. Then use that in getView to decide whether or not to set the visibility of the ImageView.
See Adapter.getView(...)
Probably you should set the image visibility in your ListAdapter's getView() depending on some field value. Upon button clicking you change this field value and then you invoke ListAdapter.notifyDataSetChanged so the List View updates - getView then gets called and image changes because your field value has changed.
Inside the getView() of your adapter, you grab the ImageView and set its visibility to gone:
ImageView iv = (ImageView)convertView.findViewById(R.id.image_view);
iv.setVisibility(buttonClicked ? View.GONE : View.VISIBLE);
Then when users click on the button, set buttonClicked = true, and call notifyDataSetChanged() to refresh the ListView.
I developd a viewpager with custom list views in android. I followed the tutorial View Pager Part 1-3. The major difference in my development is that i used a customlistview in the views of the paager. The list items in the listview also support click events to display more info about an item.
Here is the problem; I handled the onItemClick inside the instantiateItem method but i noticed that when an item is clicked, the content displayed is that of the adjacent listview (i.e if item 2 of page 2 is clicked the info displayed is item 2 of page 3).
I realised this happens because the pager loads adjacent lists so as to make rendering faster for the user so my code uses the preloaded list instead of the list in current display.
I thought to find out the appropriate way to handle listview click events in viewpager, code snippets, links,.. will really be of help. The appropriate method to use to handle the click events.
Thank you
You have to get it from the adapter of the listview (the first parameter of the onItemClick method):
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
// NOT THIS!: mValue = (HereYourClass) listView.getItemAtPosition(position);
mValue = (HereYourClass)a.getAdapter().getItem(position);
}
I want to have one ListView that when I click on the item the view slide out to the left.
So I have:
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
arg1.startAnimation(openAnimation);
}
});
However, animation applied to the different item in the list instead of the one being clicked on. The behavior seems to be random as sometime it happened to more than one item at the same time.
As I suspected this is because of the way Adapter reuse it's view to create item. I went to modify the getView method in my Adapter to inflate new view every time it's being called. Then the animation doesn't happen any more.
Is there a way to resolve this? I tried to move the animation to inside my Adapter but then I can not associate it with other action on the Listview.
Ultimately, I want the item to be clickable but when swipe left/right reveal delete button (iOS delete behavior). Am I on the wrong track here? This should be possible though as Android can implement swipe to remove in the Notification bar.
I recommend that you check this thread also.
I don't think, that this is possible without having to modify your adapter to suit this type of behavior. For what I understand, you don't have any problems with implementing the code recognizing the swipe gestures on different ListView-rows, only with the animation the should follow this gesture on according row(s).
I'd rewrite the adapter to suit at least 2 row types: normal rows, and rows to be deleted. In the "getView()" method of your adapter, you should only reuse the convertView of normal Views. Rows that are to be deleted should not reuse them, so that animating one would not modify the others.
Upon clicking a normal row, you should first tell the adapter that the row on the clicked position is now of type to-be-deleted, call .notifyDatasetChanged(), and then start the animation on that row.