All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.actionbarsherlock.internal.widget.PopupWindowCompat Maven / Gradle / Ivy

There is a newer version: 1.6.0
Show newest version

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 - 2024 Weber Informatics LLC | Privacy Policy