QuickReturn
QuickReturn copied to clipboard
The firstVisibleItem position is wrong when the top target view is out of screen.
In the sample, in the list view's onScroll method.
Re-produce step: Scroll up and make the top target disappear, then the firstVisibleItem is immediately 1 which should be 0. It will be bigger than expected if you continue to scroll up.
I add adjustFirstVisibleItem method, and changes the CompositeAbsListViewOnScrollListener's onScroll method to deliver the modification to other onScrollListener.
public class AbsListViewScrollTarget
extends QuickReturnTargetView
implements AbsListView.OnScrollListener {
private final AbsListView listView;
private int quickViewHeight;
private int itemHeight;
private int adjustedFirstVisibleItem;
public AbsListViewScrollTarget(AbsListView listView, View targetView,
int position) {
this(listView, targetView, position, 0);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public AbsListViewScrollTarget(AbsListView listView, View targetView,
int position, int targetViewHeight) {
super(targetView, position);
quickViewHeight = targetViewHeight;
this.listView = listView;
QuickReturnAdapter adapter = getAdapter();
if (adapter == null) {
throw new UnsupportedOperationException(
"You need to set the listView adapter before adding a targetView");
}
if (position == POSITION_TOP) {
adapter.setTargetViewHeight(targetViewHeight);
}
if (listView instanceof ListView) {
adapter.setVerticalSpacing(((ListView) listView).getDividerHeight());
} else if (listView instanceof GridView
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
adapter.setVerticalSpacing(((GridView) listView).getVerticalSpacing());
}
}
@Override
protected int getComputedScrollY() {
if (listView.getChildCount() == 0 || listView.getAdapter() == null) {
return 0;
}
int pos = listView.getFirstVisiblePosition();
View view = listView.getChildAt(0);
return getAdapter().getPositionVerticalOffset(pos) - view.getTop();
}
private QuickReturnAdapter getAdapter() {
ListAdapter adapter = listView.getAdapter();
if (adapter instanceof WrapperListAdapter) {
adapter = ((WrapperListAdapter) adapter).getWrappedAdapter();
}
if (!(adapter instanceof QuickReturnAdapter)) {
throw new UnsupportedOperationException(
"Your QuickReturn ListView adapter must be an instance of QuickReturnAdapter.");
}
return (QuickReturnAdapter) adapter;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(@NonNull AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (listView.getAdapter() == null || quickReturnView == null) {
return;
}
calculateItemHeight(firstVisibleItem);
// The maxVerticalOffset is less than 1.
int maxVerticalOffset = getAdapter().getMaxVerticalOffset() + itemHeight;
int listViewHeight = listView.getHeight();
int rawY = -Math.min(maxVerticalOffset > listViewHeight
? maxVerticalOffset - listViewHeight
: listViewHeight, getComputedScrollY());
int translationY = currentTransition.determineState(rawY, quickReturnView.getHeight());
translateTo(translationY);
Log.d(TAG, "maxVerticalOffset: " + maxVerticalOffset +
", listViewHeight: " + listViewHeight +
", rawY: " + rawY +
", getComputedScrollY(): " + getComputedScrollY() +
", translationY: " + translationY);
adjustFirstVisibleItem(firstVisibleItem, rawY, translationY);
}
private void calculateItemHeight(int firstVisibleItem) {
if (itemHeight == 0) {
View view = listView.getAdapter().getView(firstVisibleItem, null, listView);
view.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
itemHeight = view.getMeasuredHeight();
}
}
private void adjustFirstVisibleItem(int firstVisibleItem, int rawY, int translationY) {
if (listView.getAdapter().getCount() == 0) return;
if (translationY > 0) {
return;
}
if (itemHeight == 0) return;
int f;
if (rawY > 0) {
rawY = 0;
}
if (translationY > 0) {
translationY = 0;
}
if (translationY < -quickViewHeight) {
f = Math.abs(rawY + quickViewHeight) / itemHeight;
} else if (-quickViewHeight < translationY && translationY < 0) {
f = Math.abs(rawY + Math.abs(quickViewHeight + translationY)) / itemHeight;
} else {
f = Math.abs(rawY) / itemHeight;
}
Log.d(TAG, "actual first visible item: " + f);
adjustedFirstVisibleItem = f;
}
public int getAdjustedFirstVisibleItem() {
return adjustedFirstVisibleItem;
}
}
public class CompositeAbsListViewOnScrollListener
extends ArrayList<AbsListView.OnScrollListener>
implements AbsListView.OnScrollListener {
public void registerOnScrollListener(final AbsListView.OnScrollListener listener) {
add(listener);
}
public void unregisterOnScrollListener(final AbsListView.OnScrollListener listener) {
remove(listener);
}
@Override
public void onScrollStateChanged(final AbsListView view, final int scrollState) {
for (AbsListView.OnScrollListener listener : this) {
listener.onScrollStateChanged(view, scrollState);
}
}
@Override
public void onScroll(final AbsListView view, final int firstVisibleItem,
final int visibleItemCount, final int totalItemCount) {
if (this.size() <= 0) {
return;
}
AbsListViewScrollTarget adjustedListener = (AbsListViewScrollTarget)this.get(0);
adjustedListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
int f = adjustedListener.getAdjustedFirstVisibleItem();
Log.d("CompositeAbsList", "adjusted: " + f);
for (int i = 1; i < this.size(); ++i) {
this.get(i).onScroll(view, f, visibleItemCount, totalItemCount);
}
}
}