TypedArray.java revision a6f354b78ecba3add458df2857d4fbd7b3f0dcbc
1/* 2 * Copyright (C) 2008 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.content.res; 18 19import android.annotation.AnyRes; 20import android.annotation.ColorInt; 21import android.annotation.Nullable; 22import android.graphics.drawable.Drawable; 23import android.os.StrictMode; 24import android.util.AttributeSet; 25import android.util.DisplayMetrics; 26import android.util.TypedValue; 27 28import com.android.internal.util.XmlUtils; 29 30import java.util.Arrays; 31 32/** 33 * Container for an array of values that were retrieved with 34 * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)} 35 * or {@link Resources#obtainAttributes}. Be 36 * sure to call {@link #recycle} when done with them. 37 * 38 * The indices used to retrieve values from this structure correspond to 39 * the positions of the attributes given to obtainStyledAttributes. 40 */ 41public class TypedArray { 42 43 static TypedArray obtain(Resources res, int len) { 44 final TypedArray attrs = res.mTypedArrayPool.acquire(); 45 if (attrs != null) { 46 attrs.mLength = len; 47 attrs.mRecycled = false; 48 49 final int fullLen = len * AssetManager.STYLE_NUM_ENTRIES; 50 if (attrs.mData.length >= fullLen) { 51 return attrs; 52 } 53 54 attrs.mData = new int[fullLen]; 55 attrs.mIndices = new int[1 + len]; 56 return attrs; 57 } 58 59 return new TypedArray(res, 60 new int[len*AssetManager.STYLE_NUM_ENTRIES], 61 new int[1+len], len); 62 } 63 64 private final Resources mResources; 65 private final DisplayMetrics mMetrics; 66 private final AssetManager mAssets; 67 68 private boolean mRecycled; 69 70 /*package*/ XmlBlock.Parser mXml; 71 /*package*/ Resources.Theme mTheme; 72 /*package*/ int[] mData; 73 /*package*/ int[] mIndices; 74 /*package*/ int mLength; 75 /*package*/ TypedValue mValue = new TypedValue(); 76 77 /** 78 * Returns the number of values in this array. 79 * 80 * @throws RuntimeException if the TypedArray has already been recycled. 81 */ 82 public int length() { 83 if (mRecycled) { 84 throw new RuntimeException("Cannot make calls to a recycled instance!"); 85 } 86 87 return mLength; 88 } 89 90 /** 91 * Return the number of indices in the array that actually have data. 92 * 93 * @throws RuntimeException if the TypedArray has already been recycled. 94 */ 95 public int getIndexCount() { 96 if (mRecycled) { 97 throw new RuntimeException("Cannot make calls to a recycled instance!"); 98 } 99 100 return mIndices[0]; 101 } 102 103 /** 104 * Returns an index in the array that has data. 105 * 106 * @param at The index you would like to returned, ranging from 0 to 107 * {@link #getIndexCount()}. 108 * 109 * @return The index at the given offset, which can be used with 110 * {@link #getValue} and related APIs. 111 * @throws RuntimeException if the TypedArray has already been recycled. 112 */ 113 public int getIndex(int at) { 114 if (mRecycled) { 115 throw new RuntimeException("Cannot make calls to a recycled instance!"); 116 } 117 118 return mIndices[1+at]; 119 } 120 121 /** 122 * Returns the Resources object this array was loaded from. 123 * 124 * @throws RuntimeException if the TypedArray has already been recycled. 125 */ 126 public Resources getResources() { 127 if (mRecycled) { 128 throw new RuntimeException("Cannot make calls to a recycled instance!"); 129 } 130 131 return mResources; 132 } 133 134 /** 135 * Retrieves the styled string value for the attribute at <var>index</var>. 136 * <p> 137 * If the attribute is not a string, this method will attempt to coerce 138 * it to a string. 139 * 140 * @param index Index of attribute to retrieve. 141 * 142 * @return CharSequence holding string data. May be styled. Returns 143 * {@code null} if the attribute is not defined or could not be 144 * coerced to a string. 145 * @throws RuntimeException if the TypedArray has already been recycled. 146 */ 147 public CharSequence getText(int index) { 148 if (mRecycled) { 149 throw new RuntimeException("Cannot make calls to a recycled instance!"); 150 } 151 152 index *= AssetManager.STYLE_NUM_ENTRIES; 153 final int[] data = mData; 154 final int type = data[index+AssetManager.STYLE_TYPE]; 155 if (type == TypedValue.TYPE_NULL) { 156 return null; 157 } else if (type == TypedValue.TYPE_STRING) { 158 return loadStringValueAt(index); 159 } 160 161 final TypedValue v = mValue; 162 if (getValueAt(index, v)) { 163 StrictMode.noteResourceMismatch(v); 164 return v.coerceToString(); 165 } 166 167 // We already checked for TYPE_NULL. This should never happen. 168 throw new RuntimeException("getText of bad type: 0x" + Integer.toHexString(type)); 169 } 170 171 /** 172 * Retrieves the string value for the attribute at <var>index</var>. 173 * <p> 174 * If the attribute is not a string, this method will attempt to coerce 175 * it to a string. 176 * 177 * @param index Index of attribute to retrieve. 178 * 179 * @return String holding string data. Any styling information is removed. 180 * Returns {@code null} if the attribute is not defined or could 181 * not be coerced to a string. 182 * @throws RuntimeException if the TypedArray has already been recycled. 183 */ 184 @Nullable 185 public String getString(int index) { 186 return getString(index, true); 187 } 188 189 /** 190 * Returns a string representation of the value at the given index, 191 * optionally throwing a resource mismatch strict mode violation if the 192 * value must be coerced to a string. 193 * 194 * @param index the index of the attribute to retrieve 195 * @param strict {@code true} to throw a strict mode violation for string 196 * coercion, {@code false} otherwise 197 * @return a string representation of the value at the given index, or 198 * {@code null} if the resource could not be coerced to a string 199 * @see StrictMode#noteResourceMismatch(Object) 200 * @hide Used internally for view attribute inspection. 201 */ 202 @Nullable 203 public String getString(int index, boolean strict) { 204 if (mRecycled) { 205 throw new RuntimeException("Cannot make calls to a recycled instance!"); 206 } 207 208 index *= AssetManager.STYLE_NUM_ENTRIES; 209 final int[] data = mData; 210 final int type = data[index+AssetManager.STYLE_TYPE]; 211 if (type == TypedValue.TYPE_NULL) { 212 return null; 213 } else if (type == TypedValue.TYPE_STRING) { 214 return loadStringValueAt(index).toString(); 215 } 216 217 final TypedValue v = mValue; 218 if (getValueAt(index, v)) { 219 if (strict) { 220 StrictMode.noteResourceMismatch(v); 221 } 222 final CharSequence cs = v.coerceToString(); 223 return cs != null ? cs.toString() : null; 224 } 225 226 // We already checked for TYPE_NULL. This should never happen. 227 throw new RuntimeException("getString of bad type: 0x" + Integer.toHexString(type)); 228 } 229 230 /** 231 * Retrieves the string value for the attribute at <var>index</var>, but 232 * only if that string comes from an immediate value in an XML file. That 233 * is, this does not allow references to string resources, string 234 * attributes, or conversions from other types. As such, this method 235 * will only return strings for TypedArray objects that come from 236 * attributes in an XML file. 237 * 238 * @param index Index of attribute to retrieve. 239 * 240 * @return String holding string data. Any styling information is removed. 241 * Returns {@code null} if the attribute is not defined or is not 242 * an immediate string value. 243 * @throws RuntimeException if the TypedArray has already been recycled. 244 */ 245 public String getNonResourceString(int index) { 246 if (mRecycled) { 247 throw new RuntimeException("Cannot make calls to a recycled instance!"); 248 } 249 250 index *= AssetManager.STYLE_NUM_ENTRIES; 251 final int[] data = mData; 252 final int type = data[index+AssetManager.STYLE_TYPE]; 253 if (type == TypedValue.TYPE_STRING) { 254 final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; 255 if (cookie < 0) { 256 return mXml.getPooledString( 257 data[index+AssetManager.STYLE_DATA]).toString(); 258 } 259 } 260 return null; 261 } 262 263 /** 264 * Retrieves the string value for the attribute at <var>index</var> that is 265 * not allowed to change with the given configurations. 266 * 267 * @param index Index of attribute to retrieve. 268 * @param allowedChangingConfigs Bit mask of configurations from 269 * {@link Configuration}.NATIVE_CONFIG_* that are allowed to change. 270 * 271 * @return String holding string data. Any styling information is removed. 272 * Returns {@code null} if the attribute is not defined. 273 * @throws RuntimeException if the TypedArray has already been recycled. 274 * @hide 275 */ 276 public String getNonConfigurationString(int index, int allowedChangingConfigs) { 277 if (mRecycled) { 278 throw new RuntimeException("Cannot make calls to a recycled instance!"); 279 } 280 281 index *= AssetManager.STYLE_NUM_ENTRIES; 282 final int[] data = mData; 283 final int type = data[index+AssetManager.STYLE_TYPE]; 284 if ((data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]&~allowedChangingConfigs) != 0) { 285 return null; 286 } 287 if (type == TypedValue.TYPE_NULL) { 288 return null; 289 } else if (type == TypedValue.TYPE_STRING) { 290 return loadStringValueAt(index).toString(); 291 } 292 293 final TypedValue v = mValue; 294 if (getValueAt(index, v)) { 295 StrictMode.noteResourceMismatch(v); 296 final CharSequence cs = v.coerceToString(); 297 return cs != null ? cs.toString() : null; 298 } 299 300 // We already checked for TYPE_NULL. This should never happen. 301 throw new RuntimeException("getNonConfigurationString of bad type: 0x" 302 + Integer.toHexString(type)); 303 } 304 305 /** 306 * Retrieve the boolean value for the attribute at <var>index</var>. 307 * <p> 308 * If the attribute is an integer value, this method will return whether 309 * it is equal to zero. If the attribute is not a boolean or integer value, 310 * this method will attempt to coerce it to an integer using 311 * {@link Integer#decode(String)} and return whether it is equal to zero. 312 * 313 * @param index Index of attribute to retrieve. 314 * @param defValue Value to return if the attribute is not defined or 315 * cannot be coerced to an integer. 316 * 317 * @return Boolean value of the attribute, or defValue if the attribute was 318 * not defined or could not be coerced to an integer. 319 * @throws RuntimeException if the TypedArray has already been recycled. 320 */ 321 public boolean getBoolean(int index, boolean defValue) { 322 if (mRecycled) { 323 throw new RuntimeException("Cannot make calls to a recycled instance!"); 324 } 325 326 index *= AssetManager.STYLE_NUM_ENTRIES; 327 final int[] data = mData; 328 final int type = data[index+AssetManager.STYLE_TYPE]; 329 if (type == TypedValue.TYPE_NULL) { 330 return defValue; 331 } else if (type >= TypedValue.TYPE_FIRST_INT 332 && type <= TypedValue.TYPE_LAST_INT) { 333 return data[index+AssetManager.STYLE_DATA] != 0; 334 } 335 336 final TypedValue v = mValue; 337 if (getValueAt(index, v)) { 338 StrictMode.noteResourceMismatch(v); 339 return XmlUtils.convertValueToBoolean(v.coerceToString(), defValue); 340 } 341 342 // We already checked for TYPE_NULL. This should never happen. 343 throw new RuntimeException("getBoolean of bad type: 0x" + Integer.toHexString(type)); 344 } 345 346 /** 347 * Retrieve the integer value for the attribute at <var>index</var>. 348 * <p> 349 * If the attribute is not an integer, this method will attempt to coerce 350 * it to an integer using {@link Integer#decode(String)}. 351 * 352 * @param index Index of attribute to retrieve. 353 * @param defValue Value to return if the attribute is not defined or 354 * cannot be coerced to an integer. 355 * 356 * @return Integer value of the attribute, or defValue if the attribute was 357 * not defined or could not be coerced to an integer. 358 * @throws RuntimeException if the TypedArray has already been recycled. 359 */ 360 public int getInt(int index, int defValue) { 361 if (mRecycled) { 362 throw new RuntimeException("Cannot make calls to a recycled instance!"); 363 } 364 365 index *= AssetManager.STYLE_NUM_ENTRIES; 366 final int[] data = mData; 367 final int type = data[index+AssetManager.STYLE_TYPE]; 368 if (type == TypedValue.TYPE_NULL) { 369 return defValue; 370 } else if (type >= TypedValue.TYPE_FIRST_INT 371 && type <= TypedValue.TYPE_LAST_INT) { 372 return data[index+AssetManager.STYLE_DATA]; 373 } 374 375 final TypedValue v = mValue; 376 if (getValueAt(index, v)) { 377 StrictMode.noteResourceMismatch(v); 378 return XmlUtils.convertValueToInt(v.coerceToString(), defValue); 379 } 380 381 // We already checked for TYPE_NULL. This should never happen. 382 throw new RuntimeException("getInt of bad type: 0x" + Integer.toHexString(type)); 383 } 384 385 /** 386 * Retrieve the float value for the attribute at <var>index</var>. 387 * <p> 388 * If the attribute is not a float or an integer, this method will attempt 389 * to coerce it to a float using {@link Float#parseFloat(String)}. 390 * 391 * @param index Index of attribute to retrieve. 392 * 393 * @return Attribute float value, or defValue if the attribute was 394 * not defined or could not be coerced to a float. 395 * @throws RuntimeException if the TypedArray has already been recycled. 396 */ 397 public float getFloat(int index, float defValue) { 398 if (mRecycled) { 399 throw new RuntimeException("Cannot make calls to a recycled instance!"); 400 } 401 402 index *= AssetManager.STYLE_NUM_ENTRIES; 403 final int[] data = mData; 404 final int type = data[index+AssetManager.STYLE_TYPE]; 405 if (type == TypedValue.TYPE_NULL) { 406 return defValue; 407 } else if (type == TypedValue.TYPE_FLOAT) { 408 return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]); 409 } else if (type >= TypedValue.TYPE_FIRST_INT 410 && type <= TypedValue.TYPE_LAST_INT) { 411 return data[index+AssetManager.STYLE_DATA]; 412 } 413 414 final TypedValue v = mValue; 415 if (getValueAt(index, v)) { 416 final CharSequence str = v.coerceToString(); 417 if (str != null) { 418 StrictMode.noteResourceMismatch(v); 419 return Float.parseFloat(str.toString()); 420 } 421 } 422 423 // We already checked for TYPE_NULL. This should never happen. 424 throw new RuntimeException("getFloat of bad type: 0x" + Integer.toHexString(type)); 425 } 426 427 /** 428 * Retrieve the color value for the attribute at <var>index</var>. If 429 * the attribute references a color resource holding a complex 430 * {@link android.content.res.ColorStateList}, then the default color from 431 * the set is returned. 432 * <p> 433 * This method will throw an exception if the attribute is defined but is 434 * not an integer color or color state list. 435 * 436 * @param index Index of attribute to retrieve. 437 * @param defValue Value to return if the attribute is not defined or 438 * not a resource. 439 * 440 * @return Attribute color value, or defValue if not defined. 441 * @throws RuntimeException if the TypedArray has already been recycled. 442 * @throws UnsupportedOperationException if the attribute is defined but is 443 * not an integer color or color state list. 444 */ 445 @ColorInt 446 public int getColor(int index, @ColorInt int defValue) { 447 if (mRecycled) { 448 throw new RuntimeException("Cannot make calls to a recycled instance!"); 449 } 450 451 index *= AssetManager.STYLE_NUM_ENTRIES; 452 final int[] data = mData; 453 final int type = data[index+AssetManager.STYLE_TYPE]; 454 if (type == TypedValue.TYPE_NULL) { 455 return defValue; 456 } else if (type >= TypedValue.TYPE_FIRST_INT 457 && type <= TypedValue.TYPE_LAST_INT) { 458 return data[index+AssetManager.STYLE_DATA]; 459 } else if (type == TypedValue.TYPE_STRING) { 460 final TypedValue value = mValue; 461 if (getValueAt(index, value)) { 462 final ColorStateList csl = mResources.loadColorStateList( 463 value, value.resourceId, mTheme); 464 return csl.getDefaultColor(); 465 } 466 return defValue; 467 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 468 final TypedValue value = mValue; 469 getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value); 470 throw new UnsupportedOperationException( 471 "Failed to resolve attribute at index " + index + ": " + value); 472 } 473 474 throw new UnsupportedOperationException("Can't convert to color: type=0x" 475 + Integer.toHexString(type)); 476 } 477 478 /** 479 * Retrieve the ColorStateList for the attribute at <var>index</var>. 480 * The value may be either a single solid color or a reference to 481 * a color or complex {@link android.content.res.ColorStateList} 482 * description. 483 * <p> 484 * This method will return {@code null} if the attribute is not defined or 485 * is not an integer color or color state list. 486 * 487 * @param index Index of attribute to retrieve. 488 * 489 * @return ColorStateList for the attribute, or {@code null} if not 490 * defined. 491 * @throws RuntimeException if the attribute if the TypedArray has already 492 * been recycled. 493 * @throws UnsupportedOperationException if the attribute is defined but is 494 * not an integer color or color state list. 495 */ 496 @Nullable 497 public ColorStateList getColorStateList(int index) { 498 if (mRecycled) { 499 throw new RuntimeException("Cannot make calls to a recycled instance!"); 500 } 501 502 final TypedValue value = mValue; 503 if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { 504 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 505 throw new UnsupportedOperationException( 506 "Failed to resolve attribute at index " + index + ": " + value); 507 } 508 return mResources.loadColorStateList(value, value.resourceId, mTheme); 509 } 510 return null; 511 } 512 513 /** 514 * Retrieve the integer value for the attribute at <var>index</var>. 515 * <p> 516 * Unlike {@link #getInt(int, int)}, this method will throw an exception if 517 * the attribute is defined but is not an integer. 518 * 519 * @param index Index of attribute to retrieve. 520 * @param defValue Value to return if the attribute is not defined or 521 * not a resource. 522 * 523 * @return Attribute integer value, or defValue if not defined. 524 * @throws RuntimeException if the TypedArray has already been recycled. 525 * @throws UnsupportedOperationException if the attribute is defined but is 526 * not an integer. 527 */ 528 public int getInteger(int index, int defValue) { 529 if (mRecycled) { 530 throw new RuntimeException("Cannot make calls to a recycled instance!"); 531 } 532 533 index *= AssetManager.STYLE_NUM_ENTRIES; 534 final int[] data = mData; 535 final int type = data[index+AssetManager.STYLE_TYPE]; 536 if (type == TypedValue.TYPE_NULL) { 537 return defValue; 538 } else if (type >= TypedValue.TYPE_FIRST_INT 539 && type <= TypedValue.TYPE_LAST_INT) { 540 return data[index+AssetManager.STYLE_DATA]; 541 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 542 final TypedValue value = mValue; 543 getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value); 544 throw new UnsupportedOperationException( 545 "Failed to resolve attribute at index " + index + ": " + value); 546 } 547 548 throw new UnsupportedOperationException("Can't convert to integer: type=0x" 549 + Integer.toHexString(type)); 550 } 551 552 /** 553 * Retrieve a dimensional unit attribute at <var>index</var>. Unit 554 * conversions are based on the current {@link DisplayMetrics} 555 * associated with the resources this {@link TypedArray} object 556 * came from. 557 * <p> 558 * This method will throw an exception if the attribute is defined but is 559 * not a dimension. 560 * 561 * @param index Index of attribute to retrieve. 562 * @param defValue Value to return if the attribute is not defined or 563 * not a resource. 564 * 565 * @return Attribute dimension value multiplied by the appropriate 566 * metric, or defValue if not defined. 567 * @throws RuntimeException if the TypedArray has already been recycled. 568 * @throws UnsupportedOperationException if the attribute is defined but is 569 * not an integer. 570 * 571 * @see #getDimensionPixelOffset 572 * @see #getDimensionPixelSize 573 */ 574 public float getDimension(int index, float defValue) { 575 if (mRecycled) { 576 throw new RuntimeException("Cannot make calls to a recycled instance!"); 577 } 578 579 index *= AssetManager.STYLE_NUM_ENTRIES; 580 final int[] data = mData; 581 final int type = data[index+AssetManager.STYLE_TYPE]; 582 if (type == TypedValue.TYPE_NULL) { 583 return defValue; 584 } else if (type == TypedValue.TYPE_DIMENSION) { 585 return TypedValue.complexToDimension( 586 data[index + AssetManager.STYLE_DATA], mMetrics); 587 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 588 final TypedValue value = mValue; 589 getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value); 590 throw new UnsupportedOperationException( 591 "Failed to resolve attribute at index " + index + ": " + value); 592 } 593 594 throw new UnsupportedOperationException("Can't convert to dimension: type=0x" 595 + Integer.toHexString(type)); 596 } 597 598 /** 599 * Retrieve a dimensional unit attribute at <var>index</var> for use 600 * as an offset in raw pixels. This is the same as 601 * {@link #getDimension}, except the returned value is converted to 602 * integer pixels for you. An offset conversion involves simply 603 * truncating the base value to an integer. 604 * <p> 605 * This method will throw an exception if the attribute is defined but is 606 * not a dimension. 607 * 608 * @param index Index of attribute to retrieve. 609 * @param defValue Value to return if the attribute is not defined or 610 * not a resource. 611 * 612 * @return Attribute dimension value multiplied by the appropriate 613 * metric and truncated to integer pixels, or defValue if not defined. 614 * @throws RuntimeException if the TypedArray has already been recycled. 615 * @throws UnsupportedOperationException if the attribute is defined but is 616 * not an integer. 617 * 618 * @see #getDimension 619 * @see #getDimensionPixelSize 620 */ 621 public int getDimensionPixelOffset(int index, int defValue) { 622 if (mRecycled) { 623 throw new RuntimeException("Cannot make calls to a recycled instance!"); 624 } 625 626 index *= AssetManager.STYLE_NUM_ENTRIES; 627 final int[] data = mData; 628 final int type = data[index+AssetManager.STYLE_TYPE]; 629 if (type == TypedValue.TYPE_NULL) { 630 return defValue; 631 } else if (type == TypedValue.TYPE_DIMENSION) { 632 return TypedValue.complexToDimensionPixelOffset( 633 data[index + AssetManager.STYLE_DATA], mMetrics); 634 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 635 final TypedValue value = mValue; 636 getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value); 637 throw new UnsupportedOperationException( 638 "Failed to resolve attribute at index " + index + ": " + value); 639 } 640 641 throw new UnsupportedOperationException("Can't convert to dimension: type=0x" 642 + Integer.toHexString(type)); 643 } 644 645 /** 646 * Retrieve a dimensional unit attribute at <var>index</var> for use 647 * as a size in raw pixels. This is the same as 648 * {@link #getDimension}, except the returned value is converted to 649 * integer pixels for use as a size. A size conversion involves 650 * rounding the base value, and ensuring that a non-zero base value 651 * is at least one pixel in size. 652 * <p> 653 * This method will throw an exception if the attribute is defined but is 654 * not a dimension. 655 * 656 * @param index Index of attribute to retrieve. 657 * @param defValue Value to return if the attribute is not defined or 658 * not a resource. 659 * 660 * @return Attribute dimension value multiplied by the appropriate 661 * metric and truncated to integer pixels, or defValue if not defined. 662 * @throws RuntimeException if the TypedArray has already been recycled. 663 * @throws UnsupportedOperationException if the attribute is defined but is 664 * not a dimension. 665 * 666 * @see #getDimension 667 * @see #getDimensionPixelOffset 668 */ 669 public int getDimensionPixelSize(int index, int defValue) { 670 if (mRecycled) { 671 throw new RuntimeException("Cannot make calls to a recycled instance!"); 672 } 673 674 index *= AssetManager.STYLE_NUM_ENTRIES; 675 final int[] data = mData; 676 final int type = data[index+AssetManager.STYLE_TYPE]; 677 if (type == TypedValue.TYPE_NULL) { 678 return defValue; 679 } else if (type == TypedValue.TYPE_DIMENSION) { 680 return TypedValue.complexToDimensionPixelSize( 681 data[index+AssetManager.STYLE_DATA], mMetrics); 682 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 683 final TypedValue value = mValue; 684 getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value); 685 throw new UnsupportedOperationException( 686 "Failed to resolve attribute at index " + index + ": " + value); 687 } 688 689 throw new UnsupportedOperationException("Can't convert to dimension: type=0x" 690 + Integer.toHexString(type)); 691 } 692 693 /** 694 * Special version of {@link #getDimensionPixelSize} for retrieving 695 * {@link android.view.ViewGroup}'s layout_width and layout_height 696 * attributes. This is only here for performance reasons; applications 697 * should use {@link #getDimensionPixelSize}. 698 * <p> 699 * This method will throw an exception if the attribute is defined but is 700 * not a dimension or integer (enum). 701 * 702 * @param index Index of the attribute to retrieve. 703 * @param name Textual name of attribute for error reporting. 704 * 705 * @return Attribute dimension value multiplied by the appropriate 706 * metric and truncated to integer pixels. 707 * @throws RuntimeException if the TypedArray has already been recycled. 708 * @throws UnsupportedOperationException if the attribute is defined but is 709 * not a dimension or integer (enum). 710 */ 711 public int getLayoutDimension(int index, String name) { 712 if (mRecycled) { 713 throw new RuntimeException("Cannot make calls to a recycled instance!"); 714 } 715 716 index *= AssetManager.STYLE_NUM_ENTRIES; 717 final int[] data = mData; 718 final int type = data[index+AssetManager.STYLE_TYPE]; 719 if (type >= TypedValue.TYPE_FIRST_INT 720 && type <= TypedValue.TYPE_LAST_INT) { 721 return data[index+AssetManager.STYLE_DATA]; 722 } else if (type == TypedValue.TYPE_DIMENSION) { 723 return TypedValue.complexToDimensionPixelSize( 724 data[index+AssetManager.STYLE_DATA], mMetrics); 725 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 726 final TypedValue value = mValue; 727 getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value); 728 throw new UnsupportedOperationException( 729 "Failed to resolve attribute at index " + index + ": " + value); 730 } 731 732 throw new UnsupportedOperationException(getPositionDescription() 733 + ": You must supply a " + name + " attribute."); 734 } 735 736 /** 737 * Special version of {@link #getDimensionPixelSize} for retrieving 738 * {@link android.view.ViewGroup}'s layout_width and layout_height 739 * attributes. This is only here for performance reasons; applications 740 * should use {@link #getDimensionPixelSize}. 741 * 742 * @param index Index of the attribute to retrieve. 743 * @param defValue The default value to return if this attribute is not 744 * default or contains the wrong type of data. 745 * 746 * @return Attribute dimension value multiplied by the appropriate 747 * metric and truncated to integer pixels. 748 * @throws RuntimeException if the TypedArray has already been recycled. 749 */ 750 public int getLayoutDimension(int index, int defValue) { 751 if (mRecycled) { 752 throw new RuntimeException("Cannot make calls to a recycled instance!"); 753 } 754 755 index *= AssetManager.STYLE_NUM_ENTRIES; 756 final int[] data = mData; 757 final int type = data[index+AssetManager.STYLE_TYPE]; 758 if (type >= TypedValue.TYPE_FIRST_INT 759 && type <= TypedValue.TYPE_LAST_INT) { 760 return data[index+AssetManager.STYLE_DATA]; 761 } else if (type == TypedValue.TYPE_DIMENSION) { 762 return TypedValue.complexToDimensionPixelSize( 763 data[index + AssetManager.STYLE_DATA], mMetrics); 764 } 765 766 return defValue; 767 } 768 769 /** 770 * Retrieves a fractional unit attribute at <var>index</var>. 771 * 772 * @param index Index of attribute to retrieve. 773 * @param base The base value of this fraction. In other words, a 774 * standard fraction is multiplied by this value. 775 * @param pbase The parent base value of this fraction. In other 776 * words, a parent fraction (nn%p) is multiplied by this 777 * value. 778 * @param defValue Value to return if the attribute is not defined or 779 * not a resource. 780 * 781 * @return Attribute fractional value multiplied by the appropriate 782 * base value, or defValue if not defined. 783 * @throws RuntimeException if the TypedArray has already been recycled. 784 * @throws UnsupportedOperationException if the attribute is defined but is 785 * not a fraction. 786 */ 787 public float getFraction(int index, int base, int pbase, float defValue) { 788 if (mRecycled) { 789 throw new RuntimeException("Cannot make calls to a recycled instance!"); 790 } 791 792 index *= AssetManager.STYLE_NUM_ENTRIES; 793 final int[] data = mData; 794 final int type = data[index+AssetManager.STYLE_TYPE]; 795 if (type == TypedValue.TYPE_NULL) { 796 return defValue; 797 } else if (type == TypedValue.TYPE_FRACTION) { 798 return TypedValue.complexToFraction( 799 data[index+AssetManager.STYLE_DATA], base, pbase); 800 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 801 final TypedValue value = mValue; 802 getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value); 803 throw new UnsupportedOperationException( 804 "Failed to resolve attribute at index " + index + ": " + value); 805 } 806 807 throw new UnsupportedOperationException("Can't convert to fraction: type=0x" 808 + Integer.toHexString(type)); 809 } 810 811 /** 812 * Retrieves the resource identifier for the attribute at 813 * <var>index</var>. Note that attribute resource as resolved when 814 * the overall {@link TypedArray} object is retrieved. As a 815 * result, this function will return the resource identifier of the 816 * final resource value that was found, <em>not</em> necessarily the 817 * original resource that was specified by the attribute. 818 * 819 * @param index Index of attribute to retrieve. 820 * @param defValue Value to return if the attribute is not defined or 821 * not a resource. 822 * 823 * @return Attribute resource identifier, or defValue if not defined. 824 * @throws RuntimeException if the TypedArray has already been recycled. 825 */ 826 @AnyRes 827 public int getResourceId(int index, int defValue) { 828 if (mRecycled) { 829 throw new RuntimeException("Cannot make calls to a recycled instance!"); 830 } 831 832 index *= AssetManager.STYLE_NUM_ENTRIES; 833 final int[] data = mData; 834 if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { 835 final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; 836 if (resid != 0) { 837 return resid; 838 } 839 } 840 return defValue; 841 } 842 843 /** 844 * Retrieves the theme attribute resource identifier for the attribute at 845 * <var>index</var>. 846 * 847 * @param index Index of attribute to retrieve. 848 * @param defValue Value to return if the attribute is not defined or not a 849 * resource. 850 * 851 * @return Theme attribute resource identifier, or defValue if not defined. 852 * @throws RuntimeException if the TypedArray has already been recycled. 853 * @hide 854 */ 855 public int getThemeAttributeId(int index, int defValue) { 856 if (mRecycled) { 857 throw new RuntimeException("Cannot make calls to a recycled instance!"); 858 } 859 860 index *= AssetManager.STYLE_NUM_ENTRIES; 861 final int[] data = mData; 862 if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { 863 return data[index + AssetManager.STYLE_DATA]; 864 } 865 return defValue; 866 } 867 868 /** 869 * Retrieve the Drawable for the attribute at <var>index</var>. 870 * <p> 871 * This method will throw an exception if the attribute is defined but is 872 * not a color or drawable resource. 873 * 874 * @param index Index of attribute to retrieve. 875 * 876 * @return Drawable for the attribute, or {@code null} if not defined. 877 * @throws RuntimeException if the TypedArray has already been recycled. 878 * @throws UnsupportedOperationException if the attribute is defined but is 879 * not a color or drawable resource. 880 */ 881 @Nullable 882 public Drawable getDrawable(int index) { 883 if (mRecycled) { 884 throw new RuntimeException("Cannot make calls to a recycled instance!"); 885 } 886 887 final TypedValue value = mValue; 888 if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { 889 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 890 throw new UnsupportedOperationException( 891 "Failed to resolve attribute at index " + index + ": " + value); 892 } 893 return mResources.loadDrawable(value, value.resourceId, mTheme); 894 } 895 return null; 896 } 897 898 /** 899 * Retrieve the CharSequence[] for the attribute at <var>index</var>. 900 * This gets the resource ID of the selected attribute, and uses 901 * {@link Resources#getTextArray Resources.getTextArray} of the owning 902 * Resources object to retrieve its String[]. 903 * <p> 904 * This method will throw an exception if the attribute is defined but is 905 * not a text array resource. 906 * 907 * @param index Index of attribute to retrieve. 908 * 909 * @return CharSequence[] for the attribute, or {@code null} if not 910 * defined. 911 * @throws RuntimeException if the TypedArray has already been recycled. 912 */ 913 public CharSequence[] getTextArray(int index) { 914 if (mRecycled) { 915 throw new RuntimeException("Cannot make calls to a recycled instance!"); 916 } 917 918 final TypedValue value = mValue; 919 if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { 920 return mResources.getTextArray(value.resourceId); 921 } 922 return null; 923 } 924 925 /** 926 * Retrieve the raw TypedValue for the attribute at <var>index</var>. 927 * 928 * @param index Index of attribute to retrieve. 929 * @param outValue TypedValue object in which to place the attribute's 930 * data. 931 * 932 * @return {@code true} if the value was retrieved, false otherwise. 933 * @throws RuntimeException if the TypedArray has already been recycled. 934 */ 935 public boolean getValue(int index, TypedValue outValue) { 936 if (mRecycled) { 937 throw new RuntimeException("Cannot make calls to a recycled instance!"); 938 } 939 940 return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); 941 } 942 943 /** 944 * Returns the type of attribute at the specified index. 945 * 946 * @param index Index of attribute whose type to retrieve. 947 * 948 * @return Attribute type. 949 * @throws RuntimeException if the TypedArray has already been recycled. 950 */ 951 public int getType(int index) { 952 if (mRecycled) { 953 throw new RuntimeException("Cannot make calls to a recycled instance!"); 954 } 955 956 index *= AssetManager.STYLE_NUM_ENTRIES; 957 return mData[index + AssetManager.STYLE_TYPE]; 958 } 959 960 /** 961 * Determines whether there is an attribute at <var>index</var>. 962 * <p> 963 * <strong>Note:</strong> If the attribute was set to {@code @empty} or 964 * {@code @undefined}, this method returns {@code false}. 965 * 966 * @param index Index of attribute to retrieve. 967 * 968 * @return True if the attribute has a value, false otherwise. 969 * @throws RuntimeException if the TypedArray has already been recycled. 970 */ 971 public boolean hasValue(int index) { 972 if (mRecycled) { 973 throw new RuntimeException("Cannot make calls to a recycled instance!"); 974 } 975 976 index *= AssetManager.STYLE_NUM_ENTRIES; 977 final int[] data = mData; 978 final int type = data[index+AssetManager.STYLE_TYPE]; 979 return type != TypedValue.TYPE_NULL; 980 } 981 982 /** 983 * Determines whether there is an attribute at <var>index</var>, returning 984 * {@code true} if the attribute was explicitly set to {@code @empty} and 985 * {@code false} only if the attribute was undefined. 986 * 987 * @param index Index of attribute to retrieve. 988 * 989 * @return True if the attribute has a value or is empty, false otherwise. 990 * @throws RuntimeException if the TypedArray has already been recycled. 991 */ 992 public boolean hasValueOrEmpty(int index) { 993 if (mRecycled) { 994 throw new RuntimeException("Cannot make calls to a recycled instance!"); 995 } 996 997 index *= AssetManager.STYLE_NUM_ENTRIES; 998 final int[] data = mData; 999 final int type = data[index+AssetManager.STYLE_TYPE]; 1000 return type != TypedValue.TYPE_NULL 1001 || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; 1002 } 1003 1004 /** 1005 * Retrieve the raw TypedValue for the attribute at <var>index</var> 1006 * and return a temporary object holding its data. This object is only 1007 * valid until the next call on to {@link TypedArray}. 1008 * 1009 * @param index Index of attribute to retrieve. 1010 * 1011 * @return Returns a TypedValue object if the attribute is defined, 1012 * containing its data; otherwise returns null. (You will not 1013 * receive a TypedValue whose type is TYPE_NULL.) 1014 * @throws RuntimeException if the TypedArray has already been recycled. 1015 */ 1016 public TypedValue peekValue(int index) { 1017 if (mRecycled) { 1018 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1019 } 1020 1021 final TypedValue value = mValue; 1022 if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { 1023 return value; 1024 } 1025 return null; 1026 } 1027 1028 /** 1029 * Returns a message about the parser state suitable for printing error messages. 1030 * 1031 * @return Human-readable description of current parser state. 1032 * @throws RuntimeException if the TypedArray has already been recycled. 1033 */ 1034 public String getPositionDescription() { 1035 if (mRecycled) { 1036 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1037 } 1038 1039 return mXml != null ? mXml.getPositionDescription() : "<internal>"; 1040 } 1041 1042 /** 1043 * Recycles the TypedArray, to be re-used by a later caller. After calling 1044 * this function you must not ever touch the typed array again. 1045 * 1046 * @throws RuntimeException if the TypedArray has already been recycled. 1047 */ 1048 public void recycle() { 1049 if (mRecycled) { 1050 throw new RuntimeException(toString() + " recycled twice!"); 1051 } 1052 1053 mRecycled = true; 1054 1055 // These may have been set by the client. 1056 mXml = null; 1057 mTheme = null; 1058 1059 mResources.mTypedArrayPool.release(this); 1060 } 1061 1062 /** 1063 * Extracts theme attributes from a typed array for later resolution using 1064 * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}. 1065 * Removes the entries from the typed array so that subsequent calls to typed 1066 * getters will return the default value without crashing. 1067 * 1068 * @return an array of length {@link #getIndexCount()} populated with theme 1069 * attributes, or null if there are no theme attributes in the typed 1070 * array 1071 * @throws RuntimeException if the TypedArray has already been recycled. 1072 * @hide 1073 */ 1074 @Nullable 1075 public int[] extractThemeAttrs() { 1076 return extractThemeAttrs(null); 1077 } 1078 1079 /** 1080 * @hide 1081 */ 1082 @Nullable 1083 public int[] extractThemeAttrs(@Nullable int[] scrap) { 1084 if (mRecycled) { 1085 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1086 } 1087 1088 int[] attrs = null; 1089 1090 final int[] data = mData; 1091 final int N = length(); 1092 for (int i = 0; i < N; i++) { 1093 final int index = i * AssetManager.STYLE_NUM_ENTRIES; 1094 if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { 1095 // Not an attribute, ignore. 1096 continue; 1097 } 1098 1099 // Null the entry so that we can safely call getZzz(). 1100 data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; 1101 1102 final int attr = data[index + AssetManager.STYLE_DATA]; 1103 if (attr == 0) { 1104 // Useless data, ignore. 1105 continue; 1106 } 1107 1108 // Ensure we have a usable attribute array. 1109 if (attrs == null) { 1110 if (scrap != null && scrap.length == N) { 1111 attrs = scrap; 1112 Arrays.fill(attrs, 0); 1113 } else { 1114 attrs = new int[N]; 1115 } 1116 } 1117 1118 attrs[i] = attr; 1119 } 1120 1121 return attrs; 1122 } 1123 1124 /** 1125 * Return a mask of the configuration parameters for which the values in 1126 * this typed array may change. 1127 * 1128 * @return Returns a mask of the changing configuration parameters, as 1129 * defined by {@link android.content.pm.ActivityInfo}. 1130 * @throws RuntimeException if the TypedArray has already been recycled. 1131 * @see android.content.pm.ActivityInfo 1132 */ 1133 public int getChangingConfigurations() { 1134 if (mRecycled) { 1135 throw new RuntimeException("Cannot make calls to a recycled instance!"); 1136 } 1137 1138 int changingConfig = 0; 1139 1140 final int[] data = mData; 1141 final int N = length(); 1142 for (int i = 0; i < N; i++) { 1143 final int index = i * AssetManager.STYLE_NUM_ENTRIES; 1144 final int type = data[index + AssetManager.STYLE_TYPE]; 1145 if (type == TypedValue.TYPE_NULL) { 1146 continue; 1147 } 1148 changingConfig |= data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]; 1149 } 1150 return changingConfig; 1151 } 1152 1153 private boolean getValueAt(int index, TypedValue outValue) { 1154 final int[] data = mData; 1155 final int type = data[index+AssetManager.STYLE_TYPE]; 1156 if (type == TypedValue.TYPE_NULL) { 1157 return false; 1158 } 1159 outValue.type = type; 1160 outValue.data = data[index+AssetManager.STYLE_DATA]; 1161 outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; 1162 outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; 1163 outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]; 1164 outValue.density = data[index+AssetManager.STYLE_DENSITY]; 1165 outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; 1166 return true; 1167 } 1168 1169 private CharSequence loadStringValueAt(int index) { 1170 final int[] data = mData; 1171 final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; 1172 if (cookie < 0) { 1173 if (mXml != null) { 1174 return mXml.getPooledString( 1175 data[index+AssetManager.STYLE_DATA]); 1176 } 1177 return null; 1178 } 1179 return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); 1180 } 1181 1182 /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) { 1183 mResources = resources; 1184 mMetrics = mResources.mMetrics; 1185 mAssets = mResources.mAssets; 1186 mData = data; 1187 mIndices = indices; 1188 mLength = len; 1189 } 1190 1191 @Override 1192 public String toString() { 1193 return Arrays.toString(mData); 1194 } 1195} 1196