Yuchen
Yuchen

Reputation: 33126

Create Observable with dispose handler rxjava

We want to observe the changes in the size of a view and we create an extension like this:

fun View.layoutSizeObservable(): io.reactivex.Observable<Size> {
    return io.reactivex.Observable.create<Size> { emitter ->
        viewTreeObserver.addOnGlobalLayoutListener {
            Log.d("MainFragment", "ViewTreeObserver Listener called back.")
            if (measuredWidth > 0 && measuredHeight > 0) {
                emitter.onNext(Size(measuredWidth, measuredHeight))
            }
        }
    }
}

And then we use it like this, which works fine functionally:

sizeChangedDisposable = titleTextView.layoutSizeObservable().subscribe { size: Size ->
    Log.d("MainFragment", "Size changed subscribe on $size")
}

However, one thing that is not exactly what we want is that the Listener is added via addOnGlobalLayoutListener, but never removed.

We can call sizeChangedDisposable.dispose() which will properly stop the subscription:

D/MainFragment: Size changed subscribe on $size

But this will continue to get called:

D/MainFragment: ViewTreeObserver Listener called back.

How and where do we remove the layout listener call back?

Upvotes: 2

Views: 1637

Answers (1)

Omar Mainegra
Omar Mainegra

Reputation: 4194

You need to remove the listener when the Disposable is disposed. To be able to do that, set a Cancellable action to the emitter removing the listener.

Example

fun View.layoutSizeObservable(): io.reactivex.Observable<Size> {
    return io.reactivex.Observable.create<Size> { emitter ->
        val listener = ViewTreeObserver.OnGlobalLayoutListener {
            Log.d("MainFragment", "ViewTreeObserver Listener called back.")
            if (measuredWidth > 0 && measuredHeight > 0) {
                emitter.onNext(Size(measuredWidth, measuredHeight))
            }
        }

        viewTreeObserver.addOnGlobalLayoutListener(listener)

        emitter.setCancellable {
            Log.d("MainFragment", "ViewTreeObserver Listener removed.")
            viewTreeObserver.removeOnGlobalLayoutListener(listener)
        }
    }
}

BTW, RxBinding library already has an Observable for global layout listener

Upvotes: 5

Related Questions