How to listen for multiple GlobalLayout events

I am trying to listen for GlobalLayout using

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    int c=0;
    @Override
    public void onGlobalLayout() {
        c++; //without removing the listener c will grow for ever even though there is no GlobalLayout events
        view.setText(""+c);
    }
});

but it’s called endlessly.


I know I should remove the listener like this: view.getViewTreeObserver().removeOnGlobalLayoutListener(this);

But I want to keep the listener alive for the next GlobalLayout events.

Currently I am just trying to listen for view position changing using this question I tried onPreDraw but it is the same.


Is it possible to listen for several GlobalLayout events?



Thanks in advance.

Answer

The code you posted as an example shouldn’t compile because of issues with accessing the variable c from the listener. If you try it, you should get the following error in Android Studio:

Variable ‘c’ is accessed from within inner class, needs to be final or effectively final

We can take the suggestion that error check suggests and create a one-element array as follows:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final int[] c = {0};
        final View[] view = {findViewById(R.id.textView)};
        view[0].getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                c[0]++;
            }
        });
    }
}

If you run this code against the following layout, you will see that the listener is called twice which I think corresponds to two layout passes:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

If the layout is somehow modified within the global layout listener, then it will trigger another layout and another call to the the listener. If the listener again makes a change, the listener will be called yet again and so on forever.

It would be helpful if you would post the actual code you are having trouble with or a simple project that demonstrates the problem. Is the layout somehow modified within the listener?


Update: As you say in one of your comments on this answer, your issue was that you made a change to a view in the global layout listener which triggered another layout and another call to the listener ad infinitum. Once you removed the code that made the layout change, that particular issue was resolved.

Leave a Reply

Your email address will not be published. Required fields are marked *