генерирование LayoutParams на основе типа родителя - PullRequest
13 голосов
/ 11 августа 2011

Мне нужно полностью создать представление на Java, не зная, к какому конкретному типу относится родитель.

пример:

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

Теперь предположим, что я хотел изменить это так, чтобы convertView был wrap_content в обоих направлениях. Поскольку это Адаптер, я бы хотел избежать соединения Адаптера с конкретным типом родителя, но LayoutParams, который я даю в setLayoutParams (), должен быть правильным конкретным типом, иначе приложение будет аварийно завершать работу (т. Е. Если родитель a ListView это должен быть ListView.LayoutParams, если это LinearLayout, то это должен быть LinearLayout.LayoutParams и т. д.). Я также не хочу использовать оператор switch, поскольку это просто более гибкая форма соединения, и если я присоединяю этот адаптер к представлению, я не ожидал, что у меня все равно будет сбой. Есть ли общий способ сделать это?

Ответы [ 4 ]

8 голосов
/ 11 августа 2011

Вы можете сделать это, используя следующий код:

LayoutParams params = parent.generateLayoutParams(null);

EDIT : Приведенный выше метод не работает, потому что ViewGroup.generateLayoutParams() требует, чтобы android:layout_width и android:layout_height были установлены в переданном AttributeSet.

Если вы используете ViewGroup.LayoutParams с любым макетом, все будет работать нормально. Но если вы используете LinearLayout.LayoutParams с RelativeLayout, например, то будет сгенерировано исключение.

EDIT : Есть одно рабочее решение этой проблемы, которое мне не очень нравится. Решение состоит в том, чтобы позвонить generateLayoutParams() с действительным AttributeSet. Вы можете создать AttributeSet объект, используя как минимум два разных подхода. Один из них я реализовал:

Рез \ расположение \ 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;
}

Еще один способ создать объект AttributeSet - реализовать интерфейс AttributeSet и вернуть ему android:layout_width, android:layout_height и другие необходимые вам атрибуты макета.

2 голосов
/ 12 декабря 2014

У меня есть следующий обходной путь для этого:

View view = new View(context);
parent.addView(view);

LayoutParams params = view.getLayoutParams();
//Do whatever you need with the parameters
view.setLayoutParams(params);

Вы не можете автоматически сгенерировать правильный LayoutParams самостоятельно, если не сделаете что-то хакерское, поэтому вам просто нужно создать ситуацию, когда они будут автоматически сгенерированы для вас: просто добавьте представление в контейнер. После этого вы можете получить их из вида и делать то, что вам нужно.

Единственное предостережение в том, что если вам не нужно добавлять представление в контейнер самостоятельно, вам придется удалить представление из него позже, но это не должно быть проблемой.

1 голос
/ 08 мая 2016

почему никто (пока -> см. 2016.05) не упомянул здесь подход, основанный на отражениях?

1. точка входа:

 /**
 *  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. обыкновения:

@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);
}
0 голосов
/ 15 марта 2019

Все LayoutParams классы имеют один общий суперкласс: ViewGroup.LayoutParams.И все популярные макеты (FrameLayout, LinearLayout, ConstraintLayout и т. Д.) Используют ViewGroup.MarginLayoutParams в качестве базового класса для своих соответствующих LayoutParams классов.

Так что, если вам просто нужны ширина, высота иполя, вы можете создать ViewGroup.MarginLayoutParams и передать его в качестве параметров макета любому подклассу ViewGroup.Что произойдет, тогда контейнер автоматически преобразует более общий ViewGroup.MarginLayoutParams в свои собственные параметры макета, используя protected LayoutParams ViewGroup#generateLayoutParams(ViewGroup.LayoutParams p).

Этот код будет работать для любого класса container, включая LinearLayout, RelativeLayout,и т. д .:

val layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
container.addView(view, layoutParams)
...