I have a listview with very large number of elements and i have set the middle element in the middle of the screen (mRootLayoutHeight is height of the screen)
listview.setSelectionFromTop(adapter.getCount()/2,mRootLayoutHeight/2);
But when i try to access all the visible elements of listview on screen using
for(int i=listview.getFirstVisiblePosition();i<=listview.getLastVisiblePosition();i++)
View v=listview.getChildAt(i);
View v is always NULL. Why is that?
getFirstVisiblePosition, as per the documentation returns:
Returns the position within the adapter's data set for the first item displayed on screen.
So, if you have 100 items in your data set, and you scroll down the list, you might be looking at items 15-23. So you'd be iterating over i=15 to i=23.
The getChildAt method is from the base ViewGroup class however, and returns items at that index on screen - so if you have 8 items on screen (as per the example above), you'll only be able to get items from index 0 - 8, even though those items exist at a different position within the dataset.
So in the example I've supplied here, if you attempted to get the view at index 15, you'd get null, since there are only 8 views in the ListView view group.
Related
I have a recylcerview where the method shown below gets called on each selection of an item. The method only runs sucessfully when I do not scroll the recyclerview, so the firstvisible is 0. The moment I scroll down a bit and select something the first and last visible are set correctly in the method, but as can be seen in the screenshot, for reasons I do not understand the childAt returns null instead of the view I can see on my app screen. In the screenshot for position 7, the first 4-6 returned a child.
Can someone explain to me how this can happen? From my pov, getChildAt() should always return a view in this scenario.
first and last are going to be the adapter positions of the data and not the position as laid out in the layout manager. See LinearLayoutManager#findFirstVisibleItemPosition. The children will always start with zero and increase from there.
That is why it works before your scroll since the child at the zeroth index in the layout manager is also the zeroth item in the adapter.
Here is a discussion about the various positions in RecyclerView.
It looks like you want to make changes to all visible items. Your first and last variables will have the correct start/end adapter positions that correspond to what is visible on the screen. You need the adapter positions to call the various "notify" methods.
So, given the adapter positions, we need a map to the views that are represented on the screen. As an example, the following code loops through every visible view and changes the background color of each view.
LinearLayoutManager lm = (LinearLayoutManager) Recycler.getLayoutManager();
// Get adapter positions for first and last visible items on screen.
int firstVisible = lm.findFirstVisibleItemPosition();
int lastVisible = lm.findLastVisibleItemPosition();
for (int i = firstVisible; i <= lastVisible; i++) {
// Find the view that corresponds to this position in the adapter.
View visibleView = lm.findViewByPosition(i);
visibleView.setBackgroundColor(getResources().getColor(android.R.color.holo_red_light));
}
If you use the child methods of the layout manager, you will need to loop from zero to LayoutManager.getChildCount() - 1 to make the changes. You will see each attached view which, I believe, can exceed the number of visible views.
I would like to be able to determine the height of a listview prior to adding items. The listview is defined as layout_height=0dp and layout_weight=5 (or some other value, its not fill_parent or wrap_content). When I call lv.getHeight prior to adding items, the height returns 0. I've tried using lv.requestLayout() and lv.getMeasuredHeight in the onResume method of my fragment.
Things I've tried so far:
Querying the listview height in an onGlobalLayout handler. This gets me the correct height, but the listadapter's getView has already been called to populate the list. If I wait to bind the adapter until after onGlobalLayout, then when I use adapter.notifyDataSetChanged() to update the view, I'm stuck in a loop of onGlobalLayout handler calls.
From the docs, onResume appears to be called after the layout is complete, so I would expect that the listview.getHeight or listview.getMeasuredHeight would return valid values at this time, but all I get is 0.
Why I want this. I need to know how many items will fit in the listview prior to adding them. I want to add a number (count-1) of "blank items" at the top of the list so that it is possible for the user to scroll the top item in the list to the bottom so it is visually next to another control below the list.
Finally found this.
View's getWidth() and getHeight() returns 0
But it appears to be counter to the documentation of onResume
I have a listview in android with several columns, and for each column I want to set a separate title, as shown in this example below (I mean the fields 'Name and Number', 'SMS, 'Phone'):
(The image is just to explain what I want; in my layout there are more columns which are not as self-explaining as in the example).
Is there a good way to do that? Als, is it possible that this title row can be visible at all time, even when I have many many rows and scroll down?
If you want to have a fixed header item in the list that doesn't scroll with the other items, use ListView's addHeaderView to indicate which view(s) should stick to the top.
If you just want the first item to be different than the others, you'll have to code your ListAdapter to have two types of views, one for the header, and another for the rest. This involves
Making getViewTypeCount return 2 to say it can make two different kinds of views.
Making getItemViewType return 0 or 1 to say which type of view will be generated for the given position in the list.
Making getView return the correct view object for the given position in the list.
Presumably you'd want position 0 to be the header view and all other positions to be data.
Here's the scenario: I've got a simple ListView displaying a twitter feed. I have a CursorAdapter which is fetching tweets from sqlite. When I call requery() on my cursor, I expect that new tweets will be fetched that have since been added to the database. This all works fine, and the new items are even visible in the ListView after this happens.
The Problem is the scroll position seems to be saved based on the item position offset. So let's say the first visible position in my ListView is 4. When I requery and 2 new items are added to the top of the list, the ListView keeps the first visible item scroll position as 4, however since there's two new items in the list, I now see a different item at position 4 than before I refreshed. This image illustrates the before and after:
Notice how Before, Tweet D is the first visible item and afterwards, Tweet B now becomes the first visible item.
My Question is, how do I keep the same scroll position based on cursor positon when calling requery, so that in this example, Tweet D would still be the first visible item after a requery?
I had a similar issue in one of my projects. Here's how I solved it. Although it's not exactly the same case. Hope it helps.
Before you update the list, save the scroll position this way -
int position = getListView().getFirstVisiblePosition();
View v = getListView().getChildAt(0);
int top = v == null ? 0 : v.getTop();
And then after you update the list, you will know how many items you've added. So you can call
yourlistview.setSelectionFromTop(position + <number of items added>, top);
My suggestion would be to keep track of the number of items in the list. Once you know that, as part of the update you can add the difference to between the old count and the new count.
Psuedo Code
on_update
offset+= (list_view.length() - list_size)
list_size = list_view.length()
I am using a customadapter and overriding the getView() method. In this method I call view.getHeight() which returns 0. I would like to find out the height (in pixel) of each item in the listview. And find out where it is on the screen. So If I have 10 items in the list view is it possible to find out where the 5th item position on the screen(in pixel) I would like to find out the y of the item. top left corner.
CustomArrayAdapter adapter = new CustomArrayAdapter(this,R.layout.rowlayout,mydizi);
View header = (View)getLayoutInflater().inflate(R.layout.listview_header_row, null);
lv.addHeaderView(header);
lv.setAdapter(adapter);
View viewList = lv.getChildAt(5 - lv.getFirstVisiblePosition());
viewList.getLocationOnScreen(location);
You might need to use something like the ViewTreeObserver. Please see this question's response. You can place a block similar to this in your onCreate method. If a view is returning a value of 0 it is most likely that your are calling the view method too early in the activity lifecycle (ie before the screen objects are mapped out and able to be measured).
For a given VISIBLE row nPosition in list mList, you can get the location of the view via:
// find the position of the source and destination accounts ListView rows
int[] location = {0,0};
View viewList = mList.getChildAt(nPosition - mList.getFirstVisiblePosition());
viewList.getLocationOnScreen(location);
but you'll likely need to do this from outside your getView(), i.e. after the list is populated.