Generando un LayoutParams basado en el tipo de padre

Me encuentro necesitando para crear una vista completamente en Java sin saber qué tipo de concreto es el padre.

ejemplo:

public View getView(int position, View convertView, ViewGroup parent){ if(null == convertView){ convertView = new TextView(parent.getContext()); } ((TextView) convertView).setText(getItem(position).getName()); } 

Ahora supongamos que quería cambiar esto para que convertView fuera wrap_content en ambas direcciones. Dado que este es un adaptador, me gustaría evitar el acoplamiento del adaptador con el tipo de concreto del padre, pero LayoutParams lo doy en setLayoutParams () tiene que ser el tipo de concreto correcto de lo contrario la aplicación se bloquea (es decir, si el padre es Un ListView tiene que ser ListView.LayoutParams, si es un LinearLayout debe ser un LinearLayout.LayoutParams, etc.). No quiero usar una declaración de conmutación, ya que es sólo una forma más flexible de acoplamiento, y si adjunto este adaptador a una vista no esperaba que todavía terminan con un accidente. ¿Hay una manera genérica de hacer esto?

Puede hacerlo utilizando el código siguiente:

 LayoutParams params = parent.generateLayoutParams(null); 

EDIT : El método anterior no funciona porque ViewGroup.generateLayoutParams() requiere android:layout_width y android:layout_height que se establecerá en el AttributeSet pasado.

Si utiliza ViewGroup.LayoutParams con cualquier diseño, entonces todo funcionará bien. Pero si utiliza LinearLayout.LayoutParams con RelativeLayout por ejemplo, se lanzará una excepción.

EDIT : Hay una solución de trabajo para este problema que no me gusta. La solución es llamar a generateLayoutParams() con AttributeSet válido. Puede crear un objeto AttributeSet utilizando al menos dos enfoques diferentes. Uno de ellos lo he implementado:

Res \ layout \ params.xml :

 <?xml version="1.0" encoding="utf-8"?> <view xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="20dip" /> 

SomeActivity.java :

 private void addView(ViewGroup viewGroup, View view) { viewGroup.addView(view); view.setLayoutParams(generateLayoutParams(viewGroup)); } private ViewGroup.LayoutParams generateLayoutParams(ViewGroup viewGroup) { XmlResourceParser parser = getResources().getLayout(R.layout.params); try { while(parser.nextToken() != XmlPullParser.START_TAG) { // Skip everything until the view tag. } return viewGroup.generateLayoutParams(parser); } catch (Exception e) { e.printStackTrace(); } return null; } 

Otra forma de crear un objeto AttributeSet es implementar la interfaz AttributeSet y hacer que devuelva android:layout_width , android:layout_height y otros atributos de diseño que necesita.

Tengo la siguiente solución para esto:

 View view = new View(context); parent.addView(view); LayoutParams params = view.getLayoutParams(); //Do whatever you need with the parameters view.setLayoutParams(params); 

Usted no puede autogenerar el LayoutParams correcto a menos que usted haga algo hacky, por lo que sólo debe crear una situación donde se autogenerated para usted: sólo tiene que añadir la vista al contenedor. Después de que usted puede obtener de la vista y hacer lo que necesita.

La única advertencia es que si no necesita agregar la vista al contenedor usted mismo, tendrá que eliminar la vista de ella más tarde, pero esto no debería ser un problema.

¿Por qué nadie (aún -> ver 2016.05) ha mencionado aquí un enfoque basado en las reflexiones?

1. punto de entrada:

  /** * generates default layout params for given view group * with width and height set to WLayoutParams.RAP_CONTENT * * @param viewParent - parent of this layout params view * @param <L> - layout param class * @return layout param class object * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException */ @NonNull private <L extends ViewGroup.LayoutParams> L generateDefaultLayoutParams(@NonNull ViewGroup viewParent) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method generateDefaultLayoutParamsMethod = ViewGroup.class.getDeclaredMethod("generateDefaultLayoutParams"); // caution: below way to obtain method has some flaw as we need traverse superclasses to obtain method in case we look in object and not a class // = viewParent.getClass().getDeclaredMethod("generateDefaultLayoutParams"); generateDefaultLayoutParamsMethod.setAccessible(true); return (L) generateDefaultLayoutParamsMethod.invoke(viewParent); } 

2. usos:

 @NonNull protected <T extends ViewGroup.LayoutParams> T createLayoutParamsForView(ViewGroup viewParent, @IdRes int belowViewId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { return createLayoutParamsForView(null,null,viewParent,belowViewId); } @NonNull protected <T extends ViewGroup.LayoutParams> T createLayoutParamsForView(@NonNull Context context, @NonNull Class<? extends ViewGroup> parentClass, @IdRes int belowViewId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { return createLayoutParamsForView(context,parentClass,null,belowViewId); } @NonNull private <T extends ViewGroup.LayoutParams> T createLayoutParamsForView(@Nullable Context context, @Nullable Class<? extends ViewGroup> parentClass, @Nullable ViewGroup parent, @IdRes int belowViewId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { if(context == null && parent == null) throw new IllegalStateException("either context and parent class or must be non null!"); T layoutParams = (T) (parent != null ? generateDefaultLayoutParams(parent) : generateDefaultLayoutParams(context, parentClass)); if (belowViewId != NO_ID && RelativeLayout.LayoutParams.class.isAssignableFrom(layoutParams.getClass())){ ((RelativeLayout.LayoutParams)layoutParams).addRule(RelativeLayout.BELOW, belowViewId); } return layoutParams; } @NonNull private <P extends ViewGroup> P instantiateParent(Class parentClass, Context context) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor constructor = parentClass.getDeclaredConstructor(Context.class); constructor.setAccessible(true); return (P) constructor.newInstance(context); } @NonNull private <L extends ViewGroup.LayoutParams, P extends ViewGroup> L generateDefaultLayoutParams(Context context, @NonNull Class<? extends ViewGroup> viewParentClass) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { P viewParent = instantiateParent(viewParentClass, context); return generateDefaultLayoutParams(viewParent); } 
  • Android: El uso de FEATURE_NO_TITLE con ViewGroup personalizado deja espacio en la parte superior de la ventana
  • OnMeasure no se llama en mi grupo de vista personalizado android
  • Agregar vista al grupo de vistas xml existente en el código
  • Cómo agregar un marcador pin en la vista de la imagen en android
  • NullPointerException: Intento de leer del campo 'int android.view.View.mViewFlags'
  • Inflación del diseño XML en un grupo de vistas personalizado
  • Barra de desplazamiento a ViewGroup personalizado
  • Cómo crear un grupo de vistas personalizado en android con el ejemplo
  • Android ViewGroup crash: Intenta leer desde el campo 'int android.view.View.mViewFlags' en una referencia de objeto nulo
  • Agregar y eliminar un TextView dinámicamente en Android
  • Android View y ViewGroup
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.