Created
July 14, 2021 08:15
-
-
Save puke3615/119d3787a1e85e5d4207408b1cf57319 to your computer and use it in GitHub Desktop.
RecyclerViewScrollHelper
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.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