findViewById()

作者: 图乐 | 来源:发表于2017-01-10 21:59 被阅读1020次

    欢迎访问[Android日记][1],如有转载请注明Android日记 http://androiddiary.site
    2017.1.10 周二 晴 临沂


    写日记

    从findViewById()说

    还记得三年之前学习android就开始使用findViewById,但是用了这么久还没有具体分析过他的实现,今天终于有机会仔细看看。

    1. 在Activity中找到findViewById方法
    /**
     * Finds a view that was identified by the id attribute from the XML that
     * was processed in {@link #onCreate}.
     *
     * @return The view if found or null otherwise.
     */
    public View findViewById(@IdRes int id) {
        return getWindow().findViewById(id);
    }
    
    1. getWindow:
    public Window getWindow() {
        return mWindow;  
    }
    

    mWindow 是 private Window mWindow; 是一个Window类型的变量

    1. 再看Window类中的findViewById方法
    /**
     * Finds a view that was identified by the id attribute from the XML that
     * was processed in {@link android.app.Activity#onCreate}.  This will
     * implicitly call {@link #getDecorView} for you, with all of the
     * associated side-effects.
     *
     * @return The view if found or null otherwise.
     */
    @Nullablepublic View findViewById(@IdRes int id) {
        return getDecorView().findViewById(id);
    }
    
    1. 发现getDecorView()是Window中的一个抽象方法。而Window唯一的子类是PhoneWindow.

    2. 在PhoneWindow找到getDecorView()方法

    @Override
    public final View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }
    

    而mDecor是Phone的一个内部类DecorView

    private final class DecorView extends FrameLayout implements RootViewSurfaceTaker 
    

    它继承了FrameLayout ,而FrameLayout 是ViewGroup的子类

    1. 最终我们在View类中找到了
    /**
     * Look for a child view with the given id.  If this view has the given
     * id, return this view.
     *
     * @param id The id to search for.
     * @return The view that has the given id in the hierarchy or null
     */
    @Nullablepublic final View findViewById(@IdRes int id) {
        if (id < 0) {
            return null;
        }
        return findViewTraversal(id);
    }
    

    在View类的findViewById中调用的方法findViewTraversal只在ViewGroup里面被重写(Override)了。
    我们先看一下ViewGroup中的实现(因为mDecor是ViewGroup的子类会调用ViewGroup的方法)

    @Override
    protected View findViewTraversal(@IdRes int id) {
        if (id == mID) {
            return this;
        }
        final View[] where = mChildren;
        final int len = mChildrenCount;
        for (int i = 0; i < len; i++) {
            View v = where[i];
            if ((v.mPrivateFlags & PFLAG_IS_ROOT_NAMESPACE) == 0) {
                v = v.findViewById(id);
                if (v != null) {
                    return v;
                }
            }
        }
        return null;
    }
    

    简单的说如果当前ViewGroup(或者其子类的)mID == id 为true,也就找到了要查找了View,如果不相等则会去遍历ViewGroup的子View数组,如果在子View的找到就返回;如果遍历完所有的子View都没有找到则返回null。这里的遍历类似于树的先序遍历,
    我们在看一下在View类中的实现

    protected View findViewTraversal(@IdRes int id) {
        if (id == mID) {
            return this;
        }
        return null;
    }
    

    View类似于树的叶子节点,它没有子布局(节点),所以他的遍历很简单,就是简单比较id是否相等,相等就返回当前View,不相等则返回null;

    简单总结

    使用findViewById时,最终会调用ViewGroup中的findViewTraversal,这个方法会遍历所有的子View,形成一个递归查询,找到最末端(View中)。如果找到就会返回这个View并停止查询,如果没找到就会返回为null。在确定要查找的那个View在某个View中的时候,我们调用那个View.findViewById()方法,会减少查询的循环次数,提高效率。

    相关文章

      网友评论

        本文标题:findViewById()

        本文链接:https://www.haomeiwen.com/subject/ckvkbttx.html