forked from JakeWharton/ActionBarSherlock
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'popupmenu' of https://github.com/ksperling/ActionBarShe…
…rlock into ksperling-popupmenu
- Loading branch information
Showing
4 changed files
with
395 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
actionbarsherlock/src/com/actionbarsherlock/internal/widget/PopupWindowCompat.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
|
||
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}. | ||
* <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> | ||
*/ | ||
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(); | ||
} | ||
} |
Oops, something went wrong.