Reputation: 3249
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.
GlobalLayout
events?
Thanks in advance.
Upvotes: 6
Views: 1599
Reputation: 10235
Simply add a flag to detect if it is a normal event or a setText()
that is called by the listener and skip it if needed.
Code not tested. But I guess you will get the idea.
int c=0;
view.getViewTreeObserver().addOnGlobalLayoutListener(new CustomLayoutListener() {
int c=0;
@Override
public void onGlobalLayout() {
if(skipEvent)
return;
c++;
//flag skipEvent while performing setText()
skipEvent = true;
view.setText(""+c);
skipEvent = false;
}
});
class CustomLayoutListener extends ViewTreeObserver.OnGlobalLayoutListener {
static boolean skipEvent;
@Override
public void onGlobalLayout() {}
}
Upvotes: 0
Reputation: 62831
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.
Upvotes: 1
Reputation: 181
you can set the Listener on your Viewes try to use view.addOnLayoutChangeListener
https://developer.android.com/reference/android/view/View.OnLayoutChangeListener
it will give you the old and new X&Y of your layout and you can adjust your view base on it
Upvotes: 0
Reputation: 552
If you're just trying to trigger the code once the view position has changed, then you could probably just check if the x, y coordinates has changed before increasing the variable c
Something like this:
int c = 0;
int x = 0;
int y = 0;
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int x2 = view.getX();
int y2 = view.getY();
// do not increase c unless position has changed
if (x2 != x && y2 != y) {
c++;
// update coordinates values
x = x2;
y = y2;
}
}
});
Upvotes: 0