generating a LayoutParams based on the type of parent

keyboardr picture keyboardr · Aug 10, 2011 · Viewed 10.3k times · Source

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?

Answer

Michael picture Michael · Aug 10, 2011

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.