I'm using a collapsed toolbar for a chat view, everything working fine I have
app:layout_behavior="@string/appbar_scrolling_view_behavior"
on my recycler view and
app:layout_scrollFlags="scroll|enterAlways|snap"
on the retractable header
I'm using it for a chat view so the first element in recyclerView are at the bottom of the list due to
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setReverseLayout(true);
The thing is I would like to hide header when I'm scrolling up and hide it when I'm scrolling down, the exact inverse of the actual behavior. And the header appears when I reach the top of the list, (the oldest message) instead I would like to make it visible when I reach the bottom when I'm showing the latest message
I found a solution I'm using the old way with OnScrollListener for retractable header and inverse it for fit to the reverse layout manager
public abstract class HidingScrollListener extends RecyclerView.OnScrollListener {
private static final float HIDE_THRESHOLD = 10;
private static final float SHOW_THRESHOLD = 70;
private int mToolbarOffset = 0;
private boolean mControlsVisible = true;
private int mToolbarHeight;
private int mTotalScrolledDistance;
public HidingScrollListener(int mToolbarHeight) {
this.mToolbarHeight = mToolbarHeight;
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (mTotalScrolledDistance < mToolbarHeight) {
setVisible();
} else {
if (mControlsVisible) {
if (mToolbarOffset > HIDE_THRESHOLD) {
setInvisible();
} else {
setVisible();
}
} else {
if ((mToolbarHeight - mToolbarOffset) > SHOW_THRESHOLD) {
setVisible();
} else {
setInvisible();
}
}
}
}
}
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
dy = dy * -1;
clipToolbarOffset();
onMoved(mToolbarOffset);
if ((mToolbarOffset < mToolbarHeight && dy > 0) || (mToolbarOffset > 0 && dy < 0)) {
mToolbarOffset += dy;
}
mTotalScrolledDistance += dy;
}
private void clipToolbarOffset() {
if (mToolbarOffset > mToolbarHeight) {
mToolbarOffset = mToolbarHeight;
} else if (mToolbarOffset < 0) {
mToolbarOffset = 0;
}
}
private void setVisible() {
if (mToolbarOffset > 0) {
onShow();
mToolbarOffset = 0;
}
mControlsVisible = true;
}
private void setInvisible() {
if (mToolbarOffset < mToolbarHeight) {
onHide();
mToolbarOffset = mToolbarHeight;
}
mControlsVisible = false;
}
public abstract void onMoved(int distance);
public abstract void onShow();
public abstract void onHide();
}
And then I'm using it like this in my Fragment
dataBinding.chatDetailRoot.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mToolbarHeight = dataBinding.chatDetailAdsHeader.getHeight();
dataBinding.chatDetailRecyclerView.addOnScrollListener(new HidingScrollListener(mToolbarHeight) {
@Override
public void onMoved(int distance) {
dataBinding.chatDetailAdsHeader.setTranslationY(-distance);
}
@Override
public void onShow() {
dataBinding.chatDetailAdsHeader.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2)).start();
}
@Override
public void onHide() {
dataBinding.chatDetailAdsHeader.animate().translationY(-mToolbarHeight).setInterpolator(new AccelerateInterpolator(2)).start();
}
});
dataBinding.chatDetailRoot.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
I'm using a viewTreeObserver because I need to get the toolbar height and chatDetailAdsHeader
is my AppBarLayout
component