com.actionbarsherlock.internal.widget.PopupWindowCompat Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sample Show documentation
Show all versions of sample Show documentation
Android library for better number/date/time-picker DialogFragments.
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();
}
}