There´s a great way to achieve this behaviour/layout. Try something like this:
sectionableAdapter:
public abstract class SectionableAdapter extends BaseAdapter {
public static final int MODE_VARY_WIDTHS = 0;
public static final int MODE_VARY_COUNT = 1;
private LayoutInflater inflater;
private int rowResID;
private int headerID;
private int itemHolderID;
private int colCount;
private int sectionsCount;
private int resizeMode;
private ViewGroup measuredRow;
public SectionableAdapter(LayoutInflater inflater, int rowLayoutID, int headerID, int itemHolderID)
{
this(inflater, rowLayoutID, headerID, itemHolderID, MODE_VARY_WIDTHS);
}
/**
* Constructor.
* @param inflater inflater to create rows within the grid.
* @param rowLayoutID layout resource ID for each row within the grid.
* @param headerID resource ID for the header element contained within the grid row.
* @param itemHolderID resource ID for the cell wrapper contained within the grid row. This View must only contain cells.
*/
public SectionableAdapter(LayoutInflater inflater, int rowLayoutID, int headerID, int itemHolderID, int resizeMode)
{
super();
this.inflater = inflater;
this.rowResID = rowLayoutID;
this.headerID = headerID;
this.itemHolderID = itemHolderID;
this.resizeMode = resizeMode;
// Determine how many columns our row holds.
View row = inflater.inflate(rowLayoutID, null);
if (row == null)
throw new IllegalArgumentException("Invalid row layout ID provided.");
ViewGroup holder = (ViewGroup)row.findViewById(itemHolderID);
if (holder == null)
throw new IllegalArgumentException("Item holder ID was not found in the row.");
if (holder.getChildCount() == 0)
throw new IllegalArgumentException("Item holder does not contain any items.");
colCount = holder.getChildCount();
sectionsCount = getSectionsCount();
}
/**
* Returns the total number of items to display.
*/
protected abstract int getDataCount();
/**
* Returns the number of sections to display.
*/
protected abstract int getSectionsCount();
/**
* @param index the 0-based index of the section to count.
* @return the number of items in the requested section.
*/
protected abstract int getCountInSection(int index);
/**
* @param position the 0-based index of the data element in the grid.
* @return which section this item belongs to.
*/
protected abstract int getTypeFor(int position);
/**
* @param section the 0-based index of the section.
* @return the text to display for this section.
*/
protected abstract String getHeaderForSection(int section);
/**
* Populate the View and attach any listeners.
* @param cell the inflated cell View to populate.
* @param position the 0-based index of the data element in the grid.
*/
protected abstract void bindView(View cell, int position);
/**
* Perform any row-specific customization your grid requires. For example, you could add a header to the
* first row or a footer to the last row.
* @param row the 0-based index of the row to customize.
* @param convertView the inflated row View.
*/
protected void customizeRow(int row, View rowView)
{
// By default, does nothing. Override to perform custom actions.
}
@Override
public int getCount()
{
int totalCount = 0;
for (int i = 0; i < sectionsCount; ++i)
{
int count = getCountInSection(i);
if (count > 0)
totalCount += (getCountInSection(i)-1) / colCount + 1;
}
if (totalCount == 0)
totalCount = 1;
return totalCount;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
int realPosition = 0;
int viewsToDraw = 0;
int rows = 0;
int totalCount = 0;
for (int i = 0; i < sectionsCount; ++i)
{
int sectionCount = getCountInSection(i);
totalCount += sectionCount;
if (sectionCount > 0 && position <= rows + (sectionCount - 1) / colCount)
{
realPosition += (position - rows) * colCount;
viewsToDraw = (int)(totalCount - realPosition);
break;
}
else
{
if (sectionCount > 0)
{
rows += (int)((sectionCount - 1) / colCount + 1);
}
realPosition += sectionCount;
}
}
if (convertView == null)
{
convertView = inflater.inflate(rowResID, parent, false);
if (measuredRow == null && resizeMode == MODE_VARY_COUNT)
{
measuredRow = (ViewGroup)convertView;
// In this mode, we need to learn how wide our row will be, so we can calculate
// the number of columns to show.
// This listener will notify us once the layout pass is done and we have our
// measurements.
measuredRow.getViewTreeObserver().addOnGlobalLayoutListener(layoutObserver);
}
}
int lastType = -1;
if (realPosition > 0)
lastType = getTypeFor(realPosition-1);
if (getDataCount() > 0)
{
TextView header = (TextView)convertView.findViewById(headerID);
int newType = getTypeFor(realPosition);
if (newType != lastType)
{
header.setVisibility(View.VISIBLE);
header.setText(getHeaderForSection(newType));
}
else
{
header.setVisibility(View.GONE);
}
}
customizeRow(position, convertView);
ViewGroup itemHolder = (ViewGroup)convertView.findViewById(itemHolderID);
for (int i = 0; i < itemHolder.getChildCount(); ++i)
{
View child = itemHolder.getChildAt(i);
if (i < colCount && i < viewsToDraw && child != null)
{
bindView(child, realPosition + i);
child.setVisibility(View.VISIBLE);
}
else if (child != null)
{
child.setVisibility(View.INVISIBLE);
}
}
return convertView;
}
private ViewTreeObserver.OnGlobalLayoutListener layoutObserver = new ViewTreeObserver.OnGlobalLayoutListener() {
// The better-named method removeOnGlobalLayoutListener isn't available until a later API version.
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
if (measuredRow != null)
{
int rowWidth = measuredRow.getWidth();
ViewGroup childHolder = (ViewGroup)measuredRow.findViewById(itemHolderID);
View child = childHolder.getChildAt(0);
int itemWidth = child.getWidth();
if (rowWidth > 0 && itemWidth > 0)
{
colCount = rowWidth / itemWidth;
// Make sure this listener isn't called again after we layout for the next time.
measuredRow.getViewTreeObserver().removeGlobalOnLayoutListener(this);
// The grid will now update with the correct column count.
notifyDataSetChanged();
}
}
}
};
}
BookcaseAdapter:
public class BookcaseAdapter extends SectionableAdapter implements
View.OnClickListener {
// For simplicity, we hard-code the headers and data. In an actual app, this
// can come from the network, the filesystem, SQLite, or any of the
// usual suspects.
static final String[] AUTHORS = new String[] { "Roberto Bola–o",
"David Mitchell", "Haruki Murakami", "Thomas Pynchon" };
private static final String[][] BOOKS = new String[][] {
{ "The Savage Detectives", "2666" },
{ "Ghostwritten", "number9dream", "Cloud Atlas",
"Black Swan Green", "The Thousand Autumns of Jacob de Zoet" },
{ "A Wild Sheep Chase",
"Hard-Boiled Wonderland and the End of the World",
"Norwegian Wood", "Dance Dance Dance",
"South of the Border, West of the Sun",
"The Wind-Up Bird Chronicle", "Sputnik Sweetheart",
"Kafka on the Shore", "After Dark", "1Q84" },
{ "V.", "The Crying of Lot 49", "Gravity's Rainbow", "Vineland",
"Mason & Dixon", "Against the Day", "Inherent Vice" } };
private Activity activity;
public BookcaseAdapter(Activity activity, LayoutInflater inflater,
int rowLayoutID, int headerID, int itemHolderID, int resizeMode) {
super(inflater, rowLayoutID, headerID, itemHolderID, resizeMode);
this.activity = activity;
}
@Override
public Object getItem(int position) {
for (int i = 0; i < BOOKS.length; ++i) {
if (position < BOOKS[i].length) {
return BOOKS[i][position];
}
position -= BOOKS[i].length;
}
// This will never happen.
return null;
}
@Override
protected int getDataCount() {
int total = 0;
for (int i = 0; i < BOOKS.length; ++i) {
total += BOOKS[i].length;
}
return total;
}
@Override
protected int getSectionsCount() {
return BOOKS.length;
}
@Override
protected int getCountInSection(int index) {
return BOOKS[index].length;
}
@Override
protected int getTypeFor(int position) {
int runningTotal = 0;
int i = 0;
for (i = 0; i < BOOKS.length; ++i) {
int sectionCount = BOOKS[i].length;
if (position < runningTotal + sectionCount)
return i;
runningTotal += sectionCount;
}
// This will never happen.
return -1;
}
@Override
protected String getHeaderForSection(int section) {
return AUTHORS[section];
}
@Override
protected void bindView(View convertView, int position) {
String title = (String) getItem(position);
TextView label = (TextView) convertView
.findViewById(R.id.bookItem_title);
label.setText(title);
convertView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent i = new Intent(Intent.ACTION_SEARCH);
TextView label = (TextView) v.findViewById(R.id.bookItem_title);
String text = label.getText().toString();
i.putExtra(SearchManager.QUERY, text);
activity.startActivity(i);
}
}
SectionedGridActivity:
public class SectionedGridActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sectioned_grid);
ListView list = (ListView) findViewById(R.id.sectionedGrid_list);
// Switch between these to see the two different types of resizing available.
BookcaseAdapter adapter = new BookcaseAdapter(this,
getLayoutInflater(), R.layout.book_row, R.id.bookRow_header,
R.id.bookRow_itemHolder, SectionableAdapter.MODE_VARY_WIDTHS);
// BookcaseAdapter adapter = new BookcaseAdapter(this,
// getLayoutInflater(), R.layout.book_row_vary_columns, R.id.bookRow_header,
// R.id.bookRow_itemHolder, SectionableAdapter.MODE_VARY_COUNT);
list.setAdapter(adapter);
list.setDividerHeight(0);
}
}
And all this layouts to work with it:
activity_sectioned_grid.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"
>
<ListView
android:id="@+id/sectionedGrid_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:listSelector="@android:color/transparent"
/>
</RelativeLayout>
book_item.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="200dp"
android:layout_weight="1"
android:orientation="vertical"
android:background="@drawable/gradients"
android:layout_margin="8dp"
>
<TextView
android:id="@+id/bookItem_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ffffff"
android:padding="8dp"
/>
</LinearLayout>
book_item_vary_columns.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="190dp"
android:layout_height="190dp"
android:orientation="vertical"
android:padding="8dp"
>
<TextView
android:id="@+id/bookItem_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="#ffffff"
android:padding="8dp"
android:background="@drawable/gradients"
/>
</LinearLayout>
book_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/bookRow_header"
style="@android:style/TextAppearance.Holo.Large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:visibility="gone"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/bookRow_itemHolder"
>
<include layout="@layout/book_item"/>
<include layout="@layout/book_item"/>
</LinearLayout>
</LinearLayout>
book_row_vary_columns.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/bookRow_header"
style="@android:style/TextAppearance.Holo.Large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:visibility="gone"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/bookRow_itemHolder"
>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
<include layout="@layout/book_item_vary_columns"/>
</LinearLayout>
</LinearLayout>
Note that this code isn´t mine, it just fits this question and I´ve researched this information before. Try searching google for sectioned grid - you will find a demo apk.