根据父级的类型生成 LayoutParams

发布于 2024-11-29 02:52:57 字数 635 浏览 2 评论 0原文

我发现自己需要完全用 Java 创建一个 View,而不知道父级的具体类型是什么。

示例:

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 。由于这是一个适配器,我想避免将适配器与父级的具体类型耦合,但是我在 setLayoutParams() 中给它的 LayoutParams 必须是正确的具体类型,否则应用程序将崩溃(即,如果父级是ListView 必须是 ListView.LayoutParams,如果是 LinearLayout,则必须是 LinearLayout.LayoutParams,等等)。我也不想使用 switch 语句,因为这只是一种更灵活的耦合形式,如果我将此适配器附加到视图,我没想到我仍然会崩溃。有没有通用的方法来做到这一点?

I find myself needing to create a View completely in Java without knowing what concrete type the parent is.

example:

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

Now suppose I wanted to change this so that the convertView was wrap_content in both directions. Since this is an Adapter, I'd like to avoid coupling the Adapter with the concrete type of the parent, but the LayoutParams I give it in setLayoutParams() has to be the correct concrete type otherwise the app will crash (i.e. if parent is a ListView it has to be ListView.LayoutParams, if it's a LinearLayout it must be a LinearLayout.LayoutParams, etc.). I don't want to use a switch statement either since that's just a more flexible form of coupling, and if I attach this adapter to a view I didn't anticipate I still end up with a crash. Is there a generic way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

农村范ル 2024-12-06 02:52:57

您可以使用以下代码来完成此操作:

LayoutParams params = parent.generateLayoutParams(null);

编辑
上面的方法不起作用,因为 ViewGroup.generateLayoutParams() 需要在传递的 中设置 android:layout_widthandroid:layout_height >属性集

如果您将ViewGroup.LayoutParams与任何布局一起使用,那么一切都会正常工作。但是,如果您将 LinearLayout.LayoutParamsRelativeLayout 一起使用,则会引发异常。

编辑
对于这个问题有一个可行的解决方案,但我不太喜欢。解决方案是使用有效的 AttributeSet 调用 generateLayoutParams()。您可以使用至少两种不同的方法创建 AttributeSet 对象。我已经实现了其中一个:

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;
}

创建 AttributeSet 对象的另一种方法是实现AttributeSet接口并使其返回android:layout_widthandroid:layout_height以及其他你需要的布局属性。

You can do this using the following code:

LayoutParams params = parent.generateLayoutParams(null);

EDIT:
The method above doesn't work because ViewGroup.generateLayoutParams() requires android:layout_width and android:layout_height to be set in the passed AttributeSet.

If you use ViewGroup.LayoutParams with any layout then everything will work fine. But if you use LinearLayout.LayoutParams with RelativeLayout for example, then an exception will be thrown.

EDIT:
There's one working solution for this problem which I don't really like. The solution is to call generateLayoutParams() with valid AttributeSet. You can create an AttributeSet object using at least two different approaches. One of them I've implemented:

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;
}

Another way to create an AttributeSet object is to implement AttributeSet interface and make it return android:layout_width, android:layout_height and other layout attributes you need.

他不在意 2024-12-06 02:52:57

我对此有以下解决方法:

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

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

您无法自己自动生成正确的 LayoutParams ,除非您做了一些 hacky 的事情,所以您应该创建一个为您自动生成它们的情况:只需将视图添加到容器中。之后,您可以从视图中获取它们并执行您需要的操作。

唯一需要注意的是,如果您不需要自己将视图添加到容器中,则稍后必须从容器中删除视图,但这应该不是问题。

I have the following workaround for this:

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

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

You cannot autogenerate the correct LayoutParams yourself unless you do something hacky, so you should just create a situation where they will be autogenerated for you: just add the view to the container. After that you can get them from the view and do what you need.

The only caveat is that if you don't need to add the view to the container yourself, you'll have to remove the view from it later, but this shouldn't be a problem.

护你周全 2024-12-06 02:52:57

所有 LayoutParams 类都有一个通用超类:ViewGroup.LayoutParams。所有流行的布局(FrameLayoutLinearLayoutConstraintLayout 等)都使用 ViewGroup.MarginLayoutParams 作为它们各自的 LayoutParams 类。

因此,如果您只需要宽度、高度和边距,则可以创建 ViewGroup.MarginLayoutParams 并将其作为布局参数传递给 ViewGroup 的任何子类。然后,容器将使用受保护的 LayoutParams ViewGroup#generateLayoutParams(ViewGroup.LayoutParams p) 自动将更通用的 ViewGroup.MarginLayoutParams 转换为自己的布局参数。

此代码适用于任何 container 类,包括 LinearLayoutRelativeLayout 等:

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

All LayoutParams classes have one general superclass: ViewGroup.LayoutParams. And all popular layouts (FrameLayout, LinearLayout, ConstraintLayout, etc.) use ViewGroup.MarginLayoutParams as base class for their respective LayoutParams classes.

So if you just need width, height and margins, you can create ViewGroup.MarginLayoutParams and pass it as layout params to any subclass of ViewGroup. What will happen then is container will automatically convert more generic ViewGroup.MarginLayoutParams into its own layout parameters using protected LayoutParams ViewGroup#generateLayoutParams(ViewGroup.LayoutParams p).

This code will work for any container class, including LinearLayout, RelativeLayout, etc:

val layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
container.addView(view, layoutParams)
南冥有猫 2024-12-06 02:52:57

为什么没有人(还 -> 参见 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);
}

why no one (yet -> see 2016.05) has mentioned here an approach based on reflections ?

1. entry point:

 /**
 *  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. usages:

@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);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文