Linear Grid Layout

2014/12/18

Pre API-21, GridLayout has no concept of weights. That means it’s impossible to make a GridLayout that stretches it’s rows and columns to fill the available space. Apparently API 21 fixes this, so says the Javadocs,

As of API 21, GridLayout’s distribution of excess space accomodates the principle of weight. In the event that no weights are specified, the previous conventions are respected and columns and rows are taken as flexible if their views specify some form of alignment within their groups.

Pre API 21, here’s a simple solution. LinearGridLayout is a view that simulates a grid layout by nesting LinearLayouts. This is just sample code and while it worked for my particular situation you may need to tweak it to your requirements.

To use, just insert into your XML,

    <LinearGridLayout
        auto:columns="2"
        auto:margins="10dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

And in your code, add some views,

LinearGridLayout layout = ...;
layout.addViews(myViews);

Here’s the source …

public class LinearGridLayout extends LinearLayout {

  private int columns;
  private int margins;

  public LinearGridLayout(Context context) {
    super(context);
  }

  public LinearGridLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    applyAttrs(context, attrs);
  }

  public LinearGridLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    applyAttrs(context, attrs);
  }

  private void applyAttrs(Context context, AttributeSet attrs) {
    TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ContactlessLogosView, 0, 0);

    try {
        columns = a.getInt(R.styleable.ContactlessLogosView_columns, Integer.MAX_VALUE);
        margins = a.getDimensionPixelSize(R.styleable.ContactlessLogosView_margins, 0);
    } finally {
      a.recycle();
    }

    setOrientation(VERTICAL);
  }

  public void addViews(Collection<View> views) {
    Preconditions.checkArgument(columns != 0);

    removeAllViews();

    int c = 0;
    LinearLayout layout = null;

    for (View v: views) {
      if (c == columns) {
        c = 0;
        layout = null;
      }

      if (layout == null) {
        layout = new LinearLayout(getContext());
        layout.setOrientation(HORIZONTAL);
        LayoutParams rowParams = new LayoutParams(LayoutParams.MATCH_PARENT, 0, 1);
        layout.setLayoutParams(rowParams);
        addView(layout);
      }

      LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
      lp.setMargins(margins, margins, margins, margins);
      v.setLayoutParams(lp);
      layout.addView(v);

      c++;
    }

    if (columns != Integer.MAX_VALUE) {
      for (int i = c; i < columns; i++) {
        LayoutParams lp = new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1);
        View v = new View(getContext());
        v.setLayoutParams(lp);
        layout.addView(v);
      }
    }
  }
}