PopupWindow.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.widget; 18 19import com.android.internal.R; 20 21import android.view.KeyEvent; 22import android.view.MotionEvent; 23import android.view.View; 24import android.view.WindowManager; 25import android.view.WindowManagerImpl; 26import android.view.Gravity; 27import android.view.ViewGroup; 28import android.graphics.PixelFormat; 29import android.graphics.Rect; 30import android.graphics.drawable.Drawable; 31import android.os.IBinder; 32import android.content.Context; 33import android.content.res.TypedArray; 34import android.util.AttributeSet; 35 36/** 37 * <p>A popup window that can be used to display an arbitrary view. The popup 38 * windows is a floating container that appears on top of the current 39 * activity.</p> 40 * 41 * @see android.widget.AutoCompleteTextView 42 * @see android.widget.Spinner 43 */ 44public class PopupWindow { 45 /** 46 * The height of the status bar so we know how much of the screen we can 47 * actually be displayed in. 48 * <p> 49 * TODO: This IS NOT the right way to do this. 50 * Instead of knowing how much of the screen is available, a popup that 51 * wants anchor and maximize space shouldn't be setting a height, instead 52 * the PopupViewContainer should have its layout height as fill_parent and 53 * properly position the popup. 54 */ 55 private static final int STATUS_BAR_HEIGHT = 30; 56 57 private boolean mIsShowing; 58 59 private View mContentView; 60 private View mPopupView; 61 private boolean mFocusable; 62 63 private int mWidth; 64 private int mHeight; 65 66 private int[] mDrawingLocation = new int[2]; 67 private int[] mRootLocation = new int[2]; 68 private Rect mTempRect = new Rect(); 69 70 private Context mContext; 71 private Drawable mBackground; 72 73 private boolean mAboveAnchor; 74 75 private OnDismissListener mOnDismissListener; 76 private boolean mIgnoreCheekPress = false; 77 78 private int mAnimationStyle = -1; 79 80 private static final int[] ABOVE_ANCHOR_STATE_SET = new int[] { 81 com.android.internal.R.attr.state_above_anchor 82 }; 83 84 /** 85 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p> 86 * 87 * <p>The popup does provide a background.</p> 88 */ 89 public PopupWindow(Context context) { 90 this(context, null); 91 } 92 93 /** 94 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p> 95 * 96 * <p>The popup does provide a background.</p> 97 */ 98 public PopupWindow(Context context, AttributeSet attrs) { 99 this(context, attrs, com.android.internal.R.attr.popupWindowStyle); 100 } 101 102 /** 103 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p> 104 * 105 * <p>The popup does provide a background.</p> 106 */ 107 public PopupWindow(Context context, AttributeSet attrs, int defStyle) { 108 mContext = context; 109 110 TypedArray a = 111 context.obtainStyledAttributes( 112 attrs, com.android.internal.R.styleable.PopupWindow, defStyle, 0); 113 114 mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground); 115 116 a.recycle(); 117 } 118 119 /** 120 * <p>Create a new empty, non focusable popup window of dimension (0,0).</p> 121 * 122 * <p>The popup does not provide any background. This should be handled 123 * by the content view.</p> 124 */ 125 public PopupWindow() { 126 this(null, 0, 0); 127 } 128 129 /** 130 * <p>Create a new non focusable popup window which can display the 131 * <tt>contentView</tt>. The dimension of the window are (0,0).</p> 132 * 133 * <p>The popup does not provide any background. This should be handled 134 * by the content view.</p> 135 * 136 * @param contentView the popup's content 137 */ 138 public PopupWindow(View contentView) { 139 this(contentView, 0, 0); 140 } 141 142 /** 143 * <p>Create a new empty, non focusable popup window. The dimension of the 144 * window must be passed to this constructor.</p> 145 * 146 * <p>The popup does not provide any background. This should be handled 147 * by the content view.</p> 148 * 149 * @param width the popup's width 150 * @param height the popup's height 151 */ 152 public PopupWindow(int width, int height) { 153 this(null, width, height); 154 } 155 156 /** 157 * <p>Create a new non focusable popup window which can display the 158 * <tt>contentView</tt>. The dimension of the window must be passed to 159 * this constructor.</p> 160 * 161 * <p>The popup does not provide any background. This should be handled 162 * by the content view.</p> 163 * 164 * @param contentView the popup's content 165 * @param width the popup's width 166 * @param height the popup's height 167 */ 168 public PopupWindow(View contentView, int width, int height) { 169 this(contentView, width, height, false); 170 } 171 172 /** 173 * <p>Create a new popup window which can display the <tt>contentView</tt>. 174 * The dimension of the window must be passed to this constructor.</p> 175 * 176 * <p>The popup does not provide any background. This should be handled 177 * by the content view.</p> 178 * 179 * @param contentView the popup's content 180 * @param width the popup's width 181 * @param height the popup's height 182 * @param focusable true if the popup can be focused, false otherwise 183 */ 184 public PopupWindow(View contentView, int width, int height, 185 boolean focusable) { 186 setContentView(contentView); 187 setWidth(width); 188 setHeight(height); 189 setFocusable(focusable); 190 } 191 192 /** 193 * <p>Return the drawable used as the popup window's background.</p> 194 * 195 * @return the background drawable or null 196 */ 197 public Drawable getBackground() { 198 return mBackground; 199 } 200 201 /** 202 * <p>Change the background drawable for this popup window. The background 203 * can be set to null.</p> 204 * 205 * @param background the popup's background 206 */ 207 public void setBackgroundDrawable(Drawable background) { 208 mBackground = background; 209 } 210 211 /** 212 * <p>Return the animation style to use the popup appears and disappears</p> 213 * 214 * @return the animation style to use the popup appears and disappears 215 */ 216 public int getAnimationStyle() { 217 return mAnimationStyle; 218 } 219 220 /** 221 * set the flag on popup to ignore cheek press events 222 * This method has to be invoked before displaying the content view 223 * of the popup for the window flags to take effect and will be ignored 224 * if the pop up is already displayed. By default this flag is set to false 225 * which means the pop wont ignore cheek press dispatch events. 226 */ 227 public void setIgnoreCheekPress() { 228 mIgnoreCheekPress = true; 229 } 230 231 232 /** 233 * <p>Change the animation style for this popup.</p> 234 * 235 * @param animationStyle animation style to use when the popup appears and disappears 236 */ 237 public void setAnimationStyle(int animationStyle) { 238 mAnimationStyle = animationStyle; 239 } 240 241 /** 242 * <p>Return the view used as the content of the popup window.</p> 243 * 244 * @return a {@link android.view.View} representing the popup's content 245 * 246 * @see #setContentView(android.view.View) 247 */ 248 public View getContentView() { 249 return mContentView; 250 } 251 252 /** 253 * <p>Change the popup's content. The content is represented by an instance 254 * of {@link android.view.View}.</p> 255 * 256 * <p>This method has no effect if called when the popup is showing.</p> 257 * 258 * @param contentView the new content for the popup 259 * 260 * @see #getContentView() 261 * @see #isShowing() 262 */ 263 public void setContentView(View contentView) { 264 if (isShowing()) { 265 return; 266 } 267 268 mContentView = contentView; 269 } 270 271 /** 272 * <p>Indicate whether the popup window can grab the focus.</p> 273 * 274 * @return true if the popup is focusable, false otherwise 275 * 276 * @see #setFocusable(boolean) 277 */ 278 public boolean isFocusable() { 279 return mFocusable; 280 } 281 282 /** 283 * <p>Changes the focusability of the popup window. When focusable, the 284 * window will grab the focus from the current focused widget if the popup 285 * contains a focusable {@link android.view.View}.</p> 286 * 287 * <p>If the popup is showing, calling this method will take effect only 288 * the next time the popup is shown.</p> 289 * 290 * @param focusable true if the popup should grab focus, false otherwise 291 * 292 * @see #isFocusable() 293 * @see #isShowing() 294 */ 295 public void setFocusable(boolean focusable) { 296 mFocusable = focusable; 297 } 298 299 /** 300 * <p>Return this popup's height MeasureSpec</p> 301 * 302 * @return the height MeasureSpec of the popup 303 * 304 * @see #setHeight(int) 305 */ 306 public int getHeight() { 307 return mHeight; 308 } 309 310 /** 311 * <p>Change the popup's height MeasureSpec</p> 312 * 313 * <p>If the popup is showing, calling this method will take effect only 314 * the next time the popup is shown.</p> 315 * 316 * @param height the height MeasureSpec of the popup 317 * 318 * @see #getHeight() 319 * @see #isShowing() 320 */ 321 public void setHeight(int height) { 322 mHeight = height; 323 } 324 325 /** 326 * <p>Return this popup's width MeasureSpec</p> 327 * 328 * @return the width MeasureSpec of the popup 329 * 330 * @see #setWidth(int) 331 */ 332 public int getWidth() { 333 return mWidth; 334 } 335 336 /** 337 * <p>Change the popup's width MeasureSpec</p> 338 * 339 * <p>If the popup is showing, calling this method will take effect only 340 * the next time the popup is shown.</p> 341 * 342 * @param width the width MeasureSpec of the popup 343 * 344 * @see #getWidth() 345 * @see #isShowing() 346 */ 347 public void setWidth(int width) { 348 mWidth = width; 349 } 350 351 /** 352 * <p>Indicate whether this popup window is showing on screen.</p> 353 * 354 * @return true if the popup is showing, false otherwise 355 */ 356 public boolean isShowing() { 357 return mIsShowing; 358 } 359 360 /** 361 * <p> 362 * Display the content view in a popup window at the specified location. If the popup window 363 * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams} 364 * for more information on how gravity and the x and y parameters are related. Specifying 365 * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying 366 * <code>Gravity.LEFT | Gravity.TOP</code>. 367 * </p> 368 * 369 * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from 370 * @param gravity the gravity which controls the placement of the popup window 371 * @param x the popup's x location offset 372 * @param y the popup's y location offset 373 */ 374 public void showAtLocation(View parent, int gravity, int x, int y) { 375 if (isShowing() || mContentView == null) { 376 return; 377 } 378 379 mIsShowing = true; 380 381 WindowManager.LayoutParams p = createPopupLayout(parent.getWindowToken()); 382 if (mAnimationStyle != -1) { 383 p.windowAnimations = mAnimationStyle; 384 } 385 386 preparePopup(p); 387 if (gravity == Gravity.NO_GRAVITY) { 388 gravity = Gravity.TOP | Gravity.LEFT; 389 } 390 p.gravity = gravity; 391 p.x = x; 392 p.y = y; 393 invokePopup(p); 394 } 395 396 /** 397 * <p>Display the content view in a popup window anchored to the bottom-left 398 * corner of the anchor view. If there is not enough room on screen to show 399 * the popup in its entirety, this method tries to find a parent scroll 400 * view to scroll. If no parent scroll view can be scrolled, the bottom-left 401 * corner of the popup is pinned at the top left corner of the anchor view.</p> 402 * 403 * @param anchor the view on which to pin the popup window 404 * 405 * @see #dismiss() 406 */ 407 public void showAsDropDown(View anchor) { 408 showAsDropDown(anchor, 0, 0); 409 } 410 411 /** 412 * <p>Display the content view in a popup window anchored to the bottom-left 413 * corner of the anchor view offset by the specified x and y coordinates. 414 * If there is not enough room on screen to show 415 * the popup in its entirety, this method tries to find a parent scroll 416 * view to scroll. If no parent scroll view can be scrolled, the bottom-left 417 * corner of the popup is pinned at the top left corner of the anchor view.</p> 418 * 419 * @param anchor the view on which to pin the popup window 420 * 421 * @see #dismiss() 422 */ 423 public void showAsDropDown(View anchor, int xoff, int yoff) { 424 if (isShowing() || mContentView == null) { 425 return; 426 } 427 428 mIsShowing = true; 429 430 WindowManager.LayoutParams p = createPopupLayout(anchor.getWindowToken()); 431 preparePopup(p); 432 if (mBackground != null) { 433 mPopupView.refreshDrawableState(); 434 } 435 mAboveAnchor = findDropDownPosition(anchor, p, xoff, yoff); 436 if (mAnimationStyle == -1) { 437 p.windowAnimations = mAboveAnchor 438 ? com.android.internal.R.style.Animation_DropDownUp 439 : com.android.internal.R.style.Animation_DropDownDown; 440 } else { 441 p.windowAnimations = mAnimationStyle; 442 } 443 invokePopup(p); 444 } 445 446 /** 447 * <p>Prepare the popup by embedding in into a new ViewGroup if the 448 * background drawable is not null. If embedding is required, the layout 449 * parameters' height is mnodified to take into account the background's 450 * padding.</p> 451 * 452 * @param p the layout parameters of the popup's content view 453 */ 454 private void preparePopup(WindowManager.LayoutParams p) { 455 if (mBackground != null) { 456 // when a background is available, we embed the content view 457 // within another view that owns the background drawable 458 PopupViewContainer popupViewContainer = new PopupViewContainer(mContext); 459 PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams( 460 ViewGroup.LayoutParams.FILL_PARENT, 461 ViewGroup.LayoutParams.FILL_PARENT 462 ); 463 popupViewContainer.setBackgroundDrawable(mBackground); 464 popupViewContainer.addView(mContentView, listParams); 465 466 if (p.height >= 0) { 467 // accomodate the popup's height to take into account the 468 // background's padding 469 p.height += popupViewContainer.getPaddingTop() + 470 popupViewContainer.getPaddingBottom(); 471 } 472 if (p.width >= 0) { 473 // accomodate the popup's width to take into account the 474 // background's padding 475 p.width += popupViewContainer.getPaddingLeft() + 476 popupViewContainer.getPaddingRight(); 477 } 478 mPopupView = popupViewContainer; 479 } else { 480 mPopupView = mContentView; 481 } 482 483 } 484 485 /** 486 * <p>Invoke the popup window by adding the content view to the window 487 * manager.</p> 488 * 489 * <p>The content view must be non-null when this method is invoked.</p> 490 * 491 * @param p the layout parameters of the popup's content view 492 */ 493 private void invokePopup(WindowManager.LayoutParams p) { 494 WindowManagerImpl wm = WindowManagerImpl.getDefault(); 495 wm.addView(mPopupView, p); 496 } 497 498 /** 499 * <p>Generate the layout parameters for the popup window.</p> 500 * 501 * @param token the window token used to bind the popup's window 502 * 503 * @return the layout parameters to pass to the window manager 504 */ 505 private WindowManager.LayoutParams createPopupLayout(IBinder token) { 506 // generates the layout parameters for the drop down 507 // we want a fixed size view located at the bottom left of the anchor 508 WindowManager.LayoutParams p = new WindowManager.LayoutParams(); 509 // these gravity settings put the view at the top left corner of the 510 // screen. The view is then positioned to the appropriate location 511 // by setting the x and y offsets to match the anchor's bottom 512 // left corner 513 p.gravity = Gravity.LEFT | Gravity.TOP; 514 p.width = mWidth; 515 p.height = mHeight; 516 if (mBackground != null) { 517 p.format = mBackground.getOpacity(); 518 } else { 519 p.format = PixelFormat.TRANSLUCENT; 520 } 521 if(mIgnoreCheekPress) { 522 p.flags |= WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES; 523 } 524 if (!mFocusable) { 525 p.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 526 } 527 p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 528 p.token = token; 529 530 return p; 531 } 532 533 /** 534 * <p>Positions the popup window on screen. When the popup window is too 535 * tall to fit under the anchor, a parent scroll view is seeked and scrolled 536 * up to reclaim space. If scrolling is not possible or not enough, the 537 * popup window gets moved on top of the anchor.</p> 538 * 539 * <p>The height must have been set on the layout parameters prior to 540 * calling this method.</p> 541 * 542 * @param anchor the view on which the popup window must be anchored 543 * @param p the layout parameters used to display the drop down 544 * 545 * @return true if the popup is translated upwards to fit on screen 546 */ 547 private boolean findDropDownPosition(View anchor, WindowManager.LayoutParams p, int xoff, int yoff) { 548 anchor.getLocationInWindow(mDrawingLocation); 549 p.x = mDrawingLocation[0] + xoff; 550 p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; 551 552 boolean onTop = false; 553 554 if (p.y + p.height > WindowManagerImpl.getDefault().getDefaultDisplay().getHeight()) { 555 // if the drop down disappears at the bottom of the screen. we try to 556 // scroll a parent scrollview or move the drop down back up on top of 557 // the edit box 558 View root = anchor.getRootView(); 559 root.getLocationInWindow(mRootLocation); 560 int delta = p.y + p.height - mRootLocation[1] - root.getHeight(); 561 562 if (delta > 0 || p.x + p.width - mRootLocation[0] - root.getWidth() > 0) { 563 Rect r = new Rect(anchor.getScrollX(), anchor.getScrollY(), 564 p.width, p.height + anchor.getMeasuredHeight()); 565 566 onTop = !anchor.requestRectangleOnScreen(r, true); 567 568 if (onTop) { 569 p.y -= anchor.getMeasuredHeight() + p.height; 570 } else { 571 anchor.getLocationOnScreen(mDrawingLocation); 572 p.x = mDrawingLocation[0] + xoff; 573 p.y = mDrawingLocation[1] + anchor.getMeasuredHeight() + yoff; 574 } 575 } 576 } 577 578 return onTop; 579 } 580 581 /** 582 * Returns the maximum height that is available for the popup to be 583 * completely shown. It is recommended that this height be the maximum for 584 * the popup's height, otherwise it is possible that the popup will be 585 * clipped. 586 * 587 * @param anchor The view on which the popup window must be anchored. 588 * @return The maximum available height for the popup to be completely 589 * shown. 590 */ 591 public int getMaxAvailableHeight(View anchor) { 592 // TODO: read comment on STATUS_BAR_HEIGHT 593 final int screenHeight = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight() 594 - STATUS_BAR_HEIGHT; 595 596 final int[] anchorPos = mDrawingLocation; 597 anchor.getLocationOnScreen(anchorPos); 598 anchorPos[1] -= STATUS_BAR_HEIGHT; 599 600 final int distanceFromAnchorToBottom = screenHeight - (anchorPos[1] + anchor.getHeight()); 601 602 // anchorPos[1] is distance from anchor to top of screen 603 int returnedHeight = Math.max(anchorPos[1], distanceFromAnchorToBottom); 604 if (mBackground != null) { 605 mBackground.getPadding(mTempRect); 606 returnedHeight -= mTempRect.top + mTempRect.bottom; 607 } 608 609 return returnedHeight; 610 } 611 612 /** 613 * <p>Dispose of the popup window. This method can be invoked only after 614 * {@link #showAsDropDown(android.view.View)} has been executed. Failing that, calling 615 * this method will have no effect.</p> 616 * 617 * @see #showAsDropDown(android.view.View) 618 */ 619 public void dismiss() { 620 if (isShowing() && mPopupView != null) { 621 WindowManagerImpl wm = WindowManagerImpl.getDefault(); 622 wm.removeView(mPopupView); 623 if (mPopupView != mContentView && mPopupView instanceof ViewGroup) { 624 ((ViewGroup) mPopupView).removeView(mContentView); 625 } 626 mIsShowing = false; 627 628 if (mOnDismissListener != null) { 629 mOnDismissListener.onDismiss(); 630 } 631 } 632 } 633 634 /** 635 * Sets the listener to be called when the window is dismissed. 636 * 637 * @param onDismissListener The listener. 638 */ 639 public void setOnDismissListener(OnDismissListener onDismissListener) { 640 mOnDismissListener = onDismissListener; 641 } 642 643 /** 644 * <p>Updates the position and the dimension of the popup window. Width and 645 * height can be set to -1 to update location only.</p> 646 * 647 * @param x the new x location 648 * @param y the new y location 649 * @param width the new width, can be -1 to ignore 650 * @param height the new height, can be -1 to ignore 651 */ 652 public void update(int x, int y, int width, int height) { 653 if (width != -1) { 654 setWidth(width); 655 } 656 657 if (height != -1) { 658 setHeight(height); 659 } 660 661 if (!isShowing() || mContentView == null) { 662 return; 663 } 664 665 WindowManager.LayoutParams p = (WindowManager.LayoutParams) 666 mPopupView.getLayoutParams(); 667 668 boolean update = false; 669 670 if (width != -1 && p.width != width) { 671 p.width = width; 672 update = true; 673 } 674 675 if (height != -1 && p.height != height) { 676 p.height = height; 677 update = true; 678 } 679 680 if (p.x != x) { 681 p.x = x; 682 update = true; 683 } 684 685 if (p.y != y) { 686 p.y = y; 687 update = true; 688 } 689 690 if (update) { 691 if (mPopupView != mContentView) { 692 final View popupViewContainer = mPopupView; 693 if (p.height >= 0) { 694 // accomodate the popup's height to take into account the 695 // background's padding 696 p.height += popupViewContainer.getPaddingTop() + 697 popupViewContainer.getPaddingBottom(); 698 } 699 if (p.width >= 0) { 700 // accomodate the popup's width to take into account the 701 // background's padding 702 p.width += popupViewContainer.getPaddingLeft() + 703 popupViewContainer.getPaddingRight(); 704 } 705 } 706 707 WindowManagerImpl wm = WindowManagerImpl.getDefault(); 708 wm.updateViewLayout(mPopupView, p); 709 } 710 } 711 712 /** 713 * <p>Updates the position and the dimension of the popup window. Width and 714 * height can be set to -1 to update location only.</p> 715 * 716 * @param anchor the popup's anchor view 717 * @param width the new width, can be -1 to ignore 718 * @param height the new height, can be -1 to ignore 719 */ 720 public void update(View anchor, int width, int height) { 721 update(anchor, 0, 0, width, height); 722 } 723 724 /** 725 * <p>Updates the position and the dimension of the popup window. Width and 726 * height can be set to -1 to update location only.</p> 727 * 728 * @param anchor the popup's anchor view 729 * @param xoff x offset from the view's left edge 730 * @param yoff y offset from the view's bottom edge 731 * @param width the new width, can be -1 to ignore 732 * @param height the new height, can be -1 to ignore 733 */ 734 public void update(View anchor, int xoff, int yoff, int width, int height) { 735 if (!isShowing() || mContentView == null) { 736 return; 737 } 738 739 WindowManager.LayoutParams p = (WindowManager.LayoutParams) 740 mPopupView.getLayoutParams(); 741 742 int x = p.x; 743 int y = p.y; 744 findDropDownPosition(anchor, p, xoff, yoff); 745 746 update(x, y, width, height); 747 } 748 749 /** 750 * Listener that is called when this popup window is dismissed. 751 */ 752 interface OnDismissListener { 753 /** 754 * Called when this popup window is dismissed. 755 */ 756 public void onDismiss(); 757 } 758 759 private class PopupViewContainer extends FrameLayout { 760 761 public PopupViewContainer(Context context) { 762 super(context); 763 } 764 765 @Override 766 protected int[] onCreateDrawableState(int extraSpace) { 767 if (mAboveAnchor) { 768 // 1 more needed for the above anchor state 769 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 770 View.mergeDrawableStates(drawableState, ABOVE_ANCHOR_STATE_SET); 771 return drawableState; 772 } else { 773 return super.onCreateDrawableState(extraSpace); 774 } 775 } 776 777 @Override 778 public boolean dispatchKeyEvent(KeyEvent event) { 779 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 780 dismiss(); 781 return true; 782 } else { 783 return super.dispatchKeyEvent(event); 784 } 785 } 786 787 @Override 788 public boolean onTouchEvent(MotionEvent event) { 789 final int x = (int) event.getX(); 790 final int y = (int) event.getY(); 791 792 if ((event.getAction() == MotionEvent.ACTION_DOWN) 793 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) { 794 dismiss(); 795 return true; 796 } else { 797 return super.onTouchEvent(event); 798 } 799 } 800 801 } 802 803} 804