Análisis del proceso de creación de View en Android

        En el método onCreate de AppCompatActivity, todos sabemos que el método setContentView es para cargar el archivo de diseño. Este método es muy simple de usar, solo coloque el archivo de diseño de diseño en él. Entonces, ¿cómo se muestra internamente en el escritorio?Hoy analizaré el proceso de creación de View en Android desde setContentView().

Abra androidx/appcompat/app/AppCompatActivity.java, donde setContentView es solo una línea de código, llamando a setContentView() de AppCompatDelegate.

    @Override
    public void setContentView(@LayoutRes int layoutResID) {
        getDelegate().setContentView(layoutResID);
    }

AppCompatDelegate es una clase abstracta, analicemos qué hace su clase de implementación AppCompatDelegateImpl en el método setContentView().

@Override
    public void setContentView(int resId) {
        ensureSubDecor();
        //获取content视图
        ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
        //移除viewGroup下的所有子View
        contentParent.removeAllViews();
        //解析布局文件添加到content视图中
        LayoutInflater.from(mContext).inflate(resId, contentParent);
        mAppCompatWindowCallback.getWrapped().onContentChanged();
    }

Abra /frameworks/base/core/java/android/view/LayoutInflater.java para ver el método LayoutInflater.inflate ( ) que analiza el archivo de diseño :

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
        ...
        //获取布局解析器
        XmlResourceParser parser = res.getLayout(resource);
        try {
            return inflate(parser, root, attachToRoot);
        } finally {
            parser.close();
        }
    }

...

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");

            final Context inflaterContext = mContext;
            final AttributeSet attrs = Xml.asAttributeSet(parser);
            Context lastContext = (Context) mConstructorArgs[0];
            mConstructorArgs[0] = inflaterContext;
            View result = root;

            try {
                advanceToRootNode(parser);
                //取得XML标签名字
                final String name = parser.getName();
            
                if (TAG_MERGE.equals(name)) {
                    //如果是merge标签调用rInflate方法,递归执行解析
                    rInflate(parser, root, inflaterContext, attrs, false);
                } else {
                    // 通过xml的tag来构建View
                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);

                    ViewGroup.LayoutParams params = null;
                    if (root != null) {
                         //获取布局参数
                        params = root.generateLayoutParams(attrs);
                        if (!attachToRoot) {
                            //设置临时的布局参数
                            temp.setLayoutParams(params);
                        }
                    }
                    //递归执行解析,继续解析temp下的子项,也走到了rInflate()方法
                    rInflateChildren(parser, temp, attrs, true);
                    // 如果root不为空并且attachToRoot为true
                    if (root != null && attachToRoot) {
                        //将View填充到ViewGroup
                        root.addView(temp, params);
                    }

                    // 如果root为空 或者 attachToRoot为false,直接返回temp
                    if (root == null || !attachToRoot) {
                        result = temp;
                    }
                }

            } catch (XmlPullParserException e) {
              ...
            }

            return result;
        }
    }

En el método de inflado, se realizan principalmente los siguientes pasos:

1. Analice la etiqueta raíz del archivo layout.xml

2. Determine si se trata de una combinación.Si se trata de una combinación, llame al método recursivo rInflate(), cree una instancia de la vista, agregue la Vista al ViewGroup y luego llame a onFinishInflate().

3. Si no es una combinación, llame a createViewFromTag() para crear una vista y agréguela al ViewGroup, luego llame a rInflate() para continuar analizando recursivamente la subvista;

4. A través de attachToRoot, regrese a la vista.

El método rInflate también llama al método createViewFromTag para abrir createViewFromTag():

 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        ...

        try {
            View view = tryCreateView(parent, name, context, attrs);

            if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                     //通过.来判断是自定义View还是内置View
                    if (-1 == name.indexOf('.')) {
                        //创建内置View
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        //创建自定义View
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;
                }
            }
            return view;
        } ...
          
    }
Al continuar rastreando el código, se encuentra que la vista personalizada sigue siendo una vista integrada y, finalmente, se llama al método createView:
@Nullable
    public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Objects.requireNonNull(viewContext);
        Objects.requireNonNull(name);
        //从缓存中获取构造
        Constructor<? extends View> constructor = sConstructorMap.get(name);
        if (constructor != null && !verifyClassLoader(constructor)) {
            constructor = null;
            sConstructorMap.remove(name);
        }
        Class<? extends View> clazz = null;

        try {

            if (constructor == null) {
                //类载入器, 初始化对象
                clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
                        mContext.getClassLoader()).asSubclass(View.class);

                if (mFilter != null && clazz != null) {
                    boolean allowed = mFilter.onLoadClass(clazz);
                    if (!allowed) {
                        failNotAllowed(name, prefix, viewContext, attrs);
                    }
                }
                //从class中获取构造
                constructor = clazz.getConstructor(mConstructorSignature);
                constructor.setAccessible(true);
                //加入缓存中
                sConstructorMap.put(name, constructor);
            } else {
                ...
            }

            Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = viewContext;
            Object[] args = mConstructorArgs;
            args[1] = attrs;

            try {
                //设置attrs属性,通过反射获取View实例
                final View view = constructor.newInstance(args);
                if (view instanceof ViewStub) {
                    // Use the same context when inflating ViewStub later.
                    final ViewStub viewStub = (ViewStub) view;
                    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
                }
                return view;
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        } 
        ...
    }
  • Resumir:

  • Obtenga la etiqueta de Vista analizando el archivo XML a través de extracción;
  • Si está en la etiqueta Determinar si es una vista personalizada o una vista integrada
  • Cree un objeto Vista por reflexión;
  • Atraviese la Vista en primer orden de profundidad para formar un árbol de Vista;

Supongo que te gusta

Origin blog.csdn.net/weixin_43858011/article/details/124584373
Recomendado
Clasificación