OK, i've found a nice solution for this, which will make the rows depend on their children, but for this I had to make some compromises :
it's an adapter for a listView , which has horizontal linear layouts for its rows.
whoever uses this code should set the selector of the cells and choose what to do on clicking or long clicking.
whoever uses this code should be ready to prepare an empty cell for the extra last cells of the listView.
the listView that is used should have its listSelector set to transparent, to disallow clicking on rows.
I hope anyone could make a better solution that won't need extra views.
sample usage:
_listAdapter=new GriddedListViewAdapter();
_listAdapter.setNumberOfColumns(numberOfColumns);
-listAdapter.setItems(items);
_listView.setAdapter(_listAdapter);
Here's the code:
public abstract class GriddedListViewAdapter<ItemType> extends BaseAdapter
{
private int _numColumns =1;
private final List<Row<ItemType>> _rows =new ArrayList<Row<ItemType>>();
protected final Context _context =App.global();
private List<ItemType> _items;
public void setItems(final List<ItemType> items)
{
_items=items;
prepareRows();
notifyDataSetChanged();
}
private void prepareRows()
{
_rows.clear();
final int itemsCount=_items.size();
for(int i=0;i<itemsCount;i+=_numColumns)
{
final Row<ItemType> row=new Row<ItemType>();
row.startIndex=i;
row.items=new ArrayList<ItemType>(_numColumns);
for(int j=0;j<_numColumns;++j)
{
ItemType item;
if(i+j<itemsCount)
item=_items.get(i+j);
else item=null;
row.items.add(item);
}
_rows.add(row);
}
}
public void setNumberOfColumns(final int numColumns)
{
if(_numColumns==numColumns)
return;
_numColumns=numColumns;
if(_items!=null)
{
prepareRows();
notifyDataSetChanged();
}
}
@Override
public final int getCount()
{
return _rows.size();
}
@Override
public final Row<ItemType> getItem(final int position)
{
return _rows.get(position);
}
@Override
public final long getItemId(final int position)
{
return position;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public final View getView(final int position,final View convertView,final ViewGroup parent)
{
final Row<ItemType> row=getItem(position);
IcsLinearLayout rowLayout=(IcsLinearLayout)convertView;
if(rowLayout==null)
{
rowLayout=new IcsLinearLayout(_context,null);
rowLayout.setMeasureWithLargestChildEnabled(true);
rowLayout.setShowDividers(IcsLinearLayout.SHOW_DIVIDER_MIDDLE);
rowLayout.setDividerDrawable(_context.getResources().getDrawable(R.drawable.list_divider_holo_dark));
rowLayout.setOrientation(LinearLayout.HORIZONTAL);
rowLayout.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT));
}
final int childCount=rowLayout.getChildCount();
for(int i=childCount;i>_numColumns;--i)
rowLayout.removeViewAt(i-1);
// reuse previous views of the row if possible
for(int i=0;i<_numColumns;++i)
{
// reuse old views if possible
final View cellConvertView=i<childCount ? rowLayout.getChildAt(i) : null;
// fill cell with data
final View cellView=getCellView(row.items.get(i),row.startIndex+i,cellConvertView,rowLayout);
LinearLayout.LayoutParams layoutParams=(LinearLayout.LayoutParams)cellView.getLayoutParams();
if(layoutParams==null)
{
layoutParams=new LinearLayout.LayoutParams(0,LayoutParams.MATCH_PARENT,1);
layoutParams.gravity=Gravity.CENTER_VERTICAL;
cellView.setLayoutParams(layoutParams);
}
else
{
final boolean needSetting=layoutParams.weight!=1||layoutParams.width!=0||layoutParams.height!=LayoutParams.MATCH_PARENT;
if(needSetting)
{
layoutParams.width=0;
layoutParams.height=LayoutParams.MATCH_PARENT;
layoutParams.gravity=Gravity.CENTER_VERTICAL;
layoutParams.weight=1;
cellView.setLayoutParams(layoutParams);
}
}
if(cellConvertView==null)
rowLayout.addView(cellView);
}
return rowLayout;
}
@Override
public final int getViewTypeCount()
{
return super.getViewTypeCount();
}
@Override
public final int getItemViewType(final int position)
{
return super.getItemViewType(position);
}
/**
* should handle getting a single cell view. <br/>
* NOTE:read the parameters description carefully !
*
* @param item
* the item that is associated with the cell. if null, you should prepare an empty cell
* @param rawPosition the position within the original list of items that is associated with the cell
* @param convertView
* a recycled cell. you must use it when it's not null, fill it with data, and return it
* @param parent
* the parent of the view. you should use it for inflating the view (but don't attach the view to the
* parent)
*/
public abstract View getCellView(ItemType item,int rawPosition,View convertView,ViewGroup parent);
// ////////////////////////////////////
// Row//
// /////
private static class Row<ItemType>
{
int startIndex;
ArrayList<ItemType> items;
}
}