Cuándo se han agregado vistas secundarias a Diseño / Grupo de vistas desde XML

Mi pregunta es: ¿Quiero saber cuándo un xLayout (o ViewGroup en general) agrega una vista secundaria de XML? Y por "cuando" me refiero a qué punto del código, en qué "pasar" de la "transversalidad" de la caja de herramientas de la interfaz de usuario? ¿Qué método de xLayout o ViewGroup debo anular?

He hecho mi tarea: He visto la "Escritura de vistas personalizadas para Android" presentado (por Adam Powell y Romain Guy) en el último I / O de Google y he leído los comentarios de Adam Powell en esta entrada de Google+.

Buscando el punto exacto en el código fuente de Android donde se agregan los niños.

Podemos ver lo que setContentView(R.layout.some_id) está haciendo bajo el capó.

setContentView(int) llamadas PhoneWindow#setContentView(int)PhoneWindow Link es una PhoneWindow concreta de Window :

 @Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } } 

El método LayoutInflater#inflate(layoutResID, mContentParent) eventualmente llama a ViewGroup#addView(View, LayoutParams) en mContentParent . En medio, las vistas de los niños

Quiero saber qué sucede exactamente después de establecer la vista de contenido en un archivo XML que contenga una vista personalizada. Una vez que el constructor tiene que haber una parte en el código en el que la vista personalizada "analizar / leer / inflar / convertir" vistas declaradas por XML a las vistas reales! (Comentario de JohnTube)

Ambiquilidad: De JohnTube el comentario, parece que él está más interesado en saber cómo una opinión de encargo se infla . Para saber esto, tendremos que mirar el funcionamiento de LayoutInflater Link .

Por lo tanto, la respuesta a Which method of xLayout or ViewGroup should I override ? Es ViewGroup#addView(View, LayoutParams) . Tenga en cuenta que, en este punto, la inflación de todas las vistas regulares / personalizadas ya ha tenido lugar.

Inflación de vistas personalizadas:

El siguiente método en LayoutInflater es donde se addView(View, LayoutParams) principal / root:

Nota: La llamada mLayoutInflater.inflate(layoutResID, mContentParent); En las PhoneWindow#setContentView(int) a esto. Aquí mContentParent es el DecorView : la vista que es accesible a través de getWindow().getDecorView() .

 // Inflate a new view hierarchy from the specified XML node. public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) // Recursive method used to descend down the xml hierarchy and instantiate views, // instantiate their children, and then call onFinishInflate(). void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException 

La llamada de interés en este método (y en el rInflate(XmlPullParser, View, AttributeSet, boolean) recursivo rInflate(XmlPullParser, View, AttributeSet, boolean) ) es:

 temp = createViewFromTag(root, name, attrs); 

Vamos a ver lo que createViewFromTag(...) está haciendo:

 View createViewFromTag(View parent, String name, AttributeSet attrs) { .... .... if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } } .... } 

El period(.) Decide si onCreateView(...) o createView(...) se llama.

¿Por qué este cheque? Debido a que una View definida en android.view , android.widget o android.webkit se accede a través del nombre de su clase. Por ejemplo:

 android.widget: Button, TextView etc. android.view: ViewStub. SurfaceView, TextureView etc. android.webkit: WebView 

Cuando se encuentran estas vistas, onCreateView(parent, name, attrs) se llama. Este método en realidad se encadena para createView(...) :

 protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { return createView(name, "android.view.", attrs); } 

Esto trataría con SurfaceView , TextureView y otras vistas definidas en el paquete android.view . Si está interesado en saber cómo se TextView, Button etc. , mire PhoneLayoutInflater Link – extiende LayoutInflater y reemplaza onCreateView(...) para comprobar si android.widget y android.webkit son los nombres de paquetes deseados. De hecho, la llamada getLayoutInflater() obtiene una instancia de PhoneLayoutInflater . Esta es la razón por la cual si tuvieras que subclasificar LayoutInflater , ni siquiera podrías inflar el diseño más sencillo, ya que LayoutInflater sólo puede tratar las vistas desde el paquete android.view .

De todos modos, divago. Este bit extra sucede para las vistas regulares – que no tienen un period(.) En su definición. Las vistas personalizadas tienen un punto en sus nombres: com.my.package.CustomView . Así es como el LayoutInflater distingue entre los dos .

Por lo tanto, en el caso de una vista regular (por ejemplo, Button), un prefix como android.widget pasará como el segundo argumento – para las vistas personalizadas, esto será null . El prefix se utiliza junto con el name para obtener el constructor para la clase de esa vista en particular. Las vistas personalizadas no necesitan esto porque su name ya está completo. Supongo que esto se ha hecho por conveniencia. De lo contrario, habrías estado definiendo tus diseños de esta manera:

 <android.widget.LinearLayout ... ... /> 

(Su legal aunque …)

Además, es por eso que las vistas procedentes de una biblioteca de soporte (por ejemplo, <android.support.v4.widget.DrawerLayout … />) tienen que utilizar nombres totalmente calificados.

Por cierto, si querías escribir tus diseños como:

 <MyCustomView ../> 

Todo lo que tienes que hacer es extender LayoutInflater y agregar el nombre del paquete com.my.package. A la lista de cadenas que se comprueban durante la inflación. Compruebe PhoneLayoutInflater para obtener ayuda con esto.

Veamos qué sucede en la etapa final para las vistas personalizada y regular – createView(...) :

 public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { // Try looking for the constructor in cache Constructor<? extends View> constructor = sConstructorMap.get(name); Class<? extends View> clazz = null; try { if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); .... // Get constructor constructor = clazz.getConstructor(mConstructorSignature); sConstructorMap.put(name, constructor); } else { .... } Object[] args = mConstructorArgs; args[1] = attrs; // Obtain an instance final View view = constructor.newInstance(args); .... // We finally have a view! return view; } // A bunch of catch blocks: - if the only constructor defined is `CustomView(Context)` - NoSuchMethodException - if `com.my.package.CustomView` doesn't extend View - ClassCastException - if `com.my.package.CustomView` is not found - ClassNotFoundException // All these catch blocks throw the often seen `InflateException`. } 

… nace una View .

Si está hablando de un ViewGroup definido en XML, se agrega a sus hijos cuando la vista se infla. Esto puede ocurrir cuando se infla explícitamente con un LayoutInflater o cuando se establece la vista de contenido de una actividad. (Probablemente hay algunas otras veces también, especialmente si está usando vistas de stub.)

Si desea agregar a los niños a un ViewGroup que no esté inflado, puede hacerlo en el constructor de la vista.

EDIT: si desea ver cómo se agregan los niños cuando se infla una vista, esto ocurre en la llamada a LayoutInflater.inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) . La fuente de android.view.LayoutInflater está incluida en las distribuciones de SDK de Android; Las versiones en línea se pueden encontrar en muchos lugares ( aquí en GrepCode , por ejemplo). Este método termina siendo llamado cuando, por ejemplo, se llama a setContentView(int) para una Activity o cuando se infla explícitamente un recurso de diseño.

Los niños realmente se agregan en la llamada a rInflate(parser, root, attrs, false); ("Inflar recursivo"), que podría ser llamado desde un par de lugares diferentes en el método inflate inflate() , dependiendo de lo que el inflado encontrado como la etiqueta raíz. Usted puede rastrear a través de la lógica de código usted mismo. Un punto interesante es que un niño no se agrega a su padre hasta que sus propios hijos han sido inflados recursivamente y agregados a ella.

El otro método interesante, utilizado por ambos rInflate y rInflate , es createViewFromTag . Esto puede depender de un LayoutInflater.Factory (o .Factory2 objeto .Factory2 ) para crear la vista, o puede terminar llamando a createView . Allí se puede ver cómo se realiza la llamada al constructor de dos argumentos de la vista ( (Context context, AttributeSet attrs) ).

  • IPhone como cabezal deslizante, o: ¿cómo forzar el redibujo inmediato en el cambio de margen superior?
  • Cambiar el tamaño de los diferentes caracteres en la vista de texto, Android
  • Cómo importar conjunto de iconos en el proyecto de Android Studio
  • Creación de un botón de retroceso personalizado en android
  • ¿Cómo puedo obtener botones de acción con diseños personalizados para que se llamen como botones de acción estándar en Android 3.0+
  • Fragment onActivityCreated () se llama después de onDestroy () de Actividad
  • Diseño del menú emergente en Android 5.0
  • android - vista de panel dual con indicador para el elemento de lista seleccionado
  • Reemplace el título de la barra de acción con una ruleta (desplegable)
  • Construir Reproducir Interfaz similar a la música
  • ¿Puede Android WebView HTML influenciar las características del teclado o del teclado?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.