Создание LayoutParams на основе типа родительского
Мне нужно создать View полностью в 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(), должен быть правильным конкретным типом, иначе приложение будет разбиваться (то есть, если родительский ListView должен быть ListView.LayoutParams, если это LinearLayout, это должен быть LinearLayout.LayoutParams и т.д.). Я не хочу использовать оператор switch, так как это просто более гибкая форма связи, и если я присоединю этот адаптер к виду, которое я не ожидал, я все равно в конечном итоге столкнулся бы с крахом. Есть ли общий способ сделать это?
Ответы
Ответ 1
Вы можете сделать это, используя следующий код:
LayoutParams params = parent.generateLayoutParams(null);
ИЗМЕНИТЬ:
Вышеуказанный метод не работает, потому что ViewGroup.generateLayoutParams()
требует, чтобы в переданном AttributeSet
задан android:layout_width
и android:layout_height
.
Если вы используете ViewGroup.LayoutParams
с любым макетом, все будет работать нормально. Но если вы используете LinearLayout.LayoutParams
с RelativeLayout
, например, будет выбрано исключение.
ИЗМЕНИТЬ:
Там одно рабочее решение для этой проблемы, которое мне не очень нравится. Решение состоит в вызове 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
У меня есть следующее обходное решение для этого:
View view = new View(context);
parent.addView(view);
LayoutParams params = view.getLayoutParams();
//Do whatever you need with the parameters
view.setLayoutParams(params);
Вы не можете самостоятельно генерировать правильный LayoutParams
, если вы не сделаете что-то взломанное, поэтому вам нужно просто создать ситуацию, когда они будут автоматически созданы для вас: просто добавьте представление в контейнер. После этого вы можете получить их из представления и сделать то, что вам нужно.
Единственное предостережение в том, что если вам не нужно добавлять представление в контейнер самостоятельно, вам придется удалить его из него позже, но это не должно быть проблемой.
Ответ 3
почему никто (еще → см. 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);
}