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.
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 have a ListView which I populate using a custom CursorAdapter.
Now I want to manually update just one specific item in the ListView. I have the content URI of that item. Is it possible to use just this info to get the position of the item in the listView?
If I have the position I can do something like
View v = mListView.getChildAt(itemPosition - mListView.getFirstVisiblePosition());
to update the view. But how can I get itemPosition?
I know that I get the position in the onItemClickListener, but I need to update the view without it being clicked.
Any help guys?
ListView does not support updating a single position.
You must update the adapter with the new data (even if you change a single position) and the, invoke mAdapter.notifyDataSetChanged()
If you get the View by its position (via listView.getChildAt()) will work. However, if you scroll up and down the list, that view will display the old data again because the adapter is not aware of the change (and it is the adapter which update the view contect view getView()).
When you invoke notifyDataSetChanged(), you are telling to the adapter that your data set has new info and the ListView/Adapter will re-draw the visible items (it won't re-draw whole list at once.. only the visible items).
You may want to consider to change to RecyclerView in the future. The BaseAdapter used in a RecyclerView support actions such as add/remove/update a single position.
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.
I tried the following but had no luck since the getChildAt always returns null(I am sure the listView is filled).
listView.performItemClick(listView.getChildAt(0), 0, listView.getChildAt(0).getId());
Any suggestions? Thanks!
Edited: code for adding item into list
lvAdapter = new ActionStreamAdapter<HasDescription>(getBaseContext());
while ((item = actionStreamQueue.poll()) != null) {
...
lvAdapter.addItem(item);
}
ListView.setAdapter(lvAdapter);
Where ActionStreamAdapter is a class extends BaseAdapter. The queue is filled by calling the Dao of a simple data sturcure class.
There is a difference between childs and items. A Child is a view that is lower in the view hierarchy. For example, when there is a LinearLayout containing two TextViews, then the TextViews are children of the LinearLayout. A ListView has items. These are on the same hierarchical level as your ListView so they aren't children. They are items of the ListView. You should use ListView.getItemAtPosition() instead.
Then, there is a difference between selecting and clicking. Clicking means performing an action on an item and selecting is highlighting it. For selecting the first item, you should do something like: ListView.getItemAtPosition(n).requestFocus()
It is an interesting API where it takes a pointer of the viewitem, typically listview items are recycled so the item at position 0, need not be 0th item on the adapter. You could use findViewWithTagTraversal() to find the view and do the selection on it.
If you don't mind what do you want to do with selection, or do you want to give focus to the first item?
You could use setSelection()
It seems like ListView doesn't expose its y-position in the same way a ScrollView does. In other words: I need to remember the exact position the ListView was scrolled to and set it when I return to the activity.
Just to clarify: I don't need the selected item... that's pretty straight forward. I need to restore the exact pixel-wise y position on the ListView.
Thanks!
I've used this successfully when saving (in onSaveInstanceState) and restoring (onCreate), when switching orientations:
Saving:
int savedPosition = list.getFirstVisiblePosition();
View firstVisibleView = list.getChildAt(0);
int savedListTop = (firstVisibleView == null) ? 0 : firstVisibleView.getTop();
Restoring:
if (savedPosition >= 0) { //initialized to -1
list.setSelectionFromTop(savedPosition, savedListTop);
}
This will precisely save the Y position. Well, it misses by a few pixels every once in a while.
When you are returning from another Activity, the ListView will remain scrolled to its original position that it was at when you left that ListView Activity. If you are updating the contents of the list make sure you just use notifyDataSetChanged() on the adapter, do not re-assign the adapter - this will reset the list to the top.
Check your onResume method in your ListView Activity, it might be re-assigning a list adapter.
If you need to remember an arbitrary scroll position that doesn't rely on the Activity stack, I am willing to bet that isn't possible besides just saving the current selected or first visible item. A ListView does not have a defined height. It is relative to both the number of items and content of those items.
Parcelable state = list.onSaveInstanceState();
// do stuff
list.onRestoreInstanceState(state);
Is the only correct way I know of to maintain exact position of a list. The above solution that's marked as correct bumps up/down a few pixels so not really the most professional solution.