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