Skip to content

Instantly share code, notes, and snippets.

@SangsooNam
Created September 22, 2015 08:49
Show Gist options
  • Save SangsooNam/90c158d90b8f8f49610d to your computer and use it in GitHub Desktop.
Save SangsooNam/90c158d90b8f8f49610d to your computer and use it in GitHub Desktop.
ScrollToPosition bug fixed version of GridLayoutManager
package android.support.v7.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
/**
* Bug fixed version of GridLayoutManager
* Created by SangsooNam on 20/09/15.
* <p/>
* --- Bug ---
* <br>
* https://code.google.com/p/android/issues/detail?id=182517
* <br>
* After calling `scrollToPosition`, only first grid column is shown and others are shown in
* the other row. This is due to wrong `anchorInfo.mPosition`. There are two modes depending on the
* value `anchorInfo.mLayoutFromEnd`. One is to layout from start to end, and the other is to layout
* from end to start. When to layout from start to end, `ensureAnchorInFirstSpan` is reasonable.
* However, it doesn't make sense when to layout from end to start. At that time, we need to ensure
* that anchor is in the last span instead of the first. This class is fixing that issue.
*/
public class GridLayoutQuirksFixedManager extends GridLayoutManager {
public GridLayoutQuirksFixedManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public GridLayoutQuirksFixedManager(Context context, int spanCount) {
super(context, spanCount);
}
public GridLayoutQuirksFixedManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
@Override
void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) {
updateMeasurements();
if (state.getItemCount() > 0 && !state.isPreLayout()) {
if (anchorInfo.mLayoutFromEnd) {
ensureAnchorIsInLastSpan(anchorInfo);
} else {
ensureAnchorIsInFirstSpan(anchorInfo);
}
}
if (mSet == null || mSet.length != mSpanCount) {
mSet = new View[mSpanCount];
}
}
private void ensureAnchorIsInLastSpan(AnchorInfo anchorInfo) {
int spanIndex = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
int spanSize = mSpanSizeLookup.getSpanSize(anchorInfo.mPosition);
while (spanIndex + spanSize < mSpanCount && anchorInfo.mPosition < getItemCount()) {
anchorInfo.mPosition++;
spanIndex = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
spanSize = mSpanSizeLookup.getSpanSize(anchorInfo.mPosition);
}
}
/**
* The same code from GridLayoutManager
*/
private void ensureAnchorIsInFirstSpan(AnchorInfo anchorInfo) {
int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
while (span > 0 && anchorInfo.mPosition > 0) {
anchorInfo.mPosition--;
span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
}
}
/**
* The same code from GridLayoutManager
*/
private void updateMeasurements() {
int totalSpace;
if (getOrientation() == VERTICAL) {
totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
} else {
totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();
}
calculateItemBorders(totalSpace);
}
/**
* The same code from GridLayoutManager
*/
private void calculateItemBorders(int totalSpace) {
if (mCachedBorders == null || mCachedBorders.length != mSpanCount + 1
|| mCachedBorders[mCachedBorders.length - 1] != totalSpace) {
mCachedBorders = new int[mSpanCount + 1];
}
mCachedBorders[0] = 0;
int sizePerSpan = totalSpace / mSpanCount;
int sizePerSpanRemainder = totalSpace % mSpanCount;
int consumedPixels = 0;
int additionalSize = 0;
for (int i = 1; i <= mSpanCount; i++) {
int itemSize = sizePerSpan;
additionalSize += sizePerSpanRemainder;
if (additionalSize > 0 && (mSpanCount - additionalSize) < sizePerSpanRemainder) {
itemSize += 1;
additionalSize -= mSpanCount;
}
consumedPixels += itemSize;
mCachedBorders[i] = consumedPixels;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment