Bug P2
Status Update
Comments
jg...@google.com <jg...@google.com>
wu...@gmail.com <wu...@gmail.com> #2
I face with the same problem. I have different pageMargin in landscape and portrait and when viewpager is scrolled for the more than first position, after rotating device it is shifted.
I beleive the problem is inside onSizeChanged that is called after onConfigurationChanged, it has following code:
if (w != oldw) {
recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin);
}
w always not equals oldw when changing orientation, so recomputeScrollPosition is called, but whit the same values for margin and oldMargin.
This should be fixed.
I beleive the problem is inside onSizeChanged that is called after onConfigurationChanged, it has following code:
if (w != oldw) {
recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin);
}
w always not equals oldw when changing orientation, so recomputeScrollPosition is called, but whit the same values for margin and oldMargin.
This should be fixed.
Description
It's the ViewPager.recomputeScrollPosition() source code:
--
private void recomputeScrollPosition(int width, int oldWidth, int margin, int oldMargin) {
if (oldWidth > 0 && !mItems.isEmpty()) {
if (!mScroller.isFinished()) {
mScroller.setFinalX(getCurrentItem() * getClientWidth());
} else {
final int widthWithMargin = width - getPaddingLeft() - getPaddingRight() + margin;
final int oldWidthWithMargin = oldWidth - getPaddingLeft() - getPaddingRight()
+ oldMargin;
final int xpos = getScrollX();
final float pageOffset = (float) xpos / oldWidthWithMargin;
final int newOffsetPixels = (int) (pageOffset * widthWithMargin);
scrollTo(newOffsetPixels, getScrollY());
}
} else {
final ItemInfo ii = infoForPosition(mCurItem);
final float scrollOffset = ii != null ? Math.min(ii.offset, mLastOffset) : 0;
final int scrollPos =
(int) (scrollOffset * (width - getPaddingLeft() - getPaddingRight()));
if (scrollPos != getScrollX()) {
completeScroll(false);
scrollTo(scrollPos, getScrollY());
}
}
}
--
We can find that 'final float pageOffset = (float) xpos / oldWidthWithMargin;' consider the pageMargin.As we know, the scrollX of the specified position Item was set by this code:
--
private void scrollToItem(int item, boolean smoothScroll, int velocity,
boolean dispatchSelected) {
final ItemInfo curInfo = infoForPosition(item);
int destX = 0;
if (curInfo != null) {
final int width = getClientWidth();
destX = (int) (width * Math.max(mFirstOffset,
Math.min(curInfo.offset, mLastOffset)));
}
if (smoothScroll) {
smoothScrollTo(destX, 0, velocity);
if (dispatchSelected) {
dispatchOnPageSelected(item);
}
} else {
if (dispatchSelected) {
dispatchOnPageSelected(item);
}
completeScroll(false);
scrollTo(destX, 0);
pageScrolled(destX);
}
}
--
'curInfo.offset' was set in method 'calculatePageOffsets()':
--
private void calculatePageOffsets(ItemInfo curItem, int curIndex, ItemInfo oldCurInfo) {
final int N = mAdapter.getCount();
final int width = getClientWidth();
final float marginOffset = width > 0 ? (float) mPageMargin / width : 0;
// Fix up offsets for later layout.
if (oldCurInfo != null) {
final int oldCurPosition = oldCurInfo.position;
// Base offsets off of oldCurInfo.
if (oldCurPosition < curItem.position) {
int itemIndex = 0;
ItemInfo ii = null;
float offset = oldCurInfo.offset + oldCurInfo.widthFactor + marginOffset;
for (int pos = oldCurPosition + 1;
pos <= curItem.position && itemIndex < mItems.size(); pos++) {
ii = mItems.get(itemIndex);
while (pos > ii.position && itemIndex < mItems.size() - 1) {
itemIndex++;
ii = mItems.get(itemIndex);
}
while (pos < ii.position) {
// We don't have an item populated for this,
// ask the adapter for an offset.
offset += mAdapter.getPageWidth(pos) + marginOffset;
pos++;
}
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
} else if (oldCurPosition > curItem.position) {
int itemIndex = mItems.size() - 1;
ItemInfo ii = null;
float offset = oldCurInfo.offset;
for (int pos = oldCurPosition - 1;
pos >= curItem.position && itemIndex >= 0; pos--) {
ii = mItems.get(itemIndex);
while (pos < ii.position && itemIndex > 0) {
itemIndex--;
ii = mItems.get(itemIndex);
}
while (pos > ii.position) {
// We don't have an item populated for this,
// ask the adapter for an offset.
offset -= mAdapter.getPageWidth(pos) + marginOffset;
pos--;
}
offset -= ii.widthFactor + marginOffset;
ii.offset = offset;
}
}
}
// Base all offsets off of curItem.
final int itemCount = mItems.size();
float offset = curItem.offset;
int pos = curItem.position - 1;
mFirstOffset = curItem.position == 0 ? curItem.offset : -Float.MAX_VALUE;
mLastOffset = curItem.position == N - 1
? curItem.offset + curItem.widthFactor - 1 : Float.MAX_VALUE;
// Previous pages
for (int i = curIndex - 1; i >= 0; i--, pos--) {
final ItemInfo ii = mItems.get(i);
while (pos > ii.position) {
offset -= mAdapter.getPageWidth(pos--) + marginOffset;
}
offset -= ii.widthFactor + marginOffset;
ii.offset = offset;
if (ii.position == 0) mFirstOffset = offset;
}
offset = curItem.offset + curItem.widthFactor + marginOffset;
pos = curItem.position + 1;
// Next pages
for (int i = curIndex + 1; i < itemCount; i++, pos++) {
final ItemInfo ii = mItems.get(i);
while (pos < ii.position) {
offset += mAdapter.getPageWidth(pos++) + marginOffset;
}
if (ii.position == N - 1) {
mLastOffset = offset + ii.widthFactor - 1;
}
ii.offset = offset;
offset += ii.widthFactor + marginOffset;
}
mNeedCalculatePageOffsets = false;
}
--
We can figure out setting 'offset' already consider the 'pageMargin',but when invoke 'recomputeScrollPosition()',the code 'final float pageOffset = (float) xpos / oldWidthWithMargin;' lead to pageOffset wrong.'float pageOffset = (float) xpos / oldWidth' will be the true answer.
By the way, all versions of ViewPage included in 'android support library' and 'android source code', the error exist.
Please fix it as soon as possible.