PopupWindowCompat.java revision f185f104c4786740765e549d535f9ba1052f96cc
1/* 2 * Copyright (C) 2013 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.support.v4.widget; 18 19import android.support.v4.view.GravityCompat; 20import android.support.v4.view.ViewCompat; 21import android.view.Gravity; 22import android.view.View; 23import android.view.WindowManager; 24import android.widget.PopupWindow; 25 26import java.lang.reflect.Method; 27 28/** 29 * Helper for accessing features in PopupWindow introduced after API level 4 30 * in a backwards compatible fashion. 31 */ 32public final class PopupWindowCompat { 33 /** 34 * Interface for the full API. 35 */ 36 interface PopupWindowImpl { 37 void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, int gravity); 38 void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor); 39 boolean getOverlapAnchor(PopupWindow popupWindow); 40 void setWindowLayoutType(PopupWindow popupWindow, int layoutType); 41 int getWindowLayoutType(PopupWindow popupWindow); 42 } 43 44 /** 45 * Interface implementation that doesn't use anything above v4 APIs. 46 */ 47 static class BasePopupWindowImpl implements PopupWindowImpl { 48 private static Method sSetWindowLayoutTypeMethod; 49 private static boolean sSetWindowLayoutTypeMethodAttempted; 50 private static Method sGetWindowLayoutTypeMethod; 51 private static boolean sGetWindowLayoutTypeMethodAttempted; 52 53 @Override 54 public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 55 int gravity) { 56 final int hgrav = GravityCompat.getAbsoluteGravity(gravity, 57 ViewCompat.getLayoutDirection(anchor)) & Gravity.HORIZONTAL_GRAVITY_MASK; 58 if (hgrav == Gravity.RIGHT) { 59 // Flip the location to align the right sides of the popup and 60 // anchor instead of left. 61 xoff -= (popup.getWidth() - anchor.getWidth()); 62 } 63 popup.showAsDropDown(anchor, xoff, yoff); 64 } 65 66 @Override 67 public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 68 // noop 69 } 70 71 @Override 72 public boolean getOverlapAnchor(PopupWindow popupWindow) { 73 return false; 74 } 75 76 @Override 77 public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 78 if (!sSetWindowLayoutTypeMethodAttempted) { 79 try { 80 sSetWindowLayoutTypeMethod = PopupWindow.class.getDeclaredMethod( 81 "setWindowLayoutType", int.class); 82 sSetWindowLayoutTypeMethod.setAccessible(true); 83 } catch (Exception e) { 84 // Reflection method fetch failed. Oh well. 85 } 86 sSetWindowLayoutTypeMethodAttempted = true; 87 } 88 89 if (sSetWindowLayoutTypeMethod != null) { 90 try { 91 sSetWindowLayoutTypeMethod.invoke(popupWindow, layoutType); 92 } catch (Exception e) { 93 // Reflection call failed. Oh well. 94 } 95 } 96 } 97 98 @Override 99 public int getWindowLayoutType(PopupWindow popupWindow) { 100 if (!sGetWindowLayoutTypeMethodAttempted) { 101 try { 102 sGetWindowLayoutTypeMethod = PopupWindow.class.getDeclaredMethod( 103 "getWindowLayoutType"); 104 sGetWindowLayoutTypeMethod.setAccessible(true); 105 } catch (Exception e) { 106 // Reflection method fetch failed. Oh well. 107 } 108 sGetWindowLayoutTypeMethodAttempted = true; 109 } 110 111 if (sGetWindowLayoutTypeMethod != null) { 112 try { 113 return (Integer) sGetWindowLayoutTypeMethod.invoke(popupWindow); 114 } catch (Exception e) { 115 // Reflection call failed. Oh well. 116 } 117 } 118 return 0; 119 } 120 } 121 122 /** 123 * Interface implementation for devices with at least KitKat APIs. 124 */ 125 static class KitKatPopupWindowImpl extends BasePopupWindowImpl { 126 @Override 127 public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 128 int gravity) { 129 PopupWindowCompatKitKat.showAsDropDown(popup, anchor, xoff, yoff, gravity); 130 } 131 } 132 133 static class Api21PopupWindowImpl extends KitKatPopupWindowImpl { 134 @Override 135 public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 136 PopupWindowCompatApi21.setOverlapAnchor(popupWindow, overlapAnchor); 137 } 138 139 @Override 140 public boolean getOverlapAnchor(PopupWindow popupWindow) { 141 return PopupWindowCompatApi21.getOverlapAnchor(popupWindow); 142 } 143 } 144 145 static class Api23PopupWindowImpl extends Api21PopupWindowImpl { 146 @Override 147 public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 148 PopupWindowCompatApi23.setOverlapAnchor(popupWindow, overlapAnchor); 149 } 150 151 @Override 152 public boolean getOverlapAnchor(PopupWindow popupWindow) { 153 return PopupWindowCompatApi23.getOverlapAnchor(popupWindow); 154 } 155 156 @Override 157 public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 158 PopupWindowCompatApi23.setWindowLayoutType(popupWindow, layoutType); 159 } 160 161 @Override 162 public int getWindowLayoutType(PopupWindow popupWindow) { 163 return PopupWindowCompatApi23.getWindowLayoutType(popupWindow); 164 } 165 } 166 167 /** 168 * Select the correct implementation to use for the current platform. 169 */ 170 static final PopupWindowImpl IMPL; 171 static { 172 final int version = android.os.Build.VERSION.SDK_INT; 173 if (version >= 23) { 174 IMPL = new Api23PopupWindowImpl(); 175 } else if (version >= 21) { 176 IMPL = new Api21PopupWindowImpl(); 177 } else if (version >= 19) { 178 IMPL = new KitKatPopupWindowImpl(); 179 } else { 180 IMPL = new BasePopupWindowImpl(); 181 } 182 } 183 184 private PopupWindowCompat() { 185 // This class is not publicly instantiable. 186 } 187 188 /** 189 * <p>Display the content view in a popup window anchored to the bottom-left 190 * corner of the anchor view offset by the specified x and y coordinates. 191 * If there is not enough room on screen to show 192 * the popup in its entirety, this method tries to find a parent scroll 193 * view to scroll. If no parent scroll view can be scrolled, the bottom-left 194 * corner of the popup is pinned at the top left corner of the anchor view.</p> 195 * <p>If the view later scrolls to move <code>anchor</code> to a different 196 * location, the popup will be moved correspondingly.</p> 197 * 198 * @param popup the PopupWindow to show 199 * @param anchor the view on which to pin the popup window 200 * @param xoff A horizontal offset from the anchor in pixels 201 * @param yoff A vertical offset from the anchor in pixels 202 * @param gravity Alignment of the popup relative to the anchor 203 */ 204 public static void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, 205 int gravity) { 206 IMPL.showAsDropDown(popup, anchor, xoff, yoff, gravity); 207 } 208 209 /** 210 * Sets whether the popup window should overlap its anchor view when 211 * displayed as a drop-down. 212 * 213 * @param overlapAnchor Whether the popup should overlap its anchor. 214 */ 215 public static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) { 216 IMPL.setOverlapAnchor(popupWindow, overlapAnchor); 217 } 218 219 /** 220 * Returns whether the popup window should overlap its anchor view when 221 * displayed as a drop-down. 222 * 223 * @return Whether the popup should overlap its anchor. 224 */ 225 public static boolean getOverlapAnchor(PopupWindow popupWindow) { 226 return IMPL.getOverlapAnchor(popupWindow); 227 } 228 229 /** 230 * Set the layout type for this window. This value will be passed through to 231 * {@link WindowManager.LayoutParams#type} therefore the value should match any value 232 * {@link WindowManager.LayoutParams#type} accepts. 233 * 234 * @param layoutType Layout type for this window. 235 * 236 * @see WindowManager.LayoutParams#type 237 */ 238 public static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) { 239 IMPL.setWindowLayoutType(popupWindow, layoutType); 240 } 241 242 /** 243 * Returns the layout type for this window. 244 * 245 * @see #setWindowLayoutType(PopupWindow popupWindow, int) 246 */ 247 public static int getWindowLayoutType(PopupWindow popupWindow) { 248 return IMPL.getWindowLayoutType(popupWindow); 249 } 250} 251