TypedArray.java revision 500c56089f7e3e2735a105954a05801025d56921
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 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 404 throw new RuntimeException("Failed to resolve attribute at index " + index); 405 } 406 407 throw new UnsupportedOperationException("Can't convert to color: type=0x" 408 + Integer.toHexString(type)); 409 } 410 411 /** 412 * Retrieve the ColorStateList for the attribute at <var>index</var>. 413 * The value may be either a single solid color or a reference to 414 * a color or complex {@link android.content.res.ColorStateList} description. 415 * 416 * @param index Index of attribute to retrieve. 417 * 418 * @return ColorStateList for the attribute, or null if not defined. 419 */ 420 public ColorStateList getColorStateList(int index) { 421 if (mRecycled) { 422 throw new RuntimeException("Cannot make calls to a recycled instance!"); 423 } 424 425 final TypedValue value = mValue; 426 if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { 427 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 428 throw new RuntimeException("Failed to resolve attribute at index " + index); 429 } 430 return mResources.loadColorStateList(value, value.resourceId); 431 } 432 return null; 433 } 434 435 /** 436 * Retrieve the integer value for the attribute at <var>index</var>. 437 * 438 * @param index Index of attribute to retrieve. 439 * @param defValue Value to return if the attribute is not defined or 440 * not a resource. 441 * 442 * @return Attribute integer value, or defValue if not defined. 443 */ 444 public int getInteger(int index, int defValue) { 445 if (mRecycled) { 446 throw new RuntimeException("Cannot make calls to a recycled instance!"); 447 } 448 449 index *= AssetManager.STYLE_NUM_ENTRIES; 450 final int[] data = mData; 451 final int type = data[index+AssetManager.STYLE_TYPE]; 452 if (type == TypedValue.TYPE_NULL) { 453 return defValue; 454 } else if (type >= TypedValue.TYPE_FIRST_INT 455 && type <= TypedValue.TYPE_LAST_INT) { 456 return data[index+AssetManager.STYLE_DATA]; 457 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 458 throw new RuntimeException("Failed to resolve attribute at index " + index); 459 } 460 461 throw new UnsupportedOperationException("Can't convert to integer: type=0x" 462 + Integer.toHexString(type)); 463 } 464 465 /** 466 * Retrieve a dimensional unit attribute at <var>index</var>. Unit 467 * conversions are based on the current {@link DisplayMetrics} 468 * associated with the resources this {@link TypedArray} object 469 * came from. 470 * 471 * @param index Index of attribute to retrieve. 472 * @param defValue Value to return if the attribute is not defined or 473 * not a resource. 474 * 475 * @return Attribute dimension value multiplied by the appropriate 476 * metric, or defValue if not defined. 477 * 478 * @see #getDimensionPixelOffset 479 * @see #getDimensionPixelSize 480 */ 481 public float getDimension(int index, float defValue) { 482 if (mRecycled) { 483 throw new RuntimeException("Cannot make calls to a recycled instance!"); 484 } 485 486 index *= AssetManager.STYLE_NUM_ENTRIES; 487 final int[] data = mData; 488 final int type = data[index+AssetManager.STYLE_TYPE]; 489 if (type == TypedValue.TYPE_NULL) { 490 return defValue; 491 } else if (type == TypedValue.TYPE_DIMENSION) { 492 return TypedValue.complexToDimension( 493 data[index+AssetManager.STYLE_DATA], mMetrics); 494 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 495 throw new RuntimeException("Failed to resolve attribute at index " + index); 496 } 497 498 throw new UnsupportedOperationException("Can't convert to dimension: type=0x" 499 + Integer.toHexString(type)); 500 } 501 502 /** 503 * Retrieve a dimensional unit attribute at <var>index</var> for use 504 * as an offset in raw pixels. This is the same as 505 * {@link #getDimension}, except the returned value is converted to 506 * integer pixels for you. An offset conversion involves simply 507 * truncating the base value to an integer. 508 * 509 * @param index Index of attribute to retrieve. 510 * @param defValue Value to return if the attribute is not defined or 511 * not a resource. 512 * 513 * @return Attribute dimension value multiplied by the appropriate 514 * metric and truncated to integer pixels, or defValue if not defined. 515 * 516 * @see #getDimension 517 * @see #getDimensionPixelSize 518 */ 519 public int getDimensionPixelOffset(int index, int defValue) { 520 if (mRecycled) { 521 throw new RuntimeException("Cannot make calls to a recycled instance!"); 522 } 523 524 index *= AssetManager.STYLE_NUM_ENTRIES; 525 final int[] data = mData; 526 final int type = data[index+AssetManager.STYLE_TYPE]; 527 if (type == TypedValue.TYPE_NULL) { 528 return defValue; 529 } else if (type == TypedValue.TYPE_DIMENSION) { 530 return TypedValue.complexToDimensionPixelOffset( 531 data[index+AssetManager.STYLE_DATA], mMetrics); 532 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 533 throw new RuntimeException("Failed to resolve attribute at index " + index); 534 } 535 536 throw new UnsupportedOperationException("Can't convert to dimension: type=0x" 537 + Integer.toHexString(type)); 538 } 539 540 /** 541 * Retrieve a dimensional unit attribute at <var>index</var> for use 542 * as a size in raw pixels. This is the same as 543 * {@link #getDimension}, except the returned value is converted to 544 * integer pixels for use as a size. A size conversion involves 545 * rounding the base value, and ensuring that a non-zero base value 546 * is at least one pixel in size. 547 * 548 * @param index Index of attribute to retrieve. 549 * @param defValue Value to return if the attribute is not defined or 550 * not a resource. 551 * 552 * @return Attribute dimension value multiplied by the appropriate 553 * metric and truncated to integer pixels, or defValue if not defined. 554 * 555 * @see #getDimension 556 * @see #getDimensionPixelOffset 557 */ 558 public int getDimensionPixelSize(int index, int defValue) { 559 if (mRecycled) { 560 throw new RuntimeException("Cannot make calls to a recycled instance!"); 561 } 562 563 index *= AssetManager.STYLE_NUM_ENTRIES; 564 final int[] data = mData; 565 final int type = data[index+AssetManager.STYLE_TYPE]; 566 if (type == TypedValue.TYPE_NULL) { 567 return defValue; 568 } else if (type == TypedValue.TYPE_DIMENSION) { 569 return TypedValue.complexToDimensionPixelSize( 570 data[index+AssetManager.STYLE_DATA], mMetrics); 571 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 572 throw new RuntimeException("Failed to resolve attribute at index " + index); 573 } 574 575 throw new UnsupportedOperationException("Can't convert to dimension: type=0x" 576 + Integer.toHexString(type)); 577 } 578 579 /** 580 * Special version of {@link #getDimensionPixelSize} for retrieving 581 * {@link android.view.ViewGroup}'s layout_width and layout_height 582 * attributes. This is only here for performance reasons; applications 583 * should use {@link #getDimensionPixelSize}. 584 * 585 * @param index Index of the attribute to retrieve. 586 * @param name Textual name of attribute for error reporting. 587 * 588 * @return Attribute dimension value multiplied by the appropriate 589 * metric and truncated to integer pixels. 590 */ 591 public int getLayoutDimension(int index, String name) { 592 if (mRecycled) { 593 throw new RuntimeException("Cannot make calls to a recycled instance!"); 594 } 595 596 index *= AssetManager.STYLE_NUM_ENTRIES; 597 final int[] data = mData; 598 final int type = data[index+AssetManager.STYLE_TYPE]; 599 if (type >= TypedValue.TYPE_FIRST_INT 600 && type <= TypedValue.TYPE_LAST_INT) { 601 return data[index+AssetManager.STYLE_DATA]; 602 } else if (type == TypedValue.TYPE_DIMENSION) { 603 return TypedValue.complexToDimensionPixelSize( 604 data[index+AssetManager.STYLE_DATA], mMetrics); 605 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 606 throw new RuntimeException("Failed to resolve attribute at index " + index); 607 } 608 609 throw new RuntimeException(getPositionDescription() 610 + ": You must supply a " + name + " attribute."); 611 } 612 613 /** 614 * Special version of {@link #getDimensionPixelSize} for retrieving 615 * {@link android.view.ViewGroup}'s layout_width and layout_height 616 * attributes. This is only here for performance reasons; applications 617 * should use {@link #getDimensionPixelSize}. 618 * 619 * @param index Index of the attribute to retrieve. 620 * @param defValue The default value to return if this attribute is not 621 * default or contains the wrong type of data. 622 * 623 * @return Attribute dimension value multiplied by the appropriate 624 * metric and truncated to integer pixels. 625 */ 626 public int getLayoutDimension(int index, int defValue) { 627 if (mRecycled) { 628 throw new RuntimeException("Cannot make calls to a recycled instance!"); 629 } 630 631 index *= AssetManager.STYLE_NUM_ENTRIES; 632 final int[] data = mData; 633 final int type = data[index+AssetManager.STYLE_TYPE]; 634 if (type >= TypedValue.TYPE_FIRST_INT 635 && type <= TypedValue.TYPE_LAST_INT) { 636 return data[index+AssetManager.STYLE_DATA]; 637 } else if (type == TypedValue.TYPE_DIMENSION) { 638 return TypedValue.complexToDimensionPixelSize( 639 data[index+AssetManager.STYLE_DATA], mMetrics); 640 } 641 642 return defValue; 643 } 644 645 /** 646 * Retrieve a fractional unit attribute at <var>index</var>. 647 * 648 * @param index Index of attribute to retrieve. 649 * @param base The base value of this fraction. In other words, a 650 * standard fraction is multiplied by this value. 651 * @param pbase The parent base value of this fraction. In other 652 * words, a parent fraction (nn%p) is multiplied by this 653 * value. 654 * @param defValue Value to return if the attribute is not defined or 655 * not a resource. 656 * 657 * @return Attribute fractional value multiplied by the appropriate 658 * base value, or defValue if not defined. 659 */ 660 public float getFraction(int index, int base, int pbase, float defValue) { 661 if (mRecycled) { 662 throw new RuntimeException("Cannot make calls to a recycled instance!"); 663 } 664 665 index *= AssetManager.STYLE_NUM_ENTRIES; 666 final int[] data = mData; 667 final int type = data[index+AssetManager.STYLE_TYPE]; 668 if (type == TypedValue.TYPE_NULL) { 669 return defValue; 670 } else if (type == TypedValue.TYPE_FRACTION) { 671 return TypedValue.complexToFraction( 672 data[index+AssetManager.STYLE_DATA], base, pbase); 673 } else if (type == TypedValue.TYPE_ATTRIBUTE) { 674 throw new RuntimeException("Failed to resolve attribute at index " + index); 675 } 676 677 throw new UnsupportedOperationException("Can't convert to fraction: type=0x" 678 + Integer.toHexString(type)); 679 } 680 681 /** 682 * Retrieve the resource identifier for the attribute at 683 * <var>index</var>. Note that attribute resource as resolved when 684 * the overall {@link TypedArray} object is retrieved. As a 685 * result, this function will return the resource identifier of the 686 * final resource value that was found, <em>not</em> necessarily the 687 * original resource that was specified by the attribute. 688 * 689 * @param index Index of attribute to retrieve. 690 * @param defValue Value to return if the attribute is not defined or 691 * not a resource. 692 * 693 * @return Attribute resource identifier, or defValue if not defined. 694 */ 695 public int getResourceId(int index, int defValue) { 696 if (mRecycled) { 697 throw new RuntimeException("Cannot make calls to a recycled instance!"); 698 } 699 700 index *= AssetManager.STYLE_NUM_ENTRIES; 701 final int[] data = mData; 702 if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) { 703 final int resid = data[index+AssetManager.STYLE_RESOURCE_ID]; 704 if (resid != 0) { 705 return resid; 706 } 707 } 708 return defValue; 709 } 710 711 /** 712 * Retrieve the theme attribute resource identifier for the attribute at 713 * <var>index</var>. 714 * 715 * @param index Index of attribute to retrieve. 716 * @param defValue Value to return if the attribute is not defined or not a 717 * resource. 718 * @return Theme attribute resource identifier, or defValue if not defined. 719 * @hide 720 */ 721 public int getThemeAttributeId(int index, int defValue) { 722 if (mRecycled) { 723 throw new RuntimeException("Cannot make calls to a recycled instance!"); 724 } 725 726 index *= AssetManager.STYLE_NUM_ENTRIES; 727 final int[] data = mData; 728 if (data[index + AssetManager.STYLE_TYPE] == TypedValue.TYPE_ATTRIBUTE) { 729 return data[index + AssetManager.STYLE_DATA]; 730 } 731 return defValue; 732 } 733 734 /** 735 * Retrieve the Drawable for the attribute at <var>index</var>. This 736 * gets the resource ID of the selected attribute, and uses 737 * {@link Resources#getDrawable Resources.getDrawable} of the owning 738 * Resources object to retrieve its Drawable. 739 * 740 * @param index Index of attribute to retrieve. 741 * 742 * @return Drawable for the attribute, or null if not defined. 743 */ 744 public Drawable getDrawable(int index) { 745 if (mRecycled) { 746 throw new RuntimeException("Cannot make calls to a recycled instance!"); 747 } 748 749 final TypedValue value = mValue; 750 if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { 751 if (value.type == TypedValue.TYPE_ATTRIBUTE) { 752 throw new RuntimeException("Failed to resolve attribute at index " + index); 753 } 754 return mResources.loadDrawable(value, value.resourceId, mTheme); 755 } 756 return null; 757 } 758 759 /** 760 * Retrieve the CharSequence[] for the attribute at <var>index</var>. 761 * This gets the resource ID of the selected attribute, and uses 762 * {@link Resources#getTextArray Resources.getTextArray} of the owning 763 * Resources object to retrieve its String[]. 764 * 765 * @param index Index of attribute to retrieve. 766 * 767 * @return CharSequence[] for the attribute, or null if not defined. 768 */ 769 public CharSequence[] getTextArray(int index) { 770 if (mRecycled) { 771 throw new RuntimeException("Cannot make calls to a recycled instance!"); 772 } 773 774 final TypedValue value = mValue; 775 if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { 776 return mResources.getTextArray(value.resourceId); 777 } 778 return null; 779 } 780 781 /** 782 * Retrieve the raw TypedValue for the attribute at <var>index</var>. 783 * 784 * @param index Index of attribute to retrieve. 785 * @param outValue TypedValue object in which to place the attribute's 786 * data. 787 * 788 * @return Returns true if the value was retrieved, else false. 789 */ 790 public boolean getValue(int index, TypedValue outValue) { 791 if (mRecycled) { 792 throw new RuntimeException("Cannot make calls to a recycled instance!"); 793 } 794 795 return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue); 796 } 797 798 /** 799 * Returns the type of attribute at the specified index. 800 * 801 * @param index Index of attribute whose type to retrieve. 802 * @return Attribute type. 803 */ 804 public int getType(int index) { 805 if (mRecycled) { 806 throw new RuntimeException("Cannot make calls to a recycled instance!"); 807 } 808 809 index *= AssetManager.STYLE_NUM_ENTRIES; 810 return mData[index + AssetManager.STYLE_TYPE]; 811 } 812 813 /** 814 * Determines whether there is an attribute at <var>index</var>. 815 * 816 * @param index Index of attribute to retrieve. 817 * 818 * @return True if the attribute has a value, false otherwise. 819 */ 820 public boolean hasValue(int index) { 821 if (mRecycled) { 822 throw new RuntimeException("Cannot make calls to a recycled instance!"); 823 } 824 825 index *= AssetManager.STYLE_NUM_ENTRIES; 826 final int[] data = mData; 827 final int type = data[index+AssetManager.STYLE_TYPE]; 828 return type != TypedValue.TYPE_NULL; 829 } 830 831 /** 832 * Retrieve the raw TypedValue for the attribute at <var>index</var> 833 * and return a temporary object holding its data. This object is only 834 * valid until the next call on to {@link TypedArray}. 835 * 836 * @param index Index of attribute to retrieve. 837 * 838 * @return Returns a TypedValue object if the attribute is defined, 839 * containing its data; otherwise returns null. (You will not 840 * receive a TypedValue whose type is TYPE_NULL.) 841 */ 842 public TypedValue peekValue(int index) { 843 if (mRecycled) { 844 throw new RuntimeException("Cannot make calls to a recycled instance!"); 845 } 846 847 final TypedValue value = mValue; 848 if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) { 849 return value; 850 } 851 return null; 852 } 853 854 /** 855 * Returns a message about the parser state suitable for printing error messages. 856 */ 857 public String getPositionDescription() { 858 if (mRecycled) { 859 throw new RuntimeException("Cannot make calls to a recycled instance!"); 860 } 861 862 return mXml != null ? mXml.getPositionDescription() : "<internal>"; 863 } 864 865 /** 866 * Recycle the TypedArray, to be re-used by a later caller. After calling 867 * this function you must not ever touch the typed array again. 868 */ 869 public void recycle() { 870 if (mRecycled) { 871 throw new RuntimeException(toString() + " recycled twice!"); 872 } 873 874 mRecycled = true; 875 mResources = null; 876 mMetrics = null; 877 mAssets = null; 878 879 // These may have been set by the client. 880 mXml = null; 881 mTheme = null; 882 883 synchronized (mPool) { 884 mPool.release(this); 885 } 886 } 887 888 /** 889 * Extracts theme attributes from a typed array for later resolution using 890 * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}. 891 * Removes the entries from the typed array so that subsequent calls to typed 892 * getters will return the default value without crashing. 893 * 894 * @return an array of length {@link #getIndexCount()} populated with theme 895 * attributes, or null if there are no theme attributes in the typed 896 * array 897 * @hide 898 */ 899 public int[] extractThemeAttrs() { 900 if (mRecycled) { 901 throw new RuntimeException("Cannot make calls to a recycled instance!"); 902 } 903 904 int[] attrs = null; 905 906 final int[] data = mData; 907 final int N = length(); 908 for (int i = 0; i < N; i++) { 909 final int index = i * AssetManager.STYLE_NUM_ENTRIES; 910 if (data[index + AssetManager.STYLE_TYPE] != TypedValue.TYPE_ATTRIBUTE) { 911 continue; 912 } 913 914 // Null the entry so that we can safely call getZzz(). 915 data[index + AssetManager.STYLE_TYPE] = TypedValue.TYPE_NULL; 916 917 final int attr = data[index + AssetManager.STYLE_DATA]; 918 if (attr == 0) { 919 // This attribute is useless! 920 continue; 921 } 922 923 if (attrs == null) { 924 attrs = new int[N]; 925 } 926 attrs[i] = attr; 927 } 928 929 return attrs; 930 } 931 932 /** 933 * Return a mask of the configuration parameters for which the values in 934 * this typed array may change. 935 * 936 * @return Returns a mask of the changing configuration parameters, as 937 * defined by {@link android.content.pm.ActivityInfo}. 938 * @see android.content.pm.ActivityInfo 939 */ 940 public int getChangingConfigurations() { 941 int changingConfig = 0; 942 943 final int[] data = mData; 944 final int N = length(); 945 for (int i = 0; i < N; i++) { 946 final int index = i * AssetManager.STYLE_NUM_ENTRIES; 947 final int type = data[index + AssetManager.STYLE_TYPE]; 948 if (type == TypedValue.TYPE_NULL) { 949 continue; 950 } 951 changingConfig |= data[index + AssetManager.STYLE_CHANGING_CONFIGURATIONS]; 952 } 953 return changingConfig; 954 } 955 956 private boolean getValueAt(int index, TypedValue outValue) { 957 final int[] data = mData; 958 final int type = data[index+AssetManager.STYLE_TYPE]; 959 if (type == TypedValue.TYPE_NULL) { 960 return false; 961 } 962 outValue.type = type; 963 outValue.data = data[index+AssetManager.STYLE_DATA]; 964 outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; 965 outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID]; 966 outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]; 967 outValue.density = data[index+AssetManager.STYLE_DENSITY]; 968 outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null; 969 return true; 970 } 971 972 private CharSequence loadStringValueAt(int index) { 973 final int[] data = mData; 974 final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE]; 975 if (cookie < 0) { 976 if (mXml != null) { 977 return mXml.getPooledString( 978 data[index+AssetManager.STYLE_DATA]); 979 } 980 return null; 981 } 982 return mAssets.getPooledStringForCookie(cookie, data[index+AssetManager.STYLE_DATA]); 983 } 984 985 /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) { 986 mResources = resources; 987 mMetrics = mResources.getDisplayMetrics(); 988 mAssets = mResources.getAssets(); 989 mData = data; 990 mIndices = indices; 991 mLength = len; 992 } 993 994 @Override 995 public String toString() { 996 return Arrays.toString(mData); 997 } 998} 999