ViewCompat.java revision c95beb648f59c89c6bd7b0eed0a8b266a1b287e2
1/* 2 * Copyright (C) 2011 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.view; 18 19import android.graphics.Rect; 20import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 21import android.view.View; 22import android.view.accessibility.AccessibilityEvent; 23 24/** 25 * Helper for accessing features in {@link View} introduced after API 26 * level 4 in a backwards compatible fashion. 27 */ 28public class ViewCompat { 29 /** 30 * Always allow a user to over-scroll this view, provided it is a 31 * view that can scroll. 32 */ 33 public static final int OVER_SCROLL_ALWAYS = 0; 34 35 /** 36 * Allow a user to over-scroll this view only if the content is large 37 * enough to meaningfully scroll, provided it is a view that can scroll. 38 */ 39 public static final int OVER_SCROLL_IF_CONTENT_SCROLLS = 1; 40 41 /** 42 * Never allow a user to over-scroll this view. 43 */ 44 public static final int OVER_SCROLL_NEVER = 2; 45 46 interface ViewCompatImpl { 47 public boolean canScrollHorizontally(View v, int direction); 48 public boolean canScrollVertically(View v, int direction); 49 public int getOverScrollMode(View v); 50 public void setOverScrollMode(View v, int mode); 51 public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event); 52 public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event); 53 public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info); 54 public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate); 55 public boolean hasTransientState(View view); 56 public void setHasTransientState(View view, boolean hasTransientState); 57 } 58 59 static class BaseViewCompatImpl implements ViewCompatImpl { 60 public boolean canScrollHorizontally(View v, int direction) { 61 return false; 62 } 63 public boolean canScrollVertically(View v, int direction) { 64 return false; 65 } 66 public int getOverScrollMode(View v) { 67 return OVER_SCROLL_NEVER; 68 } 69 public void setOverScrollMode(View v, int mode) { 70 // Do nothing; API doesn't exist 71 } 72 public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { 73 // Do nothing; API doesn't exist 74 } 75 public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { 76 // Do nothing; API doesn't exist 77 } 78 public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { 79 // Do nothing; API doesn't exist 80 } 81 public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { 82 // Do nothing; API doesn't exist 83 } 84 public boolean hasTransientState(View view) { 85 // A view can't have transient state if transient state wasn't supported. 86 return false; 87 } 88 public void setHasTransientState(View view, boolean hasTransientState) { 89 // Do nothing; API doesn't exist 90 } 91 } 92 93 static class GBViewCompatImpl extends BaseViewCompatImpl { 94 @Override 95 public int getOverScrollMode(View v) { 96 return ViewCompatGingerbread.getOverScrollMode(v); 97 } 98 @Override 99 public void setOverScrollMode(View v, int mode) { 100 ViewCompatGingerbread.setOverScrollMode(v, mode); 101 } 102 } 103 104 static class ICSViewCompatImpl extends GBViewCompatImpl { 105 @Override 106 public boolean canScrollHorizontally(View v, int direction) { 107 return ViewCompatICS.canScrollHorizontally(v, direction); 108 } 109 @Override 110 public boolean canScrollVertically(View v, int direction) { 111 return ViewCompatICS.canScrollVertically(v, direction); 112 } 113 @Override 114 public void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { 115 ViewCompatICS.onPopulateAccessibilityEvent(v, event); 116 } 117 @Override 118 public void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { 119 ViewCompatICS.onInitializeAccessibilityEvent(v, event); 120 } 121 @Override 122 public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { 123 ViewCompatICS.onInitializeAccessibilityNodeInfo(v, info.getInfo()); 124 } 125 @Override 126 public void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { 127 ViewCompatICS.setAccessibilityDelegate(v, delegate.getBridge()); 128 } 129 } 130 131 static class JBViewCompatImpl extends ICSViewCompatImpl { 132 @Override 133 public boolean hasTransientState(View view) { 134 return ViewCompatJB.hasTransientState(view); 135 } 136 @Override 137 public void setHasTransientState(View view, boolean hasTransientState) { 138 ViewCompatJB.setHasTransientState(view, hasTransientState); 139 } 140 } 141 142 static final ViewCompatImpl IMPL; 143 static { 144 final int version = android.os.Build.VERSION.SDK_INT; 145 if (version >= 16 || android.os.Build.VERSION.CODENAME.equals("JellyBean")) { 146 IMPL = new JBViewCompatImpl(); 147 } else if (version >= 14) { 148 IMPL = new ICSViewCompatImpl(); 149 } else if (version >= 9) { 150 IMPL = new GBViewCompatImpl(); 151 } else { 152 IMPL = new BaseViewCompatImpl(); 153 } 154 } 155 156 /** 157 * Check if this view can be scrolled horizontally in a certain direction. 158 * 159 * @param v The View against which to invoke the method. 160 * @param direction Negative to check scrolling left, positive to check scrolling right. 161 * @return true if this view can be scrolled in the specified direction, false otherwise. 162 */ 163 public static boolean canScrollHorizontally(View v, int direction) { 164 return IMPL.canScrollHorizontally(v, direction); 165 } 166 167 /** 168 * Check if this view can be scrolled vertically in a certain direction. 169 * 170 * @param v The View against which to invoke the method. 171 * @param direction Negative to check scrolling up, positive to check scrolling down. 172 * @return true if this view can be scrolled in the specified direction, false otherwise. 173 */ 174 public static boolean canScrollVertically(View v, int direction) { 175 return IMPL.canScrollVertically(v, direction); 176 } 177 178 /** 179 * Returns the over-scroll mode for this view. The result will be 180 * one of {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} 181 * (allow over-scrolling only if the view content is larger than the container), 182 * or {@link #OVER_SCROLL_NEVER}. 183 * 184 * @param v The View against which to invoke the method. 185 * @return This view's over-scroll mode. 186 */ 187 public static int getOverScrollMode(View v) { 188 return IMPL.getOverScrollMode(v); 189 } 190 191 /** 192 * Set the over-scroll mode for this view. Valid over-scroll modes are 193 * {@link #OVER_SCROLL_ALWAYS} (default), {@link #OVER_SCROLL_IF_CONTENT_SCROLLS} 194 * (allow over-scrolling only if the view content is larger than the container), 195 * or {@link #OVER_SCROLL_NEVER}. 196 * 197 * Setting the over-scroll mode of a view will have an effect only if the 198 * view is capable of scrolling. 199 * 200 * @param v The View against which to invoke the method. 201 * @param overScrollMode The new over-scroll mode for this view. 202 */ 203 public static void setOverScrollMode(View v, int overScrollMode) { 204 IMPL.setOverScrollMode(v, overScrollMode); 205 } 206 207 /** 208 * Called from {@link View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)} 209 * giving a chance to this View to populate the accessibility event with its 210 * text content. While this method is free to modify event 211 * attributes other than text content, doing so should normally be performed in 212 * {@link View#onInitializeAccessibilityEvent(AccessibilityEvent)}. 213 * <p> 214 * Example: Adding formatted date string to an accessibility event in addition 215 * to the text added by the super implementation: 216 * <pre> public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 217 * super.onPopulateAccessibilityEvent(event); 218 * final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY; 219 * String selectedDateUtterance = DateUtils.formatDateTime(mContext, 220 * mCurrentDate.getTimeInMillis(), flags); 221 * event.getText().add(selectedDateUtterance); 222 * }</pre> 223 * <p> 224 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling 225 * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its 226 * {@link android.view.View.AccessibilityDelegate#onPopulateAccessibilityEvent(View, 227 * AccessibilityEvent)} 228 * is responsible for handling this call. 229 * </p> 230 * <p class="note"><strong>Note:</strong> Always call the super implementation before adding 231 * information to the event, in case the default implementation has basic information to add. 232 * </p> 233 * 234 * @param v The View against which to invoke the method. 235 * @param event The accessibility event which to populate. 236 * 237 * @see View#sendAccessibilityEvent(int) 238 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 239 */ 240 public static void onPopulateAccessibilityEvent(View v, AccessibilityEvent event) { 241 IMPL.onPopulateAccessibilityEvent(v, event); 242 } 243 244 /** 245 * Initializes an {@link AccessibilityEvent} with information about 246 * this View which is the event source. In other words, the source of 247 * an accessibility event is the view whose state change triggered firing 248 * the event. 249 * <p> 250 * Example: Setting the password property of an event in addition 251 * to properties set by the super implementation: 252 * <pre> public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 253 * super.onInitializeAccessibilityEvent(event); 254 * event.setPassword(true); 255 * }</pre> 256 * <p> 257 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling 258 * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its 259 * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityEvent(View, 260 * AccessibilityEvent)} 261 * is responsible for handling this call. 262 * </p> 263 * <p class="note"><strong>Note:</strong> Always call the super implementation before adding 264 * information to the event, in case the default implementation has basic information to add. 265 * </p> 266 * 267 * @param v The View against which to invoke the method. 268 * @param event The event to initialize. 269 * 270 * @see View#sendAccessibilityEvent(int) 271 * @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) 272 */ 273 public static void onInitializeAccessibilityEvent(View v, AccessibilityEvent event) { 274 IMPL.onInitializeAccessibilityEvent(v, event); 275 } 276 277 /** 278 * Initializes an {@link android.view.accessibility.AccessibilityNodeInfo} with information 279 * about this view. The base implementation sets: 280 * <ul> 281 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setParent(View)},</li> 282 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInParent(Rect)},</li> 283 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li> 284 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setPackageName(CharSequence)},</li> 285 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClassName(CharSequence)},</li> 286 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setContentDescription(CharSequence)},</li> 287 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setEnabled(boolean)},</li> 288 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setClickable(boolean)},</li> 289 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocusable(boolean)},</li> 290 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setFocused(boolean)},</li> 291 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setLongClickable(boolean)},</li> 292 * <li>{@link android.view.accessibility.AccessibilityNodeInfo#setSelected(boolean)},</li> 293 * </ul> 294 * <p> 295 * Subclasses should override this method, call the super implementation, 296 * and set additional attributes. 297 * </p> 298 * <p> 299 * If an {@link android.view.View.AccessibilityDelegate} has been specified via calling 300 * {@link View#setAccessibilityDelegate(android.view.View.AccessibilityDelegate)} its 301 * {@link android.view.View.AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, 302 * android.view.accessibility.AccessibilityNodeInfo)} 303 * is responsible for handling this call. 304 * </p> 305 * 306 * @param v The View against which to invoke the method. 307 * @param info The instance to initialize. 308 */ 309 public static void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfoCompat info) { 310 IMPL.onInitializeAccessibilityNodeInfo(v, info); 311 } 312 313 /** 314 * Sets a delegate for implementing accessibility support via compositon as 315 * opposed to inheritance. The delegate's primary use is for implementing 316 * backwards compatible widgets. For more details see 317 * {@link android.view.View.AccessibilityDelegate}. 318 * 319 * @param v The View against which to invoke the method. 320 * @param delegate The delegate instance. 321 * 322 * @see android.view.View.AccessibilityDelegate 323 */ 324 public static void setAccessibilityDelegate(View v, AccessibilityDelegateCompat delegate) { 325 IMPL.setAccessibilityDelegate(v, delegate); 326 } 327 328 /** 329 * Indicates whether the view is currently tracking transient state that the 330 * app should not need to concern itself with saving and restoring, but that 331 * the framework should take special note to preserve when possible. 332 * 333 * @param view View to check for transient state 334 * @return true if the view has transient state 335 */ 336 public static boolean hasTransientState(View view) { 337 return IMPL.hasTransientState(view); 338 } 339 340 /** 341 * Set whether this view is currently tracking transient state that the 342 * framework should attempt to preserve when possible. 343 * 344 * @param view View tracking transient state 345 * @param hasTransientState true if this view has transient state 346 */ 347 public static void setHasTransientState(View view, boolean hasTransientState) { 348 IMPL.setHasTransientState(view, hasTransientState); 349 } 350} 351