diff options
| author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:45 -0800 |
|---|---|---|
| committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:45 -0800 |
| commit | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (patch) | |
| tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /core/java/android/widget/PopupWindow.java | |
| parent | 076357b8567458d4b6dfdcf839ef751634cd2bfb (diff) | |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/java/android/widget/PopupWindow.java')
| -rw-r--r-- | core/java/android/widget/PopupWindow.java | 1218 |
1 files changed, 0 insertions, 1218 deletions
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java deleted file mode 100644 index 4a5cea1cb028..000000000000 --- a/core/java/android/widget/PopupWindow.java +++ /dev/null @@ -1,1218 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.widget; - -import com.android.internal.R; - -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.WindowManager; -import android.view.Gravity; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.ViewTreeObserver.OnScrollChangedListener; -import android.view.View.OnTouchListener; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.IBinder; -import android.content.Context; -import android.content.res.TypedArray; -import android.util.AttributeSet; - -import java.lang.ref.WeakReference; - -/** - * <p>A popup window that can be used to display an arbitrary view. The popup - * windows is a floating container that appears on top of the current - * activity.</p> - * - * @see android.widget.AutoCompleteTextView - * @see android.widget.Spinner - */ -public class PopupWindow { - /** - * Mode for {@link #setInputMethodMode(int): the requirements for the - * input method should be based on the focusability of the popup. That is - * if it is focusable than it needs to work with the input method, else - * it doesn't. - */ - public static final int INPUT_METHOD_FROM_FOCUSABLE = 0; - - /** - * Mode for {@link #setInputMethodMode(int): this popup always needs to - * work with an input method, regardless of whether it is focusable. This - * means that it will always be displayed so that the user can also operate - * the input method while it is shown. - */ - - public static final int INPUT_METHOD_NEEDED = 1; - - /** - * Mode for {@link #setInputMethodMode(int): this popup never needs to - * work with an input method, regardless of whether it is focusable. This - * means that it will always be displayed to use as much space on the - * screen as needed, regardless of whether this covers the input method. - */ - public static final int INPUT_METHOD_NOT_NEEDED = 2; - - private final Context mContext; - private final WindowManager mWindowManager; - - private boolean mIsShowing; - private boolean mIsDropdown; - - private View mContentView; - private View mPopupView; - private boolean mFocusable; - private int mInputMethodMode = INPUT_METHOD_FROM_FOCUSABLE; - private boolean mTouchable = true; - private boolean mOutsideTouchable = false; - private boolean mClippingEnabled = true; - - private OnTouchListener mTouchInterceptor; - - private int mWidthMode; - private int mWidth; - private int mLastWidth; - private int mHeightMode; - private int mHeight; - private int mLastHeight; - - private int mPopupWidth; - private int mPopupHeight; - - private int[] mDrawingLocation = new int[2]; - private int[] mScreenLocation = new int[2]; - private Rect mTempRect = new Rect(); - - private Drawable mBackground; - - private boolean mAboveAnchor; - - private OnDismissListener mOnDismissListener; - private boolean mIgnoreCheekPress = false; - - private int mAnimationStyle = -1; - - private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] { - com.android.internal.R.attr.state_above_anchor - }; - - private WeakReference<View> mAnchor; - private OnScrollChangedListener mOnScrollChangedListener = - new OnScrollChangedListener() { - public void onScrollChanged() { - View anchor = mAnchor.get(); - if (anchor != null && mPopupView != null) { - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); - - mAboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff); - update(p.x, p.y, -1, -1, true); - } - } - }; - private int mAnchorXoff, mAnchorYoff; - - /** - * <p>Create a new empty, non focusable popup window of dimension (0,0).</p> - * - * <p>The popup does provide a background.</p> - */ - public PopupWindow(Context context) { - this(context, null); - } - - /** - * <p>Create a new empty, non focusable popup window of dimension (0,0).</p> - * - * <p>The popup does provide a background.</p> - */ - public PopupWindow(Context context, AttributeSet attrs) { - this(context, attrs, com.android.internal.R.attr.popupWindowStyle); - } - - /** - * <p>Create a new empty, non focusable popup window of dimension (0,0).</p> - * - * <p>The popup does provide a background.</p> - */ - public PopupWindow(Context context, AttributeSet attrs, int defStyle) { - mContext = context; - mWindowManager = (WindowManager)context.getSystemService( - Context.WINDOW_SERVICE); - - TypedArray a = - context.obtainStyledAttributes( - attrs, com.android.internal.R.styleable.PopupWindow, defStyle, 0); - - mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground); - - a.recycle(); - } - - /** - * <p>Create a new empty, non focusable popup window of dimension (0,0).</p> - * - * <p>The popup does not provide any background. This should be handled - * by the content view.</p> - */ - public PopupWindow() { - this(null, 0, 0); - } - - /** - * <p>Create a new non focusable popup window which can display the - * <tt>contentView</tt>. The dimension of the window are (0,0).</p> - * - * <p>The popup does not provide any background. This should be handled - * by the content view.</p> - * - * @param contentView the popup's content - */ - public PopupWindow(View contentView) { - this(contentView, 0, 0); - } - - /** - * <p>Create a new empty, non focusable popup window. The dimension of the - * window must be passed to this constructor.</p> - * - * <p>The popup does not provide any background. This should be handled - * by the content view.</p> - * - * @param width the popup's width - * @param height the popup's height - */ - public PopupWindow(int width, int height) { - this(null, width, height); - } - - /** - * <p>Create a new non focusable popup window which can display the - * <tt>contentView</tt>. The dimension of the window must be passed to - * this constructor.</p> - * - * <p>The popup does not provide any background. This should be handled - * by the content view.</p> - * - * @param contentView the popup's content - * @param width the popup's width - * @param height the popup's height - */ - public PopupWindow(View contentView, int width, int height) { - this(contentView, width, height, false); - } - - /** - * <p>Create a new popup window which can display the <tt>contentView</tt>. - * The dimension of the window must be passed to this constructor.</p> - * - * <p>The popup does not provide any background. This should be handled - * by the content view.</p> - * - * @param contentView the popup's content - * @param width the popup's width - * @param height the popup's height - * @param focusable true if the popup can be focused, false otherwise - */ - public PopupWindow(View contentView, int width, int height, - boolean focusable) { - mContext = contentView.getContext(); - mWindowManager = (WindowManager)mContext.getSystemService( - Context.WINDOW_SERVICE); - setContentView(contentView); - setWidth(width); - setHeight(height); - setFocusable(focusable); - } - - /** - * <p>Return the drawable used as the popup window's background.</p> - * - * @return the background drawable or null - */ - public Drawable getBackground() { - return mBackground; - } - - /** - * <p>Change the background drawable for this popup window. The background - * can be set to null.</p> - * - * @param background the popup's background - */ - public void setBackgroundDrawable(Drawable background) { - mBackground = background; - } - - /** - * <p>Return the animation style to use the popup appears and disappears</p> - * - * @return the animation style to use the popup appears and disappears - */ - public int getAnimationStyle() { - return mAnimationStyle; - } - - /** - * Set the flag on popup to ignore cheek press eventt; by default this flag - * is set to false - * which means the pop wont ignore cheek press dispatch events. - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown or through a manual call to one of - * the {@link #update()} methods.</p> - * - * @see #update() - */ - public void setIgnoreCheekPress() { - mIgnoreCheekPress = true; - } - - - /** - * <p>Change the animation style resource for this popup.</p> - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown or through a manual call to one of - * the {@link #update()} methods.</p> - * - * @param animationStyle animation style to use when the popup appears - * and disappears. Set to -1 for the default animation, 0 for no - * animation, or a resource identifier for an explicit animation. - * - * @see #update() - */ - public void setAnimationStyle(int animationStyle) { - mAnimationStyle = animationStyle; - } - - /** - * <p>Return the view used as the content of the popup window.</p> - * - * @return a {@link android.view.View} representing the popup's content - * - * @see #setContentView(android.view.View) - */ - public View getContentView() { - return mContentView; - } - - /** - * <p>Change the popup's content. The content is represented by an instance - * of {@link android.view.View}.</p> - * - * <p>This method has no effect if called when the popup is showing. To - * apply it while a popup is showing, call </p> - * - * @param contentView the new content for the popup - * - * @see #getContentView() - * @see #isShowing() - */ - public void setContentView(View contentView) { - if (isShowing()) { - return; - } - - mContentView = contentView; - } - - /** - * Set a callback for all touch events being dispatched to the popup - * window. - */ - public void setTouchInterceptor(OnTouchListener l) { - mTouchInterceptor = l; - } - - /** - * <p>Indicate whether the popup window can grab the focus.</p> - * - * @return true if the popup is focusable, false otherwise - * - * @see #setFocusable(boolean) - */ - public boolean isFocusable() { - return mFocusable; - } - - /** - * <p>Changes the focusability of the popup window. When focusable, the - * window will grab the focus from the current focused widget if the popup - * contains a focusable {@link android.view.View}. By default a popup - * window is not focusable.</p> - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown or through a manual call to one of - * the {@link #update()} methods.</p> - * - * @param focusable true if the popup should grab focus, false otherwise. - * - * @see #isFocusable() - * @see #isShowing() - * @see #update() - */ - public void setFocusable(boolean focusable) { - mFocusable = focusable; - } - - /** - * Return the current value in {@link #setInputMethodMode(int)}. - * - * @see #setInputMethodMode(int) - */ - public int getInputMethodMode() { - return mInputMethodMode; - - } - - /** - * Control how the popup operates with an input method: one of - * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED}, - * or {@link #INPUT_METHOD_NOT_NEEDED}. - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown or through a manual call to one of - * the {@link #update()} methods.</p> - * - * @see #getInputMethodMode() - * @see #update() - */ - public void setInputMethodMode(int mode) { - mInputMethodMode = mode; - } - - /** - * <p>Indicates whether the popup window receives touch events.</p> - * - * @return true if the popup is touchable, false otherwise - * - * @see #setTouchable(boolean) - */ - public boolean isTouchable() { - return mTouchable; - } - - /** - * <p>Changes the touchability of the popup window. When touchable, the - * window will receive touch events, otherwise touch events will go to the - * window below it. By default the window is touchable.</p> - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown or through a manual call to one of - * the {@link #update()} methods.</p> - * - * @param touchable true if the popup should receive touch events, false otherwise - * - * @see #isTouchable() - * @see #isShowing() - * @see #update() - */ - public void setTouchable(boolean touchable) { - mTouchable = touchable; - } - - /** - * <p>Indicates whether the popup window will be informed of touch events - * outside of its window.</p> - * - * @return true if the popup is outside touchable, false otherwise - * - * @see #setOutsideTouchable(boolean) - */ - public boolean isOutsideTouchable() { - return mOutsideTouchable; - } - - /** - * <p>Controls whether the pop-up will be informed of touch events outside - * of its window. This only makes sense for pop-ups that are touchable - * but not focusable, which means touches outside of the window will - * be delivered to the window behind. The default is false.</p> - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown or through a manual call to one of - * the {@link #update()} methods.</p> - * - * @param touchable true if the popup should receive outside - * touch events, false otherwise - * - * @see #isOutsideTouchable() - * @see #isShowing() - * @see #update() - */ - public void setOutsideTouchable(boolean touchable) { - mOutsideTouchable = touchable; - } - - /** - * <p>Indicates whether clipping of the popup window is enabled.</p> - * - * @return true if the clipping is enabled, false otherwise - * - * @see #setClippingEnabled(boolean) - */ - public boolean isClippingEnabled() { - return mClippingEnabled; - } - - /** - * <p>Allows the popup window to extend beyond the bounds of the screen. By default the - * window is clipped to the screen boundaries. Setting this to false will allow windows to be - * accurately positioned.</p> - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown or through a manual call to one of - * the {@link #update()} methods.</p> - * - * @param enabled false if the window should be allowed to extend outside of the screen - * @see #isShowing() - * @see #isClippingEnabled() - * @see #update() - */ - public void setClippingEnabled(boolean enabled) { - mClippingEnabled = enabled; - } - - /** - * <p>Change the width and height measure specs that are given to the - * window manager by the popup. By default these are 0, meaning that - * the current width or height is requested as an explicit size from - * the window manager. You can supply - * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or - * {@link ViewGroup.LayoutParams#FILL_PARENT} to have that measure - * spec supplied instead, replacing the absolute width and height that - * has been set in the popup.</p> - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown.</p> - * - * @param widthSpec an explicit width measure spec mode, either - * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, - * {@link ViewGroup.LayoutParams#FILL_PARENT}, or 0 to use the absolute - * width. - * @param heightSpec an explicit height measure spec mode, either - * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, - * {@link ViewGroup.LayoutParams#FILL_PARENT}, or 0 to use the absolute - * height. - */ - public void setWindowLayoutMode(int widthSpec, int heightSpec) { - mWidthMode = widthSpec; - mHeightMode = heightSpec; - } - - /** - * <p>Return this popup's height MeasureSpec</p> - * - * @return the height MeasureSpec of the popup - * - * @see #setHeight(int) - */ - public int getHeight() { - return mHeight; - } - - /** - * <p>Change the popup's height MeasureSpec</p> - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown.</p> - * - * @param height the height MeasureSpec of the popup - * - * @see #getHeight() - * @see #isShowing() - */ - public void setHeight(int height) { - mHeight = height; - } - - /** - * <p>Return this popup's width MeasureSpec</p> - * - * @return the width MeasureSpec of the popup - * - * @see #setWidth(int) - */ - public int getWidth() { - return mWidth; - } - - /** - * <p>Change the popup's width MeasureSpec</p> - * - * <p>If the popup is showing, calling this method will take effect only - * the next time the popup is shown.</p> - * - * @param width the width MeasureSpec of the popup - * - * @see #getWidth() - * @see #isShowing() - */ - public void setWidth(int width) { - mWidth = width; - } - - /** - * <p>Indicate whether this popup window is showing on screen.</p> - * - * @return true if the popup is showing, false otherwise - */ - public boolean isShowing() { - return mIsShowing; - } - - /** - * <p> - * Display the content view in a popup window at the specified location. If the popup window - * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams} - * for more information on how gravity and the x and y parameters are related. Specifying - * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying - * <code>Gravity.LEFT | Gravity.TOP</code>. - * </p> - * - * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from - * @param gravity the gravity which controls the placement of the popup window - * @param x the popup's x location offset - * @param y the popup's y location offset - */ - public void showAtLocation(View parent, int gravity, int x, int y) { - if (isShowing() || mContentView == null) { - return; - } - - unregisterForScrollChanged(); - - mIsShowing = true; - mIsDropdown = false; - - WindowManager.LayoutParams p = createPopupLayout(parent.getWindowToken()); - p.windowAnimations = computeAnimationResource(); - - preparePopup(p); - if (gravity == Gravity.NO_GRAVITY) { - gravity = Gravity.TOP | Gravity.LEFT; - } - p.gravity = gravity; - p.x = x; - p.y = y; - invokePopup(p); - } - - /** - * <p>Display the content view in a popup window anchored to the bottom-left - * corner of the anchor view. If there is not enough room on screen to show - * the popup in its entirety, this method tries to find a parent scroll - * view to scroll. If no parent scroll view can be scrolled, the bottom-left - * corner of the popup is pinned at the top left corner of the anchor view.</p> - * - * @param anchor the view on which to pin the popup window - * - * @see #dismiss() - */ - public void showAsDropDown(View anchor) { - showAsDropDown(anchor, 0, 0); - } - - /** - * <p>Display the content view in a popup window anchored to the bottom-left - * corner of the anchor view offset by the specified x and y coordinates. - * If there is not enough room on screen to show - * the popup in its entirety, this method tries to find a parent scroll - * view to scroll. If no parent scroll view can be scrolled, the bottom-left - * corner of the popup is pinned at the top left corner of the anchor view.</p> - * <p>If the view later scrolls to move <code>anchor</code> to a different - * location, the popup will be moved correspondingly.</p> - * - * @param anchor the view on which to pin the popup window - * - * @see #dismiss() - */ - public void showAsDropDown(View anchor, int xoff, int yoff) { - if (isShowing() || mContentView == null) { - return; - } - - registerForScrollChanged(anchor, xoff, yoff); - - mIsShowing = true; - mIsDropdown = true; - - WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken()); - preparePopup(p); - mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff); - - if (mBackground != null) { - mPopupView.refreshDrawableState(); - } - - if (mHeightMode < 0) p.height = mLastHeight = mHeightMode; - if (mWidthMode < 0) p.width = mLastWidth = mWidthMode; - - p.windowAnimations = computeAnimationResource(); - - invokePopup(p); - } - - /** - * Indicates whether the popup is showing above (the y coordinate of the popup's bottom - * is less than the y coordinate of the anchor) or below the anchor view (the y coordinate - * of the popup is greater than y coordinate of the anchor's bottom). - * - * The value returned - * by this method is meaningful only after {@link #showAsDropDown(android.view.View)} - * or {@link #showAsDropDown(android.view.View, int, int)} was invoked. - * - * @return True if this popup is showing above the anchor view, false otherwise. - */ - public boolean isAboveAnchor() { - return mAboveAnchor; - } - - /** - * <p>Prepare the popup by embedding in into a new ViewGroup if the - * background drawable is not null. If embedding is required, the layout - * parameters' height is mnodified to take into account the background's - * padding.</p> - * - * @param p the layout parameters of the popup's content view - */ - private void preparePopup(WindowManager.LayoutParams p) { - if (mBackground != null) { - // when a background is available, we embed the content view - // within another view that owns the background drawable - PopupViewContainer popupViewContainer = new PopupViewContainer(mContext); - PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT - ); - popupViewContainer.setBackgroundDrawable(mBackground); - popupViewContainer.addView(mContentView, listParams); - - mPopupView = popupViewContainer; - } else { - mPopupView = mContentView; - } - mPopupWidth = p.width; - mPopupHeight = p.height; - } - - /** - * <p>Invoke the popup window by adding the content view to the window - * manager.</p> - * - * <p>The content view must be non-null when this method is invoked.</p> - * - * @param p the layout parameters of the popup's content view - */ - private void invokePopup(WindowManager.LayoutParams p) { - mWindowManager.addView(mPopupView, p); - } - - /** - * <p>Generate the layout parameters for the popup window.</p> - * - * @param token the window token used to bind the popup's window - * - * @return the layout parameters to pass to the window manager - */ - private WindowManager.LayoutParams createPopupLayout(IBinder token) { - // generates the layout parameters for the drop down - // we want a fixed size view located at the bottom left of the anchor - WindowManager.LayoutParams p = new WindowManager.LayoutParams(); - // these gravity settings put the view at the top left corner of the - // screen. The view is then positioned to the appropriate location - // by setting the x and y offsets to match the anchor's bottom - // left corner - p.gravity = Gravity.LEFT | Gravity.TOP; - p.width = mLastWidth = mWidth; - p.height = mLastHeight = mHeight; - if (mBackground != null) { - p.format = mBackground.getOpacity(); - } else { - p.format = PixelFormat.TRANSLUCENT; - } - p.flags = computeFlags(p.flags); - p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; - p.token = token; - p.setTitle("PopupWindow:" + Integer.toHexString(hashCode())); - - return p; - } - - private int computeFlags(int curFlags) { - curFlags &= ~( - WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES | - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | - WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | - WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | - WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); - if(mIgnoreCheekPress) { - curFlags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; - } - if (!mFocusable) { - curFlags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - if (mInputMethodMode == INPUT_METHOD_NEEDED) { - curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - } - } else if (mInputMethodMode == INPUT_METHOD_NOT_NEEDED) { - curFlags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - } - if (!mTouchable) { - curFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - } - if (mTouchable) { - curFlags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; - } - if (!mClippingEnabled) { - curFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; - } - return curFlags; - } - - private int computeAnimationResource() { - if (mAnimationStyle == -1) { - if (mIsDropdown) { - return mAboveAnchor - ? com.android.internal.R.style.Animation_DropDownUp - : com.android.internal.R.style.Animation_DropDownDown; - } - return 0; - } - return mAnimationStyle; - } - - /** - * <p>Positions the popup window on screen. When the popup window is too - * tall to fit under the anchor, a parent scroll view is seeked and scrolled - * up to reclaim space. If scrolling is not possible or not enough, the - * popup window gets moved on top of the anchor.</p> - * - * <p>The height must have been set on the layout parameters prior to - * calling this method.</p> - * - * @param anchor the view on which the popup window must be anchored - * @param p the layout parameters used to display the drop down - * - * @return true if the popup is translated upwards to fit on screen - */ - private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, - int xoff, int yoff) { - - anchor.getLocationInWindow(mDrawingLocation); - p.x = mDrawingLocation[0] + xoff; - p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; - - boolean onTop = false; - - p.gravity = Gravity.LEFT | Gravity.TOP; - - anchor.getLocationOnScreen(mScreenLocation); - final Rect displayFrame = new Rect(); - anchor.getWindowVisibleDisplayFrame(displayFrame); - - final View root = anchor.getRootView(); - if (p.y + mPopupHeight > displayFrame.bottom || p.x + mPopupWidth - root.getWidth() > 0) { - // if the drop down disappears at the bottom of the screen. we try to - // scroll a parent scrollview or move the drop down back up on top of - // the edit box - int scrollX = anchor.getScrollX(); - int scrollY = anchor.getScrollY(); - Rect r = new Rect(scrollX, scrollY, scrollX + mPopupWidth, - scrollY + mPopupHeight + anchor.getMeasuredHeight()); - anchor.requestRectangleOnScreen(r, true); - - // now we re-evaluate the space available, and decide from that - // whether the pop-up will go above or below the anchor. - anchor.getLocationInWindow(mDrawingLocation); - p.x = mDrawingLocation[0] + xoff; - p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; - - // determine whether there is more space above or below the anchor - anchor.getLocationOnScreen(mScreenLocation); - - onTop = (displayFrame.bottom - mScreenLocation[1] - anchor.getMeasuredHeight() - yoff) < - (mScreenLocation[1] - yoff - displayFrame.top); - if (onTop) { - p.gravity = Gravity.LEFT | Gravity.BOTTOM; - p.y = root.getHeight() - mDrawingLocation[1] + yoff; - } else { - p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; - } - } - - p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL; - - return onTop; - } - - /** - * Returns the maximum height that is available for the popup to be - * completely shown. It is recommended that this height be the maximum for - * the popup's height, otherwise it is possible that the popup will be - * clipped. - * - * @param anchor The view on which the popup window must be anchored. - * @return The maximum available height for the popup to be completely - * shown. - */ - public int getMaxAvailableHeight(View anchor) { - return getMaxAvailableHeight(anchor, 0); - } - - /** - * Returns the maximum height that is available for the popup to be - * completely shown. It is recommended that this height be the maximum for - * the popup's height, otherwise it is possible that the popup will be - * clipped. - * - * @param anchor The view on which the popup window must be anchored. - * @param yOffset y offset from the view's bottom edge - * @return The maximum available height for the popup to be completely - * shown. - */ - public int getMaxAvailableHeight(View anchor, int yOffset) { - final Rect displayFrame = new Rect(); - anchor.getWindowVisibleDisplayFrame(displayFrame); - - final int[] anchorPos = mDrawingLocation; - anchor.getLocationOnScreen(anchorPos); - - final int distanceToBottom = displayFrame.bottom - - (anchorPos[1] + anchor.getHeight()) - yOffset; - final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset; - - // anchorPos[1] is distance from anchor to top of screen - int returnedHeight = Math.max(distanceToBottom, distanceToTop); - if (mBackground != null) { - mBackground.getPadding(mTempRect); - returnedHeight -= mTempRect.top + mTempRect.bottom; - } - - return returnedHeight; - } - - /** - * <p>Dispose of the popup window. This method can be invoked only after - * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling - * this method will have no effect.</p> - * - * @see #showAsDropDown(android.view.View) - */ - public void dismiss() { - if (isShowing() && mPopupView != null) { - unregisterForScrollChanged(); - - mWindowManager.removeView(mPopupView); - if (mPopupView != mContentView && mPopupView instanceof ViewGroup) { - ((ViewGroup) mPopupView).removeView(mContentView); - } - mPopupView = null; - mIsShowing = false; - - if (mOnDismissListener != null) { - mOnDismissListener.onDismiss(); - } - } - } - - /** - * Sets the listener to be called when the window is dismissed. - * - * @param onDismissListener The listener. - */ - public void setOnDismissListener(OnDismissListener onDismissListener) { - mOnDismissListener = onDismissListener; - } - - /** - * Updates the state of the popup window, if it is currently being displayed, - * from the currently set state. This include: - * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)}, - * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)}, - * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}. - */ - public void update() { - if (!isShowing() || mContentView == null) { - return; - } - - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); - - boolean update = false; - - final int newAnim = computeAnimationResource(); - if (newAnim != p.windowAnimations) { - p.windowAnimations = newAnim; - update = true; - } - - final int newFlags = computeFlags(p.flags); - if (newFlags != p.flags) { - p.flags = newFlags; - update = true; - } - - if (update) { - mWindowManager.updateViewLayout(mPopupView, p); - } - } - - /** - * <p>Updates the position and the dimension of the popup window. Width and - * height can be set to -1 to update location only. Calling this function - * also updates the window with the current popup state as - * described for {@link #update()}.</p> - * - * @param x the new x location - * @param y the new y location - * @param width the new width, can be -1 to ignore - * @param height the new height, can be -1 to ignore - */ - public void update(int x, int y, int width, int height) { - update(x, y, width, height, false); - } - - /** - * <p>Updates the position and the dimension of the popup window. Width and - * height can be set to -1 to update location only. Calling this function - * also updates the window with the current popup state as - * described for {@link #update()}.</p> - * - * @param x the new x location - * @param y the new y location - * @param width the new width, can be -1 to ignore - * @param height the new height, can be -1 to ignore - * @param force reposition the window even if the specified position - * already seems to correspond to the LayoutParams - * - * @hide pending API council approval - */ - public void update(int x, int y, int width, int height, boolean force) { - if (width != -1) { - mLastWidth = width; - setWidth(width); - } - - if (height != -1) { - mLastHeight = height; - setHeight(height); - } - - if (!isShowing() || mContentView == null) { - return; - } - - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); - - boolean update = force; - - final int finalWidth = mWidthMode < 0 ? mWidthMode : mLastWidth; - if (width != -1 && p.width != finalWidth) { - p.width = mLastWidth = finalWidth; - update = true; - } - - final int finalHeight = mHeightMode < 0 ? mHeightMode : mLastHeight; - if (height != -1 && p.height != finalHeight) { - p.height = mLastHeight = finalHeight; - update = true; - } - - if (p.x != x) { - p.x = x; - update = true; - } - - if (p.y != y) { - p.y = y; - update = true; - } - - final int newAnim = computeAnimationResource(); - if (newAnim != p.windowAnimations) { - p.windowAnimations = newAnim; - update = true; - } - - final int newFlags = computeFlags(p.flags); - if (newFlags != p.flags) { - p.flags = newFlags; - update = true; - } - - if (update) { - mWindowManager.updateViewLayout(mPopupView, p); - } - } - - /** - * <p>Updates the position and the dimension of the popup window. Width and - * height can be set to -1 to update location only. Calling this function - * also updates the window with the current popup state as - * described for {@link #update()}.</p> - * - * @param anchor the popup's anchor view - * @param width the new width, can be -1 to ignore - * @param height the new height, can be -1 to ignore - */ - public void update(View anchor, int width, int height) { - update(anchor, 0, 0, width, height); - } - - /** - * <p>Updates the position and the dimension of the popup window. Width and - * height can be set to -1 to update location only. Calling this function - * also updates the window with the current popup state as - * described for {@link #update()}.</p> - * <p>If the view later scrolls to move <code>anchor</code> to a different - * location, the popup will be moved correspondingly.</p> - * - * @param anchor the popup's anchor view - * @param xoff x offset from the view's left edge - * @param yoff y offset from the view's bottom edge - * @param width the new width, can be -1 to ignore - * @param height the new height, can be -1 to ignore - */ - public void update(View anchor, int xoff, int yoff, int width, int height) { - if (!isShowing() || mContentView == null) { - return; - } - - WeakReference<View> oldAnchor = mAnchor; - if (oldAnchor == null || oldAnchor.get() != anchor || - mAnchorXoff != xoff || mAnchorYoff != yoff) { - registerForScrollChanged(anchor, xoff, yoff); - } - - WindowManager.LayoutParams p = (WindowManager.LayoutParams) - mPopupView.getLayoutParams(); - - if (width == -1) { - width = mPopupWidth; - } else { - mPopupWidth = width; - } - if (height == -1) { - height = mPopupHeight; - } else { - mPopupHeight = height; - } - - mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff); - update(p.x, p.y, width, height); - } - - /** - * Listener that is called when this popup window is dismissed. - */ - public interface OnDismissListener { - /** - * Called when this popup window is dismissed. - */ - public void onDismiss(); - } - - private void unregisterForScrollChanged() { - WeakReference<View> anchorRef = mAnchor; - View anchor = null; - if (anchorRef != null) { - anchor = anchorRef.get(); - } - if (anchor != null) { - ViewTreeObserver vto = anchor.getViewTreeObserver(); - vto.removeOnScrollChangedListener(mOnScrollChangedListener); - } - mAnchor = null; - } - - private void registerForScrollChanged(View anchor, int xoff, int yoff) { - unregisterForScrollChanged(); - - mAnchor = new WeakReference<View>(anchor); - ViewTreeObserver vto = anchor.getViewTreeObserver(); - if (vto != null) { - vto.addOnScrollChangedListener(mOnScrollChangedListener); - } - - mAnchorXoff = xoff; - mAnchorYoff = yoff; - } - - private class PopupViewContainer extends FrameLayout { - - public PopupViewContainer(Context context) { - super(context); - } - - @Override - protected int[] onCreateDrawableState(int extraSpace) { - if (mAboveAnchor) { - // 1 more needed for the above anchor state - final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); - View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET); - return drawableState; - } else { - return super.onCreateDrawableState(extraSpace); - } - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - dismiss(); - return true; - } else { - return super.dispatchKeyEvent(event); - } - } - - @Override - public boolean dispatchTouchEvent(MotionEvent ev) { - if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) { - return true; - } - return super.dispatchTouchEvent(ev); - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - final int x = (int) event.getX(); - final int y = (int) event.getY(); - - if ((event.getAction() == MotionEvent.ACTION_DOWN) - && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) { - dismiss(); - return true; - } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { - dismiss(); - return true; - } else { - return super.onTouchEvent(event); - } - } - - } - -} |
