package com.zulip.android.viewholders.stickyheaders; // modified from https://github.com/bgogetap/StickyHeaders/blob/master/stickyheaders/src/main/java/com/brandongogetap/stickyheaders/StickyLayoutManager.java import android.content.Context; import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import com.zulip.android.ZulipApp; import com.zulip.android.viewholders.MessageHeaderParent; import com.zulip.android.viewholders.stickyheaders.interfaces.StickyHeaderHandler; import com.zulip.android.viewholders.stickyheaders.interfaces.StickyHeaderListener; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; public class StickyLayoutManager extends LinearLayoutManager { private GetStickyHeaderPosition positioner; private StickyHeaderHandler headerHandler; private List<Integer> headerPositions; private RetrieveHeaderView viewRetriever; private RecyclerView recyclerView; @Nullable private StickyHeaderListener listener; public StickyLayoutManager(Context context, StickyHeaderHandler headerHandler) { this(context, VERTICAL, false, headerHandler); init(headerHandler); } private StickyLayoutManager(Context context, int orientation, boolean reverseLayout, StickyHeaderHandler headerHandler) { super(context, orientation, reverseLayout); init(headerHandler); } private void init(StickyHeaderHandler stickyHeaderHandler) { setStickyHeaderHandler(stickyHeaderHandler); } private void setStickyHeaderHandler(StickyHeaderHandler headerHandler) { if (headerHandler != null) { this.headerHandler = headerHandler; headerPositions = new ArrayList<>(); } else { throw new NullPointerException("StickyHeaderHandler is null"); } } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { super.onLayoutChildren(recycler, state); cacheHeaderPositions(); positioner.reset(getOrientation(), findFirstVisibleItemPosition()); positioner.updateHeaderState( findFirstVisibleItemPosition(), getVisibleHeaders(), viewRetriever); } private void cacheHeaderPositions() { headerPositions.clear(); for (int i = 0; i < headerHandler.getAdapterData().size(); i++) { if (headerHandler.getAdapterData().get(i) instanceof MessageHeaderParent) { headerPositions.add(i); } } positioner.setHeaderPositions(headerPositions); } @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { int scroll = super.scrollVerticallyBy(dy, recycler, state); if (Math.abs(scroll) > 0) { positioner.updateHeaderState( findFirstVisibleItemPosition(), getVisibleHeaders(), viewRetriever); } return scroll; } @Override public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { int scroll = super.scrollHorizontallyBy(dx, recycler, state); if (Math.abs(scroll) > 0) { positioner.updateHeaderState( findFirstVisibleItemPosition(), getVisibleHeaders(), viewRetriever); } return scroll; } private Map<Integer, View> getVisibleHeaders() { Map<Integer, View> visibleHeaders = new LinkedHashMap<>(); for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); int dataPosition = getPosition(view); if (headerPositions.contains(dataPosition)) { visibleHeaders.put(dataPosition, view); } } return visibleHeaders; } /** * Register a callback to be invoked when a header is attached/re-bound or detached. * * @param listener The callback that will be invoked, or null to unset. */ public void setStickyHeaderListener(@Nullable StickyHeaderListener listener) { this.listener = listener; if (positioner != null) { positioner.setListener(listener); } } @Override public void onAttachedToWindow(RecyclerView view) { super.onAttachedToWindow(view); recyclerView = view; viewRetriever = new RetrieveHeaderView(recyclerView); positioner = new GetStickyHeaderPosition(recyclerView); positioner.setListener(listener); } }