If you think the Android project EbookExplorer listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
Java Source Code
package com.actionbarsherlock.internal.widget;
/*www.java2s.com*/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}.
* <p>
* <code>PopupWindow</code> 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 <code>PopupWindow</code> to access the observer is detached from the
* window, as it will revert from the shared <code>ViewTreeObserver</code> owned
* by the <code>ViewRoot</code> to a floating one, meaning
* <code>PopupWindow</code> cannot unregister it's listener anymore and has
* leaked it into the global observer.
* <p>
* This class works around this issue by
* <ul>
* <li>replacing <code>PopupWindow.mOnScrollChangedListener</code> with a no-op
* listener so that any registration or unregistration performed by
* <code>PopupWindow</code> itself has no effect and causes no leaks.
* <li>registering the real listener only with the shared
* <code>ViewTreeObserver</code> 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.
* </ul>
*/publicclass PopupWindowCompat extends PopupWindow {
privatestaticfinal Field superListenerField;
static {
Field f = null;
try {
f = PopupWindow.class.getDeclaredField("mOnScrollChangedListener");
f.setAccessible(true);
} catch (NoSuchFieldException e) {
/* ignored */
}
superListenerField = f;
}
privatestaticfinal OnScrollChangedListener NOP = new OnScrollChangedListener() {
@Override
publicvoid 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();
}
privatevoid init() {
if (superListenerField != null) {
try {
mSuperScrollListener = (OnScrollChangedListener) superListenerField.get(this);
superListenerField.set(this, NOP);
} catch (Exception e) {
mSuperScrollListener = null;
}
}
}
privatevoid 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;
}
}
privatevoid 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
publicvoid showAsDropDown(View anchor, int xoff, int yoff) {
super.showAsDropDown(anchor, xoff, yoff);
registerListener(anchor);
}
@Override
publicvoid update(View anchor, int xoff, int yoff, int width, int height) {
super.update(anchor, xoff, yoff, width, height);
registerListener(anchor);
}
@Override
publicvoid update(View anchor, int width, int height) {
super.update(anchor, width, height);
registerListener(anchor);
}
@Override
publicvoid showAtLocation(View parent, int gravity, int x, int y) {
super.showAtLocation(parent, gravity, x, y);
unregisterListener();
}
@Override
publicvoid dismiss() {
super.dismiss();
unregisterListener();
}
}