在Android开发中如果出现android.content.res.Resources$NotFoundException: String resource ID #0x1这样的错误,你想也不用想,一定是Textview控件显示数据出了问题:mTextview.setText(这里的传入的数据一定写成int类型了)。我们需要做的是eg:mTextview.setText(1+""),也就是参数转化成字符串,接下来我们看一源码:
如果控件TextView添加的数据写成 eg:mTextview.setText(1)时,也就是传入的是数字,源码分析如下:
/** * Sets the text to be displayed using a string resource identifier. * * @param resid the resource identifier of the string resource to be displayed * * @see #setText(CharSequence) * * @attr ref android.R.styleable#TextView_text */ @android.view.RemotableViewMethod public final void setText(@StringRes int resid) { setText(getContext().getResources().getText(resid)); mTextFromResource = true; } |
从该方法的注释我们可以看到参数 resid要是一个string的类型。接着我们往下看getText():
/** * Return the string value associated with a particular resource ID. The * returned object will be a String if this is a plain string; it will be * some other type of CharSequence if it is styled. * {@more} * * @param id The desired resource identifier, as generated by the aapt * tool. This integer encodes the package, type, and resource * entry. The value 0 is an invalid identifier. * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. * * @return CharSequence The string data associated with the resource, plus * possibly styled text information. */ @NonNull public CharSequence getText(@StringRes int id) throws NotFoundException { CharSequence res = mResourcesImpl.getAssets().getResourceText(id); if (res != null) { return res; } throw new NotFoundException("String resource ID #0x" + Integer.toHexString(id)); } |
上面的意思大概是:返回与特定资源ID相关联的字符串值。返回的对象将是一个字符串,如果这是一个普通的字符串;@param id :所需的资源标识符,由aapt工具生成。这个整数编码了包、类型和资源条目,值0是无效标识符。接着继续往下看类AssetManager:
/** * Retrieves the string value associated with a particular resource * identifier for the current configuration. *检索与特定资源相关联的字符串值当前配置的标识符。 * @param resId the resource identifier to load * @return the string value, or {@code null} */ @Nullable final CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { return outValue.coerceToString(); } return null; } } |
返回字符序列CharSequence。那么是如何 检索与特定资源相关联的字符串 值。
继续看源码:
/** * Populates {@code outValue} with the data associated a particular * resource identifier for the current configuration. * * @param resId the resource identifier to load * @param densityDpi the density bucket for which to load the resource * @param outValue the typed value in which to put the data * @param resolveRefs {@code true} to resolve references, {@code false} * to leave them unresolved * @return {@code true} if the data was loaded into {@code outValue}, * {@code false} otherwise */ final boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue, boolean resolveRefs) { synchronized (this) { final int block = loadResourceValue(resId, (short) densityDpi, outValue, resolveRefs); if (block < 0) { return false; } // Convert the changing configurations flags populated by native code. outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava( outValue.changingConfigurations); if (outValue.type == TypedValue.TYPE_STRING) { outValue.string = mStringBlocks[block].get(outValue.data); } return true; } } /** Returns true if the resource was found, filling in mRetStringBlock and * mRetData. */ private native final int loadResourceValue(int ident, short density, TypedValue outValue, boolean resolve); |
如果找到对应的资源,则返回true。在这里我们也看到了native方法,
肯定是c/c++去做事了,我们不去考虑。如果找不到返回null,
我们从如下代码就可以知道:
@Nullable final CharSequence getResourceText(@StringRes int resId) { synchronized (this) { final TypedValue outValue = mValue; if (getResourceValue(resId, 0, outValue, true)) { return outValue.coerceToString(); } return null; } } |
如果找到对应的资源,我们就经过一系列的方法转化成字符序列,
代码如下:
/** * Regardless of the actual type of the value, try to convert it to a * string value. For example, a color type will be converted to a * string of the form #aarrggbb. * * @return CharSequence The coerced string value. If the value is * null or the type is not known, null is returned. */ public final CharSequence coerceToString() { int t = type; if (t == TYPE_STRING) { return string; } return coerceToString(t, data); }2.------------------------------------------ /** * Perform type conversion as per {@link #coerceToString()} on an * explicitly supplied type and data. * * @param type The data type identifier. * @param data The data value. * * @return String The coerced string value. If the value is * null or the type is not known, null is returned. */ public static final String coerceToString(int type, int data) { switch (type) { case TYPE_NULL: return null; case TYPE_REFERENCE: return "@" + data; case TYPE_ATTRIBUTE: return "?" + data; case TYPE_FLOAT: return Float.toString(Float.intBitsToFloat(data)); case TYPE_DIMENSION: return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[ (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; case TYPE_FRACTION: return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[ (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK]; case TYPE_INT_HEX: return "0x" + Integer.toHexString(data); case TYPE_INT_BOOLEAN: return data != 0 ? "true" : "false"; } if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) { return "#" + Integer.toHexString(data); } else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) { return Integer.toString(data); } return null; }........................... |
如果控件TextView添加的数据写成 eg:mTextview.setText(1+"")时,也就是传入的是字符串,源码分析如下:
next1.
/** * Sets the text to be displayed. TextView <em>does not</em> accept * HTML-like formatting, which you can do with text strings in XML resource files. * To style your strings, attach android.text.style.* objects to a * {@link android.text.SpannableString}, or see the * <a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources"> * Available Resource Types</a> documentation for an example of setting * formatted text in the XML resource file. * <p/> * When required, TextView will use {@link android.text.Spannable.Factory} to create final or * intermediate {@link Spannable Spannables}. Likewise it will use * {@link android.text.Editable.Factory} to create final or intermediate * {@link Editable Editables}. * * @param text text to be displayed * * @attr ref android.R.styleable#TextView_text */ @android.view.RemotableViewMethod public final void setText(CharSequence text) { setText(text, mBufferType); } |
next2.
设置要显示的文本
/** * Sets the text to be displayed and the {@link android.widget.TextView.BufferType}. * <p/> * When required, TextView will use {@link android.text.Spannable.Factory} to create final or * intermediate {@link Spannable Spannables}. Likewise it will use * {@link android.text.Editable.Factory} to create final or intermediate * {@link Editable Editables}. * * @param text text to be displayed * @param type a {@link android.widget.TextView.BufferType} which defines whether the text is * stored as a static text, styleable/spannable text, or editable text * * @see #setText(CharSequence) * @see android.widget.TextView.BufferType * @see #setSpannableFactory(Spannable.Factory) * @see #setEditableFactory(Editable.Factory) * * @attr ref android.R.styleable#TextView_text * @attr ref android.R.styleable#TextView_bufferType */ public void setText(CharSequence text, BufferType type) { setText(text, type, true, 0); if (mCharWrapper != null) { mCharWrapper.mChars = null; } } |
next3.
private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) { mTextFromResource = false; if (text == null) { text = ""; } // If suggestions are not enabled, remove the suggestion spans from the text if (!isSuggestionsEnabled()) { text = removeSuggestionSpans(text); } ............ ................. .........
notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT); if (needEditableForNotification) { sendAfterTextChanged((Editable) text); } else { // Always notify AutoFillManager - it will return right away if autofill is disabled. notifyAutoFillManagerAfterTextChangedIfNeeded(); } // SelectionModifierCursorController depends on textCanBeSelected, which depends on text if (mEditor != null) mEditor.prepareCursorControllers(); |