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.
Related
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.
In my code I have fragment and gridview in it. Also I have ArrayAdapter for this Gridview. Now I need to change background color of grid cell on click. I do this by setting onClickListener.
Question is what is difference between setting onClickLister for GridView cell in adapter and in fragment?
I'm gonna try to clarify the different android entities involved in your question a little.
A GridView is a View.
You can assign Click Listeners to Views so they react to a user click. Any view has a generic clicklistener (View.setOnClickListener) that gets called whenever the user clicks on any part of the view
Complex views can have several other more specialized clicklisteners, for example, menu-like views (ListViews, GridViews, etc) will have also a setOnItemClickListener / setOnItemLongClickListener that gets called whenever the user clicks on an item (vs. the whole view)
An Adapter is just a class whose purpose is to build views with data to data-consuming views. For example, your GridViewAdapter: It will get called once for every row and it will construct each Row View (in the getView method). Every Row View will be (probably) a ViewGroup (FrameLayout/RelativeLayout...) with some other views inside (ie. Icon ImageView, name TextView, address...)
So the adapter itself doesn't accept clicklisteners. But the Views created by the adapter can! For example, let's assume your GridView is a Phone List:
Your GridView has an ItemClickListener to react to the selected phone list entry and show info about the contact
Your GridView adapter builds views for every row. Imagine your 'contact' rows have 3 views: A title, an Icon, and a button to delete the contact
Inside your adapter, you will assign an onClickListener to the "delete contact" button View. Mind you always assign onClickListeners to Views, not to the adapter itself ("you can't click an adapter!")
About Fragments, think of them as "sub-activities". A fragment contains a root layout with several views. Again, it will be in those views where you assign the clicklisteners, not to the fragment itself.
i have create custom list view using base adapter to dynamic row
content.row content are created programmatically (check box,text view) they are include in layout.
problem to scrolling time they are very slow because not use
view holder. how can i use view holder this type of custom list view?
any solution or suggestion?
following this list..
ViewHolder is use in a list view when same view is repeated. Say there are total 6 items visible at a time in your activity. Then using viewholder pattern 6+2=8 views will be inflated at a time. one extra at the top and one extra at the bottom to give smooth scrolling effect. Now suppose scroll up operation is performed, and item at 8th position is visible, item at 0th position will be recycled and appended at the end of the list as the 9th item. if the views are not same this recycling can not be performed. check https://www.youtube.com/watch?v=wDBM6wVEO70
For your problem you can assume there is 5 maximum value possible then you can create adapter view using 10 dynamic views inside and set visibility as required.
Another optionis use LinearLayout and add each row dynamically but this won't give much optimization.
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 have a ListView in my android activity. And I populate the ListView by sub-class the BaseAdaptor (which returns a View in getView() method).
What if in my click listener of a button in a list item view, I
change the text of the TextView in the List item view
or
change the dimension of the list item view by adding/removing children of the list item view
What is an efficient to refresh my listView? I don't want the listView to re-trigger a query since there is no data change.
Thank you.
if you just change the content of your Children View, you have not more to do than TextView.setText(newText).
If you change the count of your ListView childrens, you have to call BaseAdaptor.notifyDataSetChanged()
I was about to suggest that you invalidate(), but you don't want to trigger a redraw, so I can only suggest that some form of global flag is your best bet. Set the flag, invalidate or redraw and query (and reset) the flag in OnDraw()