Search code examples
androidindexoutofboundsexceptionandroid-appbarlayout

AppBarLayout: how to removeOnOffsetChangedListener


In order to detect finish of app bar collapsing I called addOnOffsetChangedListener. In listener's onOffsetChanged I catch and handle the moment of collapsing done. Then I need to stop listen for offset changes.

In most examples here is call of removeOnOffsetChangedListener(this) from inside of onOffsetChanged. But looking in AppBarLayout.java I see:

private void dispatchOffsetUpdates(int offset) {
    // Iterate backwards through the list so that most recently added listeners
    // get the first chance to decide
    if (mListeners != null) {
        for (int i = 0, z = mListeners.size(); i < z; i++) {
            final OnOffsetChangedListener listener = mListeners.get(i);
            if (listener != null) {
                listener.onOffsetChanged(this, offset);
            }
        }
    }
}

So if there is more than one listener installed, calling removeOnOffsetChangedListener(this) naturally results in IndexOutOfBoundsException.

Have I missed something? Is there safe way to 'unsubscribe' from listening for offset updates?


Solution

  • Interestingly, this wouldn't be a problem if their code actually did what the comment says. Anyway, we can defer the removal by putting the call to removeOnOffsetChangedListener() in a Runnable, and posting it to the AppBarLayout in onOffsetChanged(), instead of calling it directly there.

    For example:

    AppBarLayout.OnOffsetChangedListener listener = new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(final AppBarLayout abl, int offset) {
            abl.post(new Runnable() {
                    @Override
                    public void run() {
                        abl.removeOnOffsetChangedListener(listener);
                    }
                }
            );
        }
    };