Last active
January 10, 2020 07:50
-
-
Save heruoxin/51b8975676b5d9f1a1964afd3ce0c5b7 to your computer and use it in GitHub Desktop.
修复当 RecyclerView 被其他透明或半透明 View 遮挡时,LinearLayoutManager#findFirstVisibleItemPosition 等方法会失效的问题。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.annotation.SuppressLint; | |
import android.content.Context; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import java.util.HashMap; | |
import java.util.Map; | |
import androidx.annotation.Nullable; | |
import androidx.recyclerview.widget.LinearLayoutManager; | |
import androidx.recyclerview.widget.RecyclerView; | |
/** | |
* @author heruoxin @ CatchingNow Inc. | |
* @since 2019-08-04 | |
*/ | |
public class AdvancedLinearLayoutManager extends LinearLayoutManager { | |
@Nullable | |
private RecyclerView rv; | |
public AdvancedLinearLayoutManager(Context context) { | |
super(context); | |
} | |
public AdvancedLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { | |
super(context, orientation, reverseLayout); | |
} | |
public AdvancedLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { | |
super(context, attrs, defStyleAttr, defStyleRes); | |
} | |
@Override | |
public void onAttachedToWindow(RecyclerView view) { | |
super.onAttachedToWindow(view); | |
rv = view; | |
} | |
@Override | |
public void onDetachedFromWindow(RecyclerView view, RecyclerView.Recycler recycler) { | |
super.onDetachedFromWindow(view, recycler); | |
rv = null; | |
} | |
// map of child adapter position to its height. | |
@SuppressLint("UseSparseArrays") | |
private Map<Integer, Integer> childSizesMap = new HashMap<>(); | |
@Override | |
public void onLayoutCompleted(RecyclerView.State state) { | |
super.onLayoutCompleted(state); | |
for (int i = 0; i < getChildCount(); i++) { | |
View child = getChildAt(i); | |
if (child != null) childSizesMap.put(getPosition(child), getDecoratedMeasuredHeight(child)); | |
} | |
} | |
@Override | |
public int computeVerticalScrollOffset(RecyclerView.State state) { | |
//return super.computeVerticalScrollOffset(state); | |
if (getChildCount() == 0) return 0; | |
int firstChildPosition = findFirstVisibleItemPosition(); | |
View firstChild = findViewByPosition(firstChildPosition); | |
if (firstChild == null) return super.computeVerticalScrollOffset(state); | |
int scrolledY = - getDecoratedTop(firstChild); | |
for (int i = 0; i < firstChildPosition; i++) { | |
Integer size = childSizesMap.get(i); | |
scrolledY += size != null ? size : 0; | |
} | |
return scrolledY + getPaddingTop(); | |
} | |
private int calcItemPosition(Operator operator) { | |
if (rv == null) return RecyclerView.NO_POSITION; | |
boolean vertical = getOrientation() == RecyclerView.VERTICAL; | |
int length = vertical ? rv.getHeight() : rv.getWidth(); | |
int paddingStart = vertical ? rv.getPaddingTop() : rv.getPaddingStart(); | |
int paddingEnd = vertical ? rv.getPaddingBottom() : rv.getPaddingEnd(); | |
View item = null; | |
for (int i = 0; i < rv.getChildCount(); i++) { | |
item = operator.apply(vertical, paddingStart, paddingEnd, length, rv.getChildAt(i), item); | |
} | |
if (item == null) return RecyclerView.NO_POSITION; | |
return getPosition(item); | |
} | |
private interface Operator { | |
View apply(boolean vertical,int paddingStart, int paddingEnd, int length, View v1, View v2); | |
} | |
@Override | |
public int findFirstVisibleItemPosition() { | |
return findFirstVisibleItemPosition(false); | |
} | |
public int findFirstVisibleItemPosition(boolean ignorePadding) { | |
return calcItemPosition((vertical, paddingStart, paddingEnd, length, v1, v2) -> { | |
int actuallyLength = ignorePadding ? 0 : paddingStart; | |
float v1Position = v1 == null ? Float.MAX_VALUE : (vertical ? v1.getY() + v1.getHeight() : v1.getX() + v1.getWidth()); | |
float v2Position = v2 == null ? Float.MAX_VALUE : (vertical ? v2.getY() + v2.getHeight() : v2.getX() + v2.getWidth()); | |
v1Position = v1Position < actuallyLength ? Float.MAX_VALUE : v1Position; | |
v2Position = v2Position < actuallyLength ? Float.MAX_VALUE : v2Position; | |
return v1Position < v2Position ? v1 : v2; | |
}); | |
} | |
@Override | |
public int findFirstCompletelyVisibleItemPosition() { | |
return findFirstCompletelyVisibleItemPosition(false); | |
} | |
public int findFirstCompletelyVisibleItemPosition(boolean ignorePadding) { | |
return calcItemPosition((vertical, paddingStart, paddingEnd, length, v1, v2) -> { | |
int actuallyLength = ignorePadding ? 0 : paddingStart; | |
float v1Position = v1 == null ? Float.MAX_VALUE : (vertical ? v1.getY() : v1.getX()); | |
float v2Position = v2 == null ? Float.MAX_VALUE : (vertical ? v2.getY() : v2.getX()); | |
v1Position = v1Position < actuallyLength ? Float.MAX_VALUE : v1Position; | |
v2Position = v2Position < actuallyLength ? Float.MAX_VALUE : v2Position; | |
return v1Position < v2Position ? v1 : v2; | |
}); | |
} | |
@Override | |
public int findLastVisibleItemPosition() { | |
return findLastVisibleItemPosition(false); | |
} | |
public int findLastVisibleItemPosition(boolean ignorePadding) { | |
return calcItemPosition((vertical, paddingStart, paddingEnd, length, v1, v2) -> { | |
int actuallyLength = ignorePadding ? length : length - paddingEnd; | |
float v1Position = v1 == null ? Float.MIN_VALUE : (vertical ? v1.getY() : v1.getX()); | |
float v2Position = v2 == null ? Float.MIN_VALUE : (vertical ? v2.getY() : v2.getX()); | |
v1Position = v1Position > actuallyLength ? Float.MIN_VALUE : v1Position; | |
v2Position = v2Position > actuallyLength ? Float.MIN_VALUE : v2Position; | |
return v1Position > v2Position ? v1 : v2; | |
}); | |
} | |
@Override | |
public int findLastCompletelyVisibleItemPosition() { | |
return findLastCompletelyVisibleItemPosition(false); | |
} | |
public int findLastCompletelyVisibleItemPosition(boolean ignorePadding) { | |
return calcItemPosition((vertical, paddingStart, paddingEnd, length, v1, v2) -> { | |
int actuallyLength = ignorePadding ? length : length - paddingEnd; | |
float v1Position = v1 == null ? Float.MIN_VALUE : (vertical ? v1.getY() + v1.getHeight() : v1.getX() + v1.getWidth()); | |
float v2Position = v2 == null ? Float.MIN_VALUE : (vertical ? v2.getY() + v2.getHeight() : v2.getX() + v2.getWidth()); | |
v1Position = v1Position > actuallyLength ? Float.MIN_VALUE : v1Position; | |
v2Position = v2Position > actuallyLength ? Float.MIN_VALUE : v2Position; | |
return v1Position > v2Position ? v1 : v2; | |
}); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment