4

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?

Abu Yousuf
  • 5,729
  • 3
  • 31
  • 50
Alexey
  • 2,980
  • 4
  • 30
  • 53

1 Answers1

2

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);
                }
            }
        );
    }
};
Mike M.
  • 38,532
  • 8
  • 99
  • 95
  • It's the solution, thank you! But why "this wouldn't be a problem if their code actually did what the comment says"? Having, for example, two items in list before entering the cycle, that code definitely causes IOOB while calling `mListeners.get(1)` on the second pass after removing the first item. There better do the job with iterator. – Alexey Jun 27 '16 at 17:28
  • If they went backwards - i.e., started at `i = mListeners.size() - 1`, and decremented `i` instead - removing an element would still shrink the `List`, but it wouldn't hit an index that doesn't exist, since it's going to `0`. It seems like they meant to change it, but goofed; a bad merge, or something. The comment sounds incomplete. – Mike M. Jun 27 '16 at 22:32