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