Scope
I need to scroll to certain position smoothly and then "jump" to another position with setSelection(anotherPosition). This is done to create an illusion of smooth scrolling of (e.g.) 100 items in ListView. smoothScrollToPosition(100) lasts too much, you know.
Problem
setSelection() doesn't wait till smoothScrollToPosition finishes its work, so setSelection() is being called immediately and user sees quick jumping only;
Code
private final int scrollableItems = 20;
int firstVisiblePosition = mListView.getFirstVisiblePosition();
if (firstVisiblePosition < scrollableItems) {
mListView.smoothScrollToPosition(0);
} else {
mListView.smoothScrollToPosition(firstVisiblePosition - scrollableItems);
mListView.setSelection(0);
}
mListView.clearFocus();
Idea
OK, we could change logic of smoothness illusion: first setSelection(), then scroll smoothly (we're scrolling to the very first item on top of the list):
int firstVisiblePosition = mListView.getFirstVisiblePosition();
if (firstVisiblePosition < scrollableItems) {
mListView.smoothScrollToPosition(0);
} else {
mListView.setSelection(scrollableItems);
mListView.smoothScrollToPosition(0);
}
mListView.clearFocus();
final ListView listView = ...;
View listItemView = ...;
listView.smoothScrollBy(listItemView.getHeight() * NUMBER_OF_VIEWS,
DURATION * 2);
listView.postDelayed(new Runnable() {
public void run() {
listView.smoothScrollBy(0, 0); // Stops the listview from overshooting.
listView.setSelection(0);
}
}, DURATION);
Of course, direction of the scroll etc. would need to be adjusted for your use case (go to the top of the list)
EDIT: Old solution could overshoot if the velocity of the scroll was too high, smoothScrollBy(0,0) will stop the smooth scrolling before setting the selection properly and immediately.
Another way is to add an OnScrollListener.
private final int scrollableItems = 20;
int firstVisiblePosition = mListView.getFirstVisiblePosition();
if (firstVisiblePosition < scrollableItems) {
mListView.smoothScrollToPosition(0);
} else {
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView absListView, int i) {
if (i == SCROLL_STATE_IDLE) {
mListView.setSelection(0);
}
}
})
mListView.smoothScrollToPosition(firstVisiblePosition - scrollableItems);
}
mListView.clearFocus();
Related
I have a ListView with a set of children vertically listed (View objects) to be viewed by the users. I have to track the the user views, say,
a. If a user views a set of items for around 1 second, I should track the impressions.
b. If the same user scrolls the items out of the viewport and return back, I should track again, if he viewed for 1 second.
I tried several options like getGlobalVisibleRect(), getLocalVisibleRect(), getLocationOnScreen() and they are confusing in the first place and didn't help me get the right coordinates and visibility of the child items of the listView.
I checked Track impression of items in an android ListView which is a bit similar to my requirement but I thought to check if there is a better solution. I am new to Android and apologies if I am not clear on some explanations
To get your desired result, I think we have two different solutions. First, create Handler for each of the item and call / remove in scroll view if it is visible. But this is very much stupid one as creating so many Handlers will make your app's life hell.
Second and best way is to use call / remove a single Handler for the entire visible items. If it persist for a time "A second" (1 second for you), use impression count in each of your item's model class and increase it with ++ operator.
You can add scroll listener in your listivew. The script will be like-
ListView listView = null;
int firstVisibleItemIndex = 0;
int visibleCount = 0;
Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
for (int i = firstVisibleItemIndex; i < firstVisibleItemIndex + visibleCount; i++) {
try {
//Get impression count from model for the visible item index i
int count = modelList.get(i).getImpressionCount();
//Set impression count to the model for the visible item index i
modelList.get(i).setImpressionCount(++count);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
//Can call this method body in onCreate directly
private void addListScrollListener() {
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// You cat determine first and last visible items here
// final int lastVisibleItem = firstVisibleItem + visibleItemCount - 1;
handler.removeCallbacks(runnable);
firstVisibleItemIndex = firstVisibleItem;
visibleCount = visibleItemCount;
handler.postDelayed(runnable, 1000);
}
#Override
public void onScrollStateChanged(AbsListView arg0, int arg1) {
// TODO Auto-generated method stub
}
});
}
I assume that you will bind your ListView with the id in your onCreate method. Also you can call the listener thing in your onCreate after binding the view with the variable.
I hope this will work for your requirement.
Let me know your feedback.
I'm working on a Circular ViewPager, and i've implemented this exactly solution (https://stackoverflow.com/a/12965787/1083564).
The only thing is missing, is the fact that i need to smoothScroll when i'm using the setCurrentItem(int i, bol b) method, that instantly goes to the pixel limit, without using the smoothScroll.
I already have the access to use this method, using the following code:
package android.support.v4.view;
import android.content.Context;
import android.util.AttributeSet;
public class MyViewPager extends ViewPager {
public MyViewPager(Context context) {
super(context);
}
public MyViewPager(Context context, AttributeSet attr) {
super(context, attr);
}
public void smoothScrollTo(int x, int y, int velocity) {
super.smoothScrollTo(x, y, velocity);
}
}
But i couldn't figure it out where and how to use it. I have the number of pixels that i need to run smoothly by using this code inside the setOnPageChangeListener on my ViewPager:
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.d( "viewpager", positionOffsetPixels+"");
}
Before it goes to 0, instantly, because of the setCurrentItem, i have the value of pixels left to reach 0 (to the left) or x (to the right, depending of screen). I dont know how can i get this x number too.
PS: I think this solution is the exatcly one used by IMDB app. You can see this scrolling from the first to the last but one, without remove your finger (use 2 fingers to do it). You will see that the "white limit" will show from the left side of the ViewPager. The only difference is that they know how to smooth scroll after using the setCurrentItem.
If you need some more information, please, ask! Thanks!
Issue: When you detect circular scrolling has to be perfomed, calling setCurrentItem immediately will cause the ViewPager to scroll to the real fragment immediately without smooth scrolling as it is set to false.
Solution: Instead allow the ViewPager to scroll to the fake fragment smoothly as it does for other fragments and then scroll to the real fragment after some delay with smooth scrolling set to false. User will not notice the change.
When we are performing circular scrolling, call setCurrentItem in a runnable with some delay. Use onPageSelected to know the index of the page selected.
public void onPageSelected(int position) {
// Consider eg. : C' A B C A'
boolean circularScroll = false;
if(position == 0) {
// Position 0 is C', we need to scroll to real C which is at index 3.
position = mPager.getAdapter().getCount() - 2;
circularScroll = true;
}
int lastIndex = mPager.getAdapter().getCount() - 1;
if(position == lastIndex) {
// Last index is A', we need to scroll to real A, which is at index 1.
position = 1;
circularScroll = true;
}
if(circularScroll) {
final int realPosition = position;
mPager.postDelayed(new Runnable() {
#Override
public void run() {
mPager.setCurrentItem(realPosition, false);
}
}, 500L);
}
}
When you set the second parameter of the setCurrentItem to true it should smooth scroll
#Override
public void onPageScrollStateChanged (int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
int curr = viewPager.getCurrentItem();
int lastReal = viewPager.getAdapter().getCount() - 2;
if (curr == 0) {
viewPager.setCurrentItem(lastReal, true);
} else if (curr > lastReal) {
viewPager.setCurrentItem(1, true);
}
}
}
Sorry for the confusing title, I cannot express the problem very concisely...
I have an Android app with a ListView that uses a circular / "infinite" adapter, which basically means I can scroll it up or down as much as I want and the items will wrap around when it reaches the top or bottom, making it seem to the user as if he is spinning an infinitely long list of (~100) repeating items.
The point of this setup is to let the user select a random item, simply by spinning / flinging the listview and waiting to see where it stops. I decreased the friction of the Listview so it flings a bit faster and longer and this seems to work really nice. Finally I placed a partially transparent image on top of the ListView to block out the top and bottom items (with a transition from transparent to black), making it seem as if the user is "selecting" the item in the middle, as if they were on a rotating "wheel" that they control by flinging.
There is one obvious problem: after flinging the ListView does not stop at a particular item, but it can stop hovering between two items (where the first visible item is then only partially shown). I want to avoid this because in that case it is not obvious which item has been "randomly selected".
Long story short: after the ListView has finished scrolling after flinging, I want it to stop on a "whole" row, instead of on a partially visible row.
Right now I implemented this behavior by checking when the scrolling has stopped, and then selecting the first visible item, as such:
lv = this.getListView();
lv.setFriction(0.005f);
lv.setOnScrollListener(new OnScrollListener() {
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE)
{
if (isAutoScrolling) return;
isAutoScrolling = true;
int pos = lv.getFirstVisiblePosition();
lv.setSelection(pos);
isAutoScrolling = false;
}
}
});
This works reasonably well, apart from one glaringly obvious problem... The first visible item might only be visible for a pixel or two. In that case, I want the ListView to jump "up" for those two pixels so that the second visible item is selected. Instead, of course, the first visible item is selected which means the ListView jumps "down" almost an entire row (minus those two pixels).
In short, instead of jumping to the first visible item, I want it to jump to the item that is visible the most. If the first visible item is less than half visible, I want it to jump to the second visible item.
Here's an illustration that hopefully conveys my point. The left most ListView (of each pair) shows the state after flinging has stopped (where it comes to a halt), and the right ListView shows how it looks after it made the "jump" by selecting the first visible item. On the left I show the current (wrong) situation: Item B is only barely visible, but it is still the first visible item so the listView jumps to select that item - which is not logical because it has to scroll almost an entire item height to get there. It would be much more logical to scroll to Item C (which is depicted on the right) because that is "closer".
(source: nickthissen.nl)
How can I achieve this behavior? The only way I can think of is to somehow measure how much of the first visible item is visible. If that is more than 50%, then I jump to that position. If it is less than 50%, I jump to that position + 1. However I have no clue how to measure that...
Any idea's?
You can get the visible dimensions of a child using the getChildVisibleRect method. When you have that, and you get the total height of the child, you can scroll to the appropriate child.
In the example below I check whether at least half of the child is visible:
View child = lv.getChildAt (0); // first visible child
Rect r = new Rect (0, 0, child.getWidth(), child.getHeight()); // set this initially, as required by the docs
double height = child.getHeight () * 1.0;
lv.getChildVisibleRect (child, r, null);
if (Math.abs (r.height ()) < height / 2.0) {
// show next child
}
else {
// show this child
}
Here's my final code inspired by Shade's answer.
I forgot to add "if(Math.abs(r.height())!=height)" at first. Then it just scrolls twice after it scroll to correct position because it's always greater than height/2 of childView.
Hope it helps.
listView.setOnScrollListener(new AbsListView.OnScrollListener(){
#Override
public void onScrollStateChanged(AbsListView view,int scrollState) {
if (scrollState == SCROLL_STATE_IDLE){
View child = listView.getChildAt (0); // first visible child
Rect r = new Rect (0, 0, child.getWidth(), child.getHeight()); // set this initially, as required by the docs
double height = child.getHeight () * 1.0;
listView.getChildVisibleRect (child, r, null);
if(Math.abs(r.height())!=height){//only smooth scroll when not scroll to correct position
if (Math.abs (r.height ()) < height / 2.0) {
listView.smoothScrollToPosition(listView.getLastVisiblePosition());
}
else if(Math.abs (r.height ()) > height / 2.0){
listView.smoothScrollToPosition(listView.getFirstVisiblePosition());
}
else{
listView.smoothScrollToPosition(listView.getFirstVisiblePosition());
}
}
}
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
}});
Follow these 3 steps, then you can get exactly what you want!!!!
1.Initialize the two variable for scrolling up and down:
int scrollingUp=0,scrollingDown=0;
2.Then increment the value of the variable based on scrolling:
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if(mLastFirstVisibleItem<firstVisibleItem)
{
scrollingDown=1;
}
if(mLastFirstVisibleItem>firstVisibleItem)
{
scrollingUp=1;
}
mLastFirstVisibleItem=firstVisibleItem;
}
3.Then do the changes in the onScrollStateChanged():
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case SCROLL_STATE_IDLE:
if(scrollingUp==1)
{
mainListView.post(new Runnable() {
public void run() {
View child = mainListView.getChildAt (0); // first visible child
Rect r = new Rect (0, 0, child.getWidth(), child.getHeight()); // set this initially, as required by the docs
double height = child.getHeight () * 1.0;
mainListView.getChildVisibleRect (child, r, null);
int dpDistance=Math.abs (r.height());
double minusDistance=dpDistance-height;
if (Math.abs (r.height()) < height/2)
{
mainListView.smoothScrollBy(dpDistance, 1500);
}
else
{
mainListView.smoothScrollBy((int)minusDistance, 1500);
}
scrollingUp=0;
}
});
}
if(scrollingDown==1)
{
mainListView.post(new Runnable() {
public void run() {
View child = mainListView.getChildAt (0); // first visible child
Rect r = new Rect (0, 0, child.getWidth(), child.getHeight()); // set this initially, as required by the docs
double height = child.getHeight () * 1.0;
mainListView.getChildVisibleRect (child, r, null);
int dpDistance=Math.abs (r.height());
double minusDistance=dpDistance-height;
if (Math.abs (r.height()) < height/2)
{
mainListView.smoothScrollBy(dpDistance, 1500);
}
else
{
mainListView.smoothScrollBy((int)minusDistance, 1500);
}
scrollingDown=0;
}
});
}
break;
case SCROLL_STATE_TOUCH_SCROLL:
break;
}
}
You probably solved this problem but I think that this solution should work
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
View firstChild = lv.getChildAt(0);
int pos = lv.getFirstVisiblePosition();
//if first visible item is higher than the half of its height
if (-firstChild.getTop() > firstChild.getHeight()/2) {
pos++;
}
lv.setSelection(pos);
}
getTop() for first item view always return nonpositive value so I don't use Math.abs(firstChild.getTop()) but just -firstChild.getTop(). Even if this value will be >0 then this condition is still working.
If you want to make this smoother then you can try to use lv.smoothScrollToPosition(pos) and enclose all above piece of code in
if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {
post(new Runnable() {
#Override
public void run() {
//put above code here
//and change lv.setSelection(pos) to lv.smoothScrollToPosition(pos)
}
});
}
Once you know the first visible position, you should be able to use View.getLocationinWindow() or View.getLocationOnScreen() on the next position's view to get the visible height of the first. Compare that to the View's height, and scroll to the next position if appropriate.
You may need to tweak it to account for padding, depending on what your rows look like.
I haven't tried the above, but it seems like it should work. If it doesn't, here's another, probably less robust idea:
getLastVisiblePosition(). If you take the difference between last and first, you can see how many positions are visible on the screen. Compare that to how many positions were visible when the list was first populated(scroll position 0).
If the same number of positions are visible, simply scroll to the first visible position as you are doing. If there is one more visible, scroll to "first + 1" position.
If you can get the position of the row that needs to be scrolled to, you can use the method:
smoothScrollToPosition
So something like:
int pos = lv.getFirstVisiblePosition();
lv.smoothScrollToPosition(pos);
Edit
Try this, sorry I don't have time to test, I'm out and about.
ImageView iv = //Code to find the image view
Rect rect = new Rect(iv.getLeft(), iv.getTop(), iv.getRight(), iv.getBottom());
lv.requestChildRectangleOnScreen(lv, rect, false);
My Solution:
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
if (swipeLayout.isRefreshing()) {
swipeLayout.setRefreshing(false);
} else {
int pos = firstVisibleItem;
if (pos == 0 && lv_post_list.getAdapter().getCount()>0) {
int topOfNext = lv_post_list.getChildAt(pos + 1).getTop();
int heightOfFirst = lv_post_list.getChildAt(pos).getHeight();
if (topOfNext > heightOfFirst) {
swipeLayout.setEnabled(true);
} else {
swipeLayout.setEnabled(false);
}
}
else
swipeLayout.setEnabled(false);
}
}
I'm trying to smoothly scroll to last element of a list after adding an element to the arrayadapter associated with the listview.
The problem is that it just scrolls to a random position
arrayadapter.add(item);
//DOES NOT WORK CORRECTLY:
listview.smoothScrollToPosition(arrayadapter.getCount()-1);
//WORKS JUST FINE:
listview.setSelection(arrayadapter.getCount()-1);
You probably want to tell the ListView to post the scroll when the UI thread can handle it (which is why yours it not scrolling properly). SmoothScroll needs to do a lot of work, as opposed to just go to a position ignoring velocity/time/etc. (required for an "animation").
Therefore you should do something like:
getListView().post(new Runnable() {
#Override
public void run() {
getListView().smoothScrollToPosition(pos);
}
});
(Copied from my answer: smoothScrollToPositionFromTop() is not always working like it should)
This is a known bug. See https://code.google.com/p/android/issues/detail?id=36062
However, I implemented this workaround that deals with all edge cases that might occur:
First call smothScrollToPositionFromTop(position) and then, when scrolling has finished, call setSelection(position). The latter call corrects the incomplete scrolling by jumping directly to the desired position. Doing so the user still has the impression that it is being animation-scrolled to this position.
I implemented this workaround within two helper methods:
smoothScrollToPosition()
public static void smoothScrollToPosition(final AbsListView view, final int position) {
View child = getChildAtPosition(view, position);
// There's no need to scroll if child is already at top or view is already scrolled to its end
if ((child != null) && ((child.getTop() == 0) || ((child.getTop() > 0) && !view.canScrollVertically(1)))) {
return;
}
view.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
if (scrollState == SCROLL_STATE_IDLE) {
view.setOnScrollListener(null);
// Fix for scrolling bug
new Handler().post(new Runnable() {
#Override
public void run() {
view.setSelection(position);
}
});
}
}
#Override
public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
final int totalItemCount) { }
});
// Perform scrolling to position
new Handler().post(new Runnable() {
#Override
public void run() {
view.smoothScrollToPositionFromTop(position, 0);
}
});
}
getChildAtPosition()
public static View getChildAtPosition(final AdapterView view, final int position) {
final int index = position - view.getFirstVisiblePosition();
if ((index >= 0) && (index < view.getChildCount())) {
return view.getChildAt(index);
} else {
return null;
}
}
You should use setSelection() method.
Use LayoutManager to smooth scroll
layoutManager.smoothScrollToPosition(recyclerView, new RecyclerView.State(), position);
final Handler handler = new Handler();
//100ms wait to scroll to item after applying changes
handler.postDelayed(new Runnable() {
#Override
public void run() {
listView.smoothScrollToPosition(selectedPosition);
}}, 100);
The set selection method mentioned by Lars works, but the animation was too jumpy for our purposes as it skips whatever was left. Another solution is to recall the method repeatedly until the first visible position is your index. This is best done quickly and with a limit as it will fight the user scrolling the view otherwise.
private void DeterminedScrollTo(Android.Widget.ListView listView, int index, int attempts = 0) {
if (listView.FirstVisiblePosition != index && attempts < 10) {
attempts++;
listView.SmoothScrollToPositionFromTop (index, 1, 100);
listView.PostDelayed (() => {
DeterminedScrollTo (listView, index, attempts);
}, 100);
}
}
Solution is in C# via. Xamarin but should translate easily to Java.
Do you call arrayadapter.notifyDataSetChanged() after you called arrayadapter.add()? Also to be sure, smoothScrollToPosition and setSelection are methods available in ListView not arrayadapter as you have mentioned above.
In any case see if this helps:
smoothScrollToPosition after notifyDataSetChanged not working in android
Solution in kotlin:
fun AbsListView.scrollToIndex(index: Int, duration: Int = 150) {
smoothScrollToPositionFromTop(index, 0, duration)
postDelayed({
setSelection(index)
post { smoothScrollToPositionFromTop(index, 0, duration) }
}, duration.toLong())
}
PS: Looks like its quite messed up on Android SDK side so this is kind of best you can get, if you don't want to calculate your view offset manually. Maybe best easy way is to set duration to 0 for long list to avoid any visible jump.
I had some issues when calling just setSelection in some positions in GridView so this really seems to me as solution instead of using that.
use this in android java, it work for me:
private void DeterminedScrollTo(int index, int attempts) {
if (listView.getFirstVisiblePosition() != index && attempts < 10) {
attempts++;
if (listView.canScrollVertically(pos))
listView.smoothScrollToPositionFromTop(index, 1, 200);
int finalAttempts = attempts;
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
#Override
public void run() {
DeterminedScrollTo(index, finalAttempts);
}
}, 200);
}
}
This is my first ever post here and I'm a dumb novice, so I hope someone out there can both help me and excuse my ignorance.
I have a ListView which is populated with an ArrayAdapter. When I either scroll or click, I want the selected item, or the item nearest the vertical center, to be forced to the exact vertical center of the screen. If I call listView.setSelection(int position) it aligns the selected position at the top of the screen, so I need to use listView.setSelectionFromTop(position, offset) instead. To find my offset, I take half of the View's height from the half of the ListView's height.
So, I can vertically center my item easy enough, within OnItemClick or OnScrollStateChanged, with the following:
int x = listView.getHeight();
int y = listView.getChildAt(0).getHeight();
listView.setSelectionFromTop(myPosition, x/2 - y/2);
All this works fine. My problem is with the initial ListView setup. I want an item to be centered when the activity starts, but I can't because I get a NullPointerException from:
int y = listView.getChildAt(0).getHeight();
I understand this is because the ListView has not yet rendered, so it has no children, when I call this from OnCreate() or OnResume().
So my question is simply: how can I force my ListView to render at startup so I can get the height value I need? Or, alternatively, is there any other way to center items vertically within a ListView?
Thanks in advance for any help!
int y = listView.getChildAt(0).getHeight();
I understand this is because the ListView has not yet rendered, so it has no children, when I call this from onCreate() or onResume().
You should call it in onScroll.
listView.setOnScrollListener(new OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
//Write your logic here
int y = listView.getChildAt(0).getHeight();
}
});
I'm answering my own question here, but it's very much a hack. I think it's interesting because it sheds some light on the behavior of listviews.
The problem was in trying to act on data (a listview row) that did not yet exist (it had not been rendered). listview.getChildAt(int) was null because the listview had no children yet. I found out onScroll() is called immediately when the activity is created, so I simply put everything in a thread and delayed the getChildAt() call. I then enclosed the whole thing in a boolean wrapper to make sure it is only ever called once (on startup).
The interesting thing was that I only had to delay the call by 1ms for everything to be OK. And that's too fast for the eye to see.
Like I said, this is all a hack so I'm sure all this is a bad idea. Thanks for any help!
private boolean listViewReady = false;
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (!listViewReady){
Thread timer = new Thread() {
public void run() {
try{
sleep(1);
}catch (InterruptedException e) {
e.printStackTrace();
}finally{
runOnUiThread(new Runnable() {
public void run() {
myPosition = 2;
int x = listView.getHeight();
int y = listView.getChildAt(0).getHeight();
listView.setSelectionFromTop(myPosition, x/2 - y/2);
listViewReady = true;
}
});
}
}
};
timer.start();
}//if !ListViewReady
I have achieved the same using a in my opinion slighlty simpler solution
mListView.post(new Runnable() {
#Override
public void run() {
int height = mListView.getHeight();
int itemHeight = mListView.getChildAt(0).getHeight();
if (positionOfMyItem == myCollection.size() - 1) {
// last element - > don't subtract item height
itemHeight = 0;
}
mListView.setSelectionFromTop(position, height / 2 - itemHeight / 2);
}
});