Skip to content

Instantly share code, notes, and snippets.

@puke3615
Created July 14, 2021 08:15
Show Gist options
  • Save puke3615/119d3787a1e85e5d4207408b1cf57319 to your computer and use it in GitHub Desktop.
Save puke3615/119d3787a1e85e5d4207408b1cf57319 to your computer and use it in GitHub Desktop.
RecyclerViewScrollHelper
import android.support.annotation.FloatRange;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author puke
* @version 2019/8/15
*/
public class RecyclerViewScrollHelper {
public static final int DEFAULT_VALVE = DisplayUtils.dp2px(50);
private final AtomContainer atomContainer;
public interface OnScrollProgressListener {
void onScrollProgress(@FloatRange(from = 0, to = 1) float progress);
}
public RecyclerViewScrollHelper(RecyclerView recyclerView) {
if (recyclerView == null) {
throw new IllegalArgumentException("The RecyclerView must not be null.");
}
this.atomContainer = new AtomContainer();
recyclerView.addOnScrollListener(new ScrollListener());
}
public static NoRepeatScrollProgressListener unRepeatListener(OnScrollProgressListener progressListener) {
return new NoRepeatScrollProgressListener(progressListener);
}
public void addListener(OnScrollProgressListener progressListener) {
addListener(DEFAULT_VALVE, progressListener);
}
public void addListener(int scrollValve, OnScrollProgressListener progressListener) {
if (scrollValve <= 0) {
throw new IllegalArgumentException("The scroll valve must be positive.");
}
atomContainer.addListener(scrollValve, progressListener);
}
public void removeListener(OnScrollProgressListener progressListener) {
atomContainer.removeListener(progressListener);
}
public void removeAllListeners() {
atomContainer.removeAllListeners();
}
private class ScrollListener extends RecyclerView.OnScrollListener {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
if (!(recyclerView.getLayoutManager() instanceof LinearLayoutManager)) {
throw new UnsupportedOperationException("Current layout manager is not supported");
}
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int position = layoutManager.findFirstVisibleItemPosition();
for (OnScrollProgressListener listener : atomContainer.listeners) {
final float progress;
if (position == 0) {
Integer valve = atomContainer.listener2Valve.get(listener);
View firstVisibleChildView = layoutManager.findViewByPosition(position);
progress = -firstVisibleChildView.getTop() / (float) valve;
} else {
progress = 1;
}
listener.onScrollProgress(formatProgress(progress));
}
}
private float formatProgress(float progress) {
if (progress < 0) {
progress = 0;
} else if (progress > 1) {
progress = 1;
}
return progress;
}
}
public static class NoRepeatScrollProgressListener implements OnScrollProgressListener {
private final OnScrollProgressListener progressListener;
private float lastProgress = -1;
public NoRepeatScrollProgressListener(OnScrollProgressListener progressListener) {
this.progressListener = progressListener;
}
@Override
public void onScrollProgress(float progress) {
if (lastProgress != progress && progressListener != null) {
progressListener.onScrollProgress(progress);
}
this.lastProgress = progress;
}
}
/* 原子容器, 保证 listeners 和 listener2Valve 的原子性; 外部只做读操作, 写操作统一由该类包装 */
private static class AtomContainer {
final List<OnScrollProgressListener> listeners;
final Map<OnScrollProgressListener, Integer> listener2Valve;
AtomContainer() {
this.listeners = new ArrayList<>();
this.listener2Valve = new HashMap<>();
}
void addListener(int scrollValve, OnScrollProgressListener progressListener) {
if (progressListener != null && !listeners.contains(progressListener)) {
listener2Valve.put(progressListener, scrollValve);
listeners.add(progressListener);
}
}
void removeListener(OnScrollProgressListener progressListener) {
if (progressListener != null && listeners.contains(progressListener)) {
listeners.remove(progressListener);
listener2Valve.remove(progressListener);
}
}
void removeAllListeners() {
listeners.clear();
listener2Valve.clear();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment