View的tag方法可以让View附带很多信息,方便使用。最常用的一个例子:在Adapter中我们经常用到View的setTag()和getTag()方法,从而使用ViewHolder来提高效率。

问题1:当我们需要添加多个tag时该怎么做呢?

View还有一个setTag(int key, final Object tag)方法,想添加多少tag都行。

问题2:直接调用方法View.setTag(1,"msg"),出现异常:

java.lang.IllegalArgumentException: The key must be an application-specific resource id.

异常信息的意思是说:key必须是特定的资源id。

最直接的方法就是在资源文件中添加一条记录:

<item type="id" name="tag_first"></item>

可以添加到res/values/strings.xml或者res/values/ids.xml中,然后调用View.setTag(R.id.tag_first,"msg")即可。

知道解决方法了,再来找一下原因。以下是View中相关源码:

/**
     * Returns the tag associated with this view and the specified key.
     *
     * @param key The key identifying the tag
     *
     * @return the Object stored in this view as a tag
     *
     * @see# setTag(int, Object)
     * @see# getTag()
     */
    public Object getTag(int key) {
        if (mKeyedTags != null) return mKeyedTags.get(key);
        return null;
    }

    /**
     * Sets a tag associated with this view and a key. A tag can be used
     * to mark a view in its hierarchy and does not have to be unique within
     * the hierarchy. Tags can also be used to store data within a view
     * without resorting to another data structure.
     *
     * The specified key should be an id declared in the resources of the
     * application to ensure it is unique (see the <a
     * href={@docRoot}guide/topics/resources/more-resources.html# Id">ID resource type</a>).
     * Keys identified as belonging to
     * the Android framework or not associated with any package will cause
     * an {@link IllegalArgumentException} to be thrown.
     *
     * @param key The key identifying the tag
     * @param tag An Object to tag the view with
     *
     * @throws IllegalArgumentException If they specified key is not valid
     *
     * @see# setTag(Object)
     * @see# getTag(int)
     */
    public void setTag(int key, final Object tag) {
        // If the package id is 0x00 or 0x01, it's either an undefined package
        // or a framework id
        if ((key >>> 24) < 2) {
            throw new IllegalArgumentException("The key must be an application-specific "
                    + "resource id.");
        }

        setKeyedTag(key, tag);
    }

从源码中可以看到,key右移24位如果小于2就会抛出这个异常,这个应该和id的生成规则有关。

这样来看key也是可以定义成一个常量的(测试了也确实可以),只要保证这个移位大于2的规则就行。但是这样写不便于维护,只是说是可行的,不建议这边做。

从源码中注意到的另外一个问题是,setTag()中的tag是单独存储的,保存在protected Object mTag;中,而setTag(int key, final Object tag)的tags是保存在private SparseArray<Object> mKeyedTags;中的。

问题3:Android中或者说ADT关于R文件中的资源id的值是如何生成的?

更新:2013-09-24

这个问题从http://www.apkbus.com/android-55911-1-1.html看到了答案:

在Android中资源的使用几乎无处不在,layout、string、drawable、raw、style、theme等等都是。这些资源会在编译过程中被打包进APK中(res文件夹)或者被打包成独立的资源APK包(比如framework-res.apk)。但是这些资源都会被赋予独一无二的ID即资源索引来方便系统访问。 这些资源索引由Android的工具AAPT(Android Asset Packing Tool)生成的八位十六进制整数型。

Android id

中间 02 所在位置值代表资源ID对应的资源的类型,分别是:

02:drawable
03:layout
04:values
05:xml
06:raw
07:color
08:menu

PS:分配resource id的主要逻辑实现是在framework/base/tools/aapt/Resource.cpp 和 ResourceTable.cpp