ViewTreeObserver: implement onGlobalLayout properly

This commit is contained in:
Mis012
2025-10-23 17:11:31 +02:00
parent 9ab58e4736
commit 451e23fcd2
15 changed files with 189 additions and 42 deletions

View File

@@ -867,7 +867,7 @@ public class View implements Drawable.Callback {
private Context context;
private Map<Integer,Object> tags = new HashMap<>();
private Object tag;
int gravity = -1; // fallback gravity for layout childs
int gravity = -1; // fallback gravity for layout children
int measuredWidth = 0;
int measuredHeight = 0;
@@ -880,6 +880,8 @@ public class View implements Drawable.Callback {
private int scrollX = 0;
private int scrollY = 0;
ViewTreeObserver floating_observer = null;
public long widget; // pointer
private int oldWidthMeasureSpec = -1;
@@ -1236,8 +1238,17 @@ public class View implements Drawable.Callback {
public void setSelected(boolean selected) {}
public native Window native_get_window(long widget);
public ViewTreeObserver getViewTreeObserver() {
return new ViewTreeObserver();
Window window = native_get_window(widget);
if (window != null) {
if (window.view_tree_observer == null)
window.view_tree_observer = new ViewTreeObserver(window);
return window.view_tree_observer;
} else {
floating_observer = new ViewTreeObserver(null);
return floating_observer;
}
}
protected void onFinishInflate() {}
@@ -1961,8 +1972,24 @@ public class View implements Drawable.Callback {
keepScreenOn = screenOn;
}
protected void onAttachedToWindow() {}
protected void onDetachedFromWindow() {}
protected void onAttachedToWindow() {
if (onAttachStateChangeListener != null) {
onAttachStateChangeListener.onViewAttachedToWindow(this);
}
if (keepScreenOn)
native_keep_screen_on(widget, true);
if(floating_observer != null) {
getViewTreeObserver().merge(floating_observer);
floating_observer = null;
}
}
protected void onDetachedFromWindow() {
if (onAttachStateChangeListener != null) {
onAttachStateChangeListener.onViewDetachedFromWindow(this);
}
if (keepScreenOn)
native_keep_screen_on(widget, false);
}
public void setLayerType(int layerType, Paint paint) {}
@@ -2274,4 +2301,8 @@ public class View implements Drawable.Callback {
public Bitmap getDrawingCache() { return null; }
public void announceForAccessibility(CharSequence text) {}
public WindowInsetsController getWindowInsetsController() {
return native_get_window(widget).getInsetsController();
}
}

View File

@@ -51,6 +51,11 @@ public final class ViewTreeObserver {
private boolean mAlive = true;
// accessed from native code
private long onGlobalLayout_signal_handle = 0;
private long window;
/**
* Interface definition for a callback to be invoked when the view hierarchy is
* attached to and detached from its window.
@@ -297,10 +302,9 @@ public final class ViewTreeObserver {
public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
}
/**
* Creates a new ViewTreeObserver. This constructor should not be called
*/
ViewTreeObserver() {
ViewTreeObserver(Window window) {
if(window != null)
this.window = window.native_window;
}
/**
@@ -335,7 +339,10 @@ public final class ViewTreeObserver {
}
}
if (observer.mOnGlobalLayoutListeners != null) {
if (observer.mOnGlobalLayoutListeners != null && observer.mOnGlobalLayoutListeners.size() > 0) {
if(mOnGlobalLayoutListeners == null || mOnGlobalLayoutListeners.size() == 0)
native_set_have_global_layout_listeners(true);
if (mOnGlobalLayoutListeners != null) {
mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
} else {
@@ -495,17 +502,11 @@ public final class ViewTreeObserver {
mOnGlobalLayoutListeners = new CopyOnWriteArray<OnGlobalLayoutListener>();
}
if (mOnGlobalLayoutListeners.size() == 0) {
native_set_have_global_layout_listeners(true);
}
mOnGlobalLayoutListeners.add(listener);
// hack: many Applications wait for the global layout before doing anything
// so we dispatch the event immediately
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
listener.onGlobalLayout();
}
});
}
/**
@@ -539,6 +540,9 @@ public final class ViewTreeObserver {
return;
}
mOnGlobalLayoutListeners.remove(victim);
if(mOnGlobalLayoutListeners.size() == 0)
native_set_have_global_layout_listeners(false);
}
/**
@@ -754,6 +758,9 @@ public final class ViewTreeObserver {
* @hide
*/
private void kill() {
/* clear any callbacks */
if (mOnGlobalLayoutListeners != null && mOnGlobalLayoutListeners.size() > 0)
native_set_have_global_layout_listeners(false);
mAlive = false;
}
@@ -1038,4 +1045,6 @@ public final class ViewTreeObserver {
getArray().clear();
}
}
private native void native_set_have_global_layout_listeners(boolean have_listeners);
}

View File

@@ -10,6 +10,8 @@ public class Window {
public static final int FEATURE_OPTIONS_PANEL = 0;
public static final int FEATURE_NO_TITLE = 1;
public ViewTreeObserver view_tree_observer = null;
public static interface Callback {
public void onContentChanged();
@@ -41,6 +43,11 @@ public class Window {
decorView.setId(android.R.id.content);
}
public void set_native_window(long native_window) {
this.native_window = native_window;
set_jobject(native_window, this);
}
public void addFlags(int flags) {}
public void setFlags(int flags, int mask) {}
public void clearFlags(int flags) {}
@@ -64,12 +71,6 @@ public class Window {
return decorView;
}
public native void set_widget_as_root(long native_window, long widget);
private native void set_title(long native_window, String title);
public native void take_input_queue(long native_window, InputQueue.Callback callback, InputQueue queue);
public native void set_layout(long native_window, int width, int height);
public void takeInputQueue(InputQueue.Callback callback) {
take_input_queue(native_window, callback, new InputQueue());
}
@@ -161,4 +162,16 @@ public class Window {
public void setEnterTransition(Transition transition) {}
public void setGravity(int gravity) {}
public void setDecorFitsSystemWindows(boolean fits) {}
public WindowInsetsController getInsetsController() {
return new InsetsController();
}
public native void set_widget_as_root(long native_window, long widget);
private native void set_title(long native_window, String title);
public native void take_input_queue(long native_window, InputQueue.Callback callback, InputQueue queue);
public native void set_layout(long native_window, int width, int height);
private static native void set_jobject(long ptr, Window obj);
}