I am writing a custom adapter for ListView which extends BaseAdapter and in this method
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View vi = convertView;
if (vi == null)
vi = inflater.inflate(R.layout.x, null);
}
why is the check if(vi==null) performed. getView() is called for row when it comes in the visible area of the screen. So vi will be null everytime getview() is called? So why is the check necessary here.?
P.S. I was getting some problem on coloring some specific rows of the listview, but when i removed this check, everything works fine. Thats why i am wondering over its usefullness.
The convertView parameter may be a recycled view (for example, after scrolling down, the top rows become invisible, so their View objects are not destroyed, but recycled and passed as parameters for reuse).
However, the very first time a draw request comes, there is no view (e.g. the first time the screen with the list is loaded). Hence, in this case convertView has no value because nothing has been recycled (it is null), in which case you must create the rows using the inflater.
convertView : is for recycling. Let's say you have a listview which can only display 10 item at a time, and currently it is displaying item 1 -> item 10. When you scroll down one item, the item 1 will be out of screen, and item 11 will be displayed. To generate View for item 11, the getView() method will be call, and convertView here is the view of item 1 (which is not neccessary anymore). So instead create a new View object for item 11 (which is costly), why not re-use convertView? => we just check convertView is null or not, if null create new view, else re-use convertView.
Related
I have a listview with custom adapter. It's pretty simple, each item has a checkbox and textbox. However the issue is, when you check a checkbox and scroll down, some list items are automatically checked outside of screen too.
For example if you check first 2 items and scroll down; first 2 items out of screen comes checked too. How to fix this?
Here is my adapters getview method:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (inflater == null)
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
convertView = inflater.inflate(R.layout.share_item, parent, false);
}
CheckBox checkBox = (CheckBox) convertView.findViewById(R.id.checkboxShare);
TextView tvShareName = (TextView)convertView.findViewById(R.id.tvShareName);
tvShareName.setText(shareList.get(position).getName());
return convertView;
}
Read the definition of convertView parameter. When this method is called and convertView is not null - your code will change the value of the TextView, but will keep the value of CheckBox intact. The net effect is that once CheckBox gets checked, it will remain so and whenever this View gets recycled you'll have it checked.
The simple solution would be to set the value of CheckBox inside this method.
Offtopic: you might want to read about View Holder (anti) pattern - I don't like it very much, but performing findViewById calls on each invocation of getView is even worse.
The Problem
My way to add the view makes every fifth item to add the view when i only want one position to have this "Mängd" row.
Why Can i only edit listitems when they are visible on the screen.
The child will be static at 5 items even though i got like 20 item....
Is there any way to only say that item 1 will have this and not
position - firstvisibleposition
i think this is the problem with the listview
My code is not understandable at the time because of other things so i hope you get my problem anyways.
This is my main question
It seems like the thing i add to position 0 also happens to 6 and 12 Why is ListView this wierd ?
It's on swedish, but this is what i got with list view.
Every listview item has a empty Linearlayout that i add a view to when i press the down arrow button. the problem is that every fifth item gets this or i can only click on the first 5.
I dont get why they make ListView so complicated. i want to be able to get a child that is in the list without seening it!
CODE getView
public View getView (int position, View convertView, ViewGroup parent){
if (convertView == null)
{
convertView = LayoutInflater.from(getContext()).inflate(R.layout.view_meal_item_editable, null);
}
convertView.setOnTouchListener(new ItemSwipeListener(position,
getResources().getDisplayMetrics().density));
convertView.setClickable(true);
// Lookup view for data population
TextView food_item_name = (TextView) convertView.findViewById(R.id.food_item_name);
food_item_name.setHint("hello");
}
Where i add the view
View view = searchResultList.getAdapter().getView(position, searchResultList.getChildAt(position - searchResultList.getFirstVisiblePosition()), searchResultList);
LinearLayout extendedView = (LinearLayout)view.findViewById(R.id.extended_food_information);
View convertExtendedView = LayoutInflater.from(this).inflate(R.layout.change_amount_on_food_view, null);
extendedView.addView(convertExtendedView);
It's recommended to use a header view if you do this stuff only for the first element.
Otherwise it will be better if you add your extra view in getView() method, something like:
if(position==0){
// add extra view
} else {
// remove extra view if exist
}
Or you can remove the IF condition: if (convertView == null), so you will inflate a new layout each time, it will solve your problem but this is not good for list performance
I have listview and custom adapter that uses my own objects to draw the listitem.
From the other head I have a service that is gathering information realtime and every 0.1s my activity calls for the service information and than redraw listview by calling myCustomAdapter.notifyDataSetChanged() method.
This is bad for me because the objects are really large and my UI thread freezes for some time probably less than 0.01s but still feels bad user experience.
The resource I'm updating is one circle that is drawn by custom drawing class in canvas. Does any of you know how to handle this problem ? Is there some way to update data and redraw listview without stopping my UI thread?
You should try to reuse your convertView, like this:
public View getView (int position, View convertView, ViewGroup parent){
//inflate the view if it is null
if( convertView == null ){
convertView = inflater.inflate(R.layout.my_list_item, parent, false);
}
//make the changes on your convertView that are changed from row to row,
//such as a text in a TextView
return convertView;
}
I have a custom adapter extended from the SimpleCursorAdapter. Using this I'm binding a ListView which contains a checkbox and Two textboxes. On opening the activity, the list appears fine. But on clicking the checkboxes and entering some text in the textboxes and scrolling down, and then up again, the data disappears.
In fact any change disappears, even if they were already checked. I uncheck them, then scroll down and up, the go back to checked. So basically, they go back whatever state they were when retrieved.
Any ideas why? Thanks.
You need to have an arraylist of the states of each item in the list,, then load these states each time the list item view is loaded.Do this by overriding GetView() method in the adapter and add your saved state to the list based on the item position
Listview tends to recreate its views every time your list is scrolled up or down. You need to have some kind of model class that can save the state of your checkbox and textbox in memory in case some change is done(for that particular row) and later display it on the view.
As mentioned on other answers in this post u can use getview to programatically induce values that you have stored in your model classes to your views based on the list view position.
Something like this
#Override
public View getView(int position, View convertView, ViewGroup parent) {
System.out.println("getView " + position + " " + convertView);
ViewHolder holder = null;
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.item1, null);
holder = new ViewHolder();
holder.textView = (TextView)convertView.findViewById(R.id.text);
convertView.setTag(holder);
}
else {
holder = (ViewHolder)convertView.getTag();
}
// Pass on the value to your text view like this. You can do it similarly for a check box as well
holder.textView.setText(mData.get(position));
return convertView;
}
Android does not render all ListView entries at once, but only those visible on the screen. When "new" List-Rows come into view the
public View getView(int position, View convertView, ViewGroup parent)
method of your Adapter gets called and the view is recreated.
To fill in previousely saved values you probalby have to overwrite the getView method.
I have a ListView in a custom ArrayAdapter that displays an icon ImageView and a TextView in each row. When I make the list long enough to let you scroll through it, the order starts out right, but when I start to scroll down, some of the earlier entries start re-appearing. If I scroll back up, the old order changes. Doing this repeatedly eventually causes the entire list order to be seemingly random. So scrolling the list is either causing the child order to change, or the drawing is not refreshing correctly.
What could cause something like this to happen? I need the order the items are displayed to the user to be the same order they are added to the ArrayList, or at LEAST to remain in one static order. If I need to provide more detailed information, please let me know. Any help is appreciated. Thanks.
I was having similar issues, but when clicking an item in the custom list, the items on the screen would reverse in sequence. If I clicked again, they'd reverse back to where they were originally.
After reading this, I checked my code where I overload the getView method. I was getting the view from the convertedView, and if it was null, that's when I'd build my stuff. However, after placing a breakpoint, I found that it was calling this method on every click and on subsequent clicks, the convertedView was not null therefore the items weren't being set.
Here is an example of what it was:
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
{
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.listitemrow, null);
RssItem rssItem = (RssItem) super.getItem(position);
if (rssItem != null)
{
TextView title = (TextView) view.findViewById(R.id.rowtitle);
if (title != null)
{
title.setText(rssItem.getTitle());
}
}
}
return view;
}
The subtle change is moving the close brace for the null check on the view to just after inflating:
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if (view == null)
{
LayoutInflater vi = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.listitemrow, null);
}
RssItem rssItem = (RssItem) super.getItem(position);
if (rssItem != null)
{
TextView title = (TextView) view.findViewById(R.id.rowtitle);
if (title != null)
{
title.setText(rssItem.getTitle());
}
}
return view;
}
I hope this helps others who experience this same problem.
To further clarify the answer of farcats below in more general way, here is my explanation:
The vi.inflate operation (needed here for parsing of the layout of a row from XML and creating the appropriate View object) is wrapped by an if (view == null) statement for efficiency, so the inflation of the same object will not happen again and again every time it pops into view.
HOWEVER, the other parts of the getView method are used to set other parameters and therefore should NOT be included within the if (view == null) statement.
Similarily, in other common implementation of this method, some textView, ImageView or ImageButton elements need to be populated by values from the list[position], using findViewById and after that .setText or .setImageBitmap operations.
These operations must come after both creating a view from scratch by inflation and getting an existing view if not null.
Another good example where this solution is applied for BaseAdapter appears in BaseAdapter causing ListView to go out of order when scrolled
The ListView reuses view objects when you scroll. Are you overriding the getView method? You need to make sure you set each property for every view, don't assume that it will remember what you had before. If you post that method, someone can probably point you at the part that is incorrect.
I have a ListView, AdapterView and a View (search_options) that contains EditText and 3 Spinners. ListView items are multiple copies of (search_options) layout, where user can add more options in ListView then click search to send sql query built according to users options.
I found that convertView mixing indecies so I added a global list (myViews) in activity and passed it to ArrayAdapter. Then in ArrayAdapter (getView) I add every newly added view to it (myViews).
Also on getView instead of checking if convertView is null, I check if the global list (myViews) has a view on the selected (position).. It totally solved problems after losing 3 days reading the internet!!
1- on Activity add this:
Map<Integer, View> myViews = new HashMap<>();
and then pass it to ArrayAdapter using adapter constructor.
mSOAdapter = new SearchOptionsAdapter(getActivity(), resultStrs, myViews);
2- on getView:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;
if (!myViews.containsKey(position)) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
view = inflater.inflate(R.layout.search_options, parent, false);
/// ...... YOUR CODE
myViews.put(position, view);
FontUtils.setCustomFontsIn(view, getContext().getAssets());
}else {
view = myViews.get(position);
}
return view;
}
Finally no more mixing items...