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:
- Relación de aspecto fija
- Uso de dispatchDraw (Lienzo lienzo)
- Cómo crear un botón que contiene varias vistas?
- Android en la vista eliminado de los padres
- Android StackOverflowError en ViewGroup.resetResolvedTextDirection
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?
- Nueve parches en un grupo de vistas personalizado
- Disposición personalizada de Android - onDraw () nunca se llama
- Cómo obtener una referencia al grupo de vista principal
- Cómo obtener el índice de una vista particular (personalizada) de ViewGroup (FrameLayout) después de hacer onLongClick en la vista
- Las vistas dentro de un Grupo de vistas personalizado no se muestran
- Android ViewGroup.setScaleX () hace que la vista se recorte
- Android: usando MotionEvent para arrastrar y soltar el posicionamiento de vistas en un grupo de vistas personalizado
- Force onLayout en un subView cuyas dimensiones no cambian
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); }
- El teclado suave de Android oculta el botón
- Android: Notificar al usuario de una versión más reciente