/**
* smoothScrollToEnd will scroll the message list to the bottom if the list is already near* the bottom. Typically this is called to smooth scroll a newly received message into view.
* It's also called when sending to scroll the list to the bottom, regardless of where it is,
* so the user can see the just sent message. This function is also called when the message
* list view changes size because the keyboard state changed or the compose message field grew.
*
* @param force always scroll to the bottom regardless of current list position
* @param listSizeChange the amount the message list view size has vertically changed
*/
private void smoothScrollToEnd(boolean force, int listSizeChange) {
int last = mMsgListView.getLastVisiblePosition();
int newPosition = mMsgListAdapter.getCount() - 1;
if (last < 0 || newPosition < 0) {
if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.v(TAG, "smoothScrollToEnd: last=" + last + ", newPos=" + newPosition +
", mMsgListView not ready");
}
return;
}
View lastChild = mMsgListView.getChildAt(last - mMsgListView.getFirstVisiblePosition());
int bottom = 0;
int height = 0;
if (lastChild != null) {
bottom = lastChild.getBottom();
height = lastChild.getHeight();
}
if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.v(TAG, "smoothScrollToEnd newPosition: " + newPosition +
" mLastSmoothScrollPosition: " + mLastSmoothScrollPosition +
" first: " + mMsgListView.getFirstVisiblePosition() +
" last: " + last +
" bottom: " + bottom +
" bottom + listSizeChange: " + (bottom + listSizeChange) +
" mMsgListView.getHeight() - mMsgListView.getPaddingBottom(): " +
(mMsgListView.getHeight() - mMsgListView.getPaddingBottom()) +
" listSizeChange: " + listSizeChange);
}
// Only scroll if the list if we're responding to a newly sent message (force == true) or
// the list is already scrolled to the end. This code also has to handle the case where
// the listview has changed size (from the keyboard coming up or down or the message entry
// field growing/shrinking) and it uses that grow/shrink factor in listSizeChange to
// compute whether the list was at the end before the resize took place.
// For example, when the keyboard comes up, listSizeChange will be negative, something
// like -524. The lastChild listitem's bottom value will be the old value before the
// keyboard became visible but the size of the list will have changed. The test below
// add listSizeChange to bottom to figure out if the old position was already scrolled
// to the bottom. We also scroll the list if the last item is taller than the size of the
// list. This happens when the keyboard is up and the last item is an mms with an
// attachment thumbnail, such as picture. In this situation, we want to scroll the list so
// the bottom of the thumbnail is visible and the top of the item is scroll off the screen.
int listHeight = mMsgListView.getHeight();
if (force || ((listSizeChange != 0 || newPosition != mLastSmoothScrollPosition) &&
bottom + listSizeChange <=
listHeight - mMsgListView.getPaddingBottom()) ||
height > listHeight) {
if (Math.abs(listSizeChange) > SMOOTH_SCROLL_THRESHOLD) {
// When the keyboard comes up, the window manager initiates a cross fade
// animation that conflicts with smooth scroll. Handle that case by jumping the
// list directly to the end.
if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.v(TAG, "keyboard state changed. setSelection=" + newPosition);
}
if (height > listHeight) {
// If the height of the last item is taller than the whole height of the list,
// we need to scroll that item so that its top is negative or above the top of
// the list. That way, the bottom of the last item will be exposed above the
// keyboard.
mMsgListView.setSelectionFromTop(newPosition, listHeight - height);
} else {
mMsgListView.setSelection(newPosition);
}
} else if (newPosition - last > MAX_ITEMS_TO_INVOKE_SCROLL_SHORTCUT) {
if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.v(TAG, "too many to scroll, setSelection=" + newPosition);
}
mMsgListView.setSelection(newPosition);
} else {
if (LogTag.VERBOSE || Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.v(TAG, "smooth scroll to " + newPosition);
}
if (height > listHeight) {
// If the height of the last item is taller than the whole height of the list,
// we need to scroll that item so that its top is negative or above the top of
// the list. That way, the bottom of the last item will be exposed above the
// keyboard.
mMsgListView.setSelectionFromTop(newPosition, listHeight - height);
} else {
mMsgListView.smoothScrollToPosition(newPosition);
}
mLastSmoothScrollPosition = newPosition;
}
}
}
上面的函数只适用于平滑的滚动到最下方的情况, 比如发消息或接收消息后, 平滑的滚动到最后一行。
调用:
1. 全屏模式下, 接收或发消息, 调用smoothScrollToEnd(mScrollOnSend || lastMsgId != mLastMessageId, 0); 即(true, 0)
2. 键盘弹起导致ListView高度发生了变化的情况,
mMsgListView.setOnSizeChangedListener(new OnSizeChangedListener() {
public void onSizeChanged(int width, int height, int oldWidth, int oldHeight) {
if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
Log.v(TAG, "onSizeChanged: w=" + width + " h=" + height +
" oldw=" + oldWidth + " oldh=" + oldHeight);
}
// The message list view changed size, most likely because the keyboard
// appeared or disappeared or the user typed/deleted chars in the message
// box causing it to change its height when expanding/collapsing to hold more
// lines of text.
smoothScrollToEnd(false, height - oldHeight);
}
});