关于 findViewById()方法和资源 ID 的重复问题

最近科研压力大,老板希望我把主要精力放在目前的科研课题上,对我找互联网方面工作的想法根本不屑一顾。按照他的说法,科研做得好,毕业他给推荐公司,薪资不知道比自己找工作高到哪里去了。。。虽然确实是这么回事,可是他推荐的都是电动汽车类公司,按照我现在的方向,去了肯定是做电池包热管理,虽然前途可能比较光明,可我特么想去互联网公司写代码啊!所以只能顶着压力偷偷学习了,谁让自己喜欢呢。。

今天从本地迁移一篇自己的思考笔记,可能有些同学想过这些问题但是没深究,一起来看看吧!


经过实践证明,不同的 xml 文件中,资源 ID 是可以出现重复的。且重复的 ID 在 R.java 文件中只会生成一份记录。

之所以允许出现重复,我猜想原因如下:
资源 ID 的存在是为了什么?是为了在布局中定位控件。
所以其实这个步骤是分两步走的:1.定位布局,2.定位控件。
那么只要布局的定位 ID 不出现重复,布局中控件的定位 ID 不出现重复,就是没问题的。而这也正是编码中的两个基本规范:1.xml 文件的文件名不可重复,2.xml 文件中的资源 ID 不可重复。

关于 findViewById() 方法

我们在 Activity 中常常使用的 findViewById()方法,和我们在 adapter 中常常使用的 findViewById()方法,乍一看上去,好像是同一个,其实不是同一个,但最终还真是同一个。有点绕,但看完你就明白了。

我们跟着代码先去看看Activity 中的 findViewById()方法是怎么实现的:

  1. 首先是 AppCompatActivity.java
@Override
    public View findViewById(@IdRes int id) {
        return getDelegate().findViewById(id);
    }

很明显,AppCompatAvtivity 自身没有实现这个方法,而是使用 getDelegate()返回了一个AppCompatDelegate对象,调用了AppCompatDelegate的 findViewById()方法。
跟进去发现AppCompatDelegate是个抽象类,那么这里肯定是向上转型了,AppCompatDelegate必然有一个实现类。所以我们第一反应是要看 getDelegate()方法返回的这个AppCompatDelegate对象是在哪里赋值的。
本以为会有一个 set 方法,结果发现是在 get 方法中直接就有赋值了。如下:
文件名:AppCompatActivity.java

public AppCompatDelegate getDelegate() {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this, this);
        }
        return mDelegate;
    }

那么我们再看看AppCompatDelegate.create(this, this);是个什么鬼就行了嘛。跟进去,发现该方法有三个重载,前两个重载的具体实现都是调用了第三个重载。第三个重载的代码如下:
文件名:AppCompatDelegate.java

    private static AppCompatDelegate create(Context context, Window window,
            AppCompatCallback callback) {
        final int sdk = Build.VERSION.SDK_INT;
        if (BuildCompat.isAtLeastN()) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (sdk >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else if (sdk >= 14) {
            return new AppCompatDelegateImplV14(context, window, callback);
        } else if (sdk >= 11) {
            return new AppCompatDelegateImplV11(context, window, callback);
        } else {
            return new AppCompatDelegateImplV9(context, window, callback);
        }
    }

果然有实现类,而且对不同的 SDK 版本其实现类还不一样。但我们跟进去会发现,上面的 lvN,lv23,lv14,lv11都是继承自 lv9,且没有重写 findViewById()方法。so,我们看看 lv9中是怎么写的吧!
如下:
文件名:AppCompatDelegateImplV9.java

@Nullable
    @Override
    public View findViewById(@IdRes int id) {
        ensureSubDecor();
        return mWindow.findViewById(id);
    }

妈蛋,一层一层还真深,再看一下这个 mWindow 是什么。
如下:
文件名:Window.java

@Nullable
    public View findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }

不用说了,getDecorView()返回的肯定是一个 View 对象。所以最终还是调用了 View 的 findViewById()方法。

而在 Adapter 中,我们则是直接使用 View 的 findViewById()方法。二者也算是殊途同归了。

说到这里其实还是没说清楚资源ID 重复的情况下是如何准确找到控件的,但可以肯定的是,找控件,首先肯定得知道是在哪个 View 中找。

展开阅读全文

没有更多推荐了,返回首页