com.actionbarsherlock.internal.widget.PopupWindowCompat Maven / Gradle / Ivy
package com.actionbarsherlock.internal.widget;
import java.lang.reflect.Field;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.widget.PopupWindow;
/**
* Works around bugs in the handling of {@link ViewTreeObserver} by
* {@link PopupWindow}.
*
* PopupWindow
registers an {@link OnScrollChangedListener} with
* {@link ViewTreeObserver}, but does not keep a reference to the observer
* instance that it has registers on. This is problematic when the anchor view
* used by PopupWindow
to access the observer is detached from the
* window, as it will revert from the shared ViewTreeObserver
owned
* by the ViewRoot
to a floating one, meaning
* PopupWindow
cannot unregister it's listener anymore and has
* leaked it into the global observer.
*
* This class works around this issue by
*
* - replacing
PopupWindow.mOnScrollChangedListener
with a no-op
* listener so that any registration or unregistration performed by
* PopupWindow
itself has no effect and causes no leaks.
* - registering the real listener only with the shared
*
ViewTreeObserver
and keeping a reference to it to facilitate
* correct unregistration. The reason for not registering on a floating observer
* (before a view is attached) is that there is no safe way to get a reference
* to the shared observer that the floating one will be merged into. This would
* again cause the listener to leak.
*
*/
public class PopupWindowCompat extends PopupWindow {
private static final Field superListenerField;
static {
Field f = null;
try {
f = PopupWindow.class.getDeclaredField("mOnScrollChangedListener");
f.setAccessible(true);
} catch (NoSuchFieldException e) {
/* ignored */
}
superListenerField = f;
}
private static final OnScrollChangedListener NOP = new OnScrollChangedListener() {
@Override
public void onScrollChanged() {
/* do nothing */
}
};
private OnScrollChangedListener mSuperScrollListener;
private ViewTreeObserver mViewTreeObserver;
public PopupWindowCompat() {
super();
init();
}
public PopupWindowCompat(Context context) {
super(context);
init();
}
public PopupWindowCompat(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PopupWindowCompat(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
// @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public PopupWindowCompat(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public PopupWindowCompat(int width, int height) {
super(width, height);
init();
}
public PopupWindowCompat(View contentView) {
super(contentView);
init();
}
public PopupWindowCompat(View contentView, int width, int height, boolean focusable) {
super(contentView, width, height, focusable);
init();
}
public PopupWindowCompat(View contentView, int width, int height) {
super(contentView, width, height);
init();
}
private void init() {
if (superListenerField != null) {
try {
mSuperScrollListener = (OnScrollChangedListener) superListenerField.get(this);
superListenerField.set(this, NOP);
} catch (Exception e) {
mSuperScrollListener = null;
}
}
}
private void unregisterListener() {
// Don't do anything if we haven't managed to patch the super listener
if (mSuperScrollListener != null && mViewTreeObserver != null) {
if (mViewTreeObserver.isAlive()) {
mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener);
}
mViewTreeObserver = null;
}
}
private void registerListener(View anchor) {
// Don't do anything if we haven't managed to patch the super listener.
// And don't bother attaching the listener if the anchor view isn't
// attached. This means we'll only have to deal with the real VTO owned
// by the ViewRoot.
if (mSuperScrollListener != null) {
ViewTreeObserver vto = (anchor.getWindowToken() != null) ? anchor.getViewTreeObserver()
: null;
if (vto != mViewTreeObserver) {
if (mViewTreeObserver != null && mViewTreeObserver.isAlive()) {
mViewTreeObserver.removeOnScrollChangedListener(mSuperScrollListener);
}
if ((mViewTreeObserver = vto) != null) {
vto.addOnScrollChangedListener(mSuperScrollListener);
}
}
}
}
@Override
public void showAsDropDown(View anchor, int xoff, int yoff) {
super.showAsDropDown(anchor, xoff, yoff);
registerListener(anchor);
}
@Override
public void update(View anchor, int xoff, int yoff, int width, int height) {
super.update(anchor, xoff, yoff, width, height);
registerListener(anchor);
}
@Override
public void update(View anchor, int width, int height) {
super.update(anchor, width, height);
registerListener(anchor);
}
@Override
public void showAtLocation(View parent, int gravity, int x, int y) {
super.showAtLocation(parent, gravity, x, y);
unregisterListener();
}
@Override
public void dismiss() {
super.dismiss();
unregisterListener();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy