Google plus (google+) app has a nice viewing of images on the \"highlights\" category.
For each section on this screen, they made a header that cont
since i can't find any other solution, i've decided to make my own solution (code based on another code i've made, here)
it's used on a ListView instead, but it works quite well. you just set the adapter on the listView and you are good to go. you can set exactly how the headers look like and how each cell look like.
it works by having 2 types of rows: header-rows and cells-rows .
it's not the best solution, since it creates extra views instead of having the ListView/GridView (or whatever you use) put the cells correctly, but it works fine and it doesn't crash
it also doesn't have items clicking (since it's for listView), but it shouldn't be hard to add for whoever uses this code.
sadly it also doesn't have the header as a pinned header, but maybe it's possible to be used with this library (PinnedHeaderListView) .
here's the code :
public abstract class HeaderGridedListViewAdapter<SectionData, ItemType> extends BaseAdapter {
private static final int TYPE_HEADER_ROW = 0;
private static final int TYPE_CELLS_ROW = 1;
private final int mNumColumns;
private final List<Row<SectionData, ItemType>> mRows = new ArrayList<Row<SectionData, ItemType>>();
private final int mCellsRowHeight;
private final Context mContext;
public HeaderGridedListViewAdapter(final Context context, final List<Section<SectionData, ItemType>> sections,
final int numColumns, final int cellsRowHeight) {
this.mContext = context;
this.mNumColumns = numColumns;
this.mCellsRowHeight = cellsRowHeight;
for (final Section<SectionData, ItemType> section : sections) {
// add header
Row<SectionData, ItemType> row = new Row<SectionData, ItemType>();
row.section = section;
row.type = TYPE_HEADER_ROW;
mRows.add(row);
int startIndex = 0;
// add section rows
for (int cellsLeft = section.getItemsCount(); cellsLeft > 0;) {
row = new Row<SectionData, ItemType>();
row.section = section;
row.startIndex = startIndex;
row.type = TYPE_CELLS_ROW;
cellsLeft -= Math.min(mNumColumns, cellsLeft);
startIndex += mNumColumns;
mRows.add(row);
}
}
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(final int position) {
return getItem(position).type;
}
@Override
public int getCount() {
return mRows.size();
}
@Override
public Row<SectionData, ItemType> getItem(final int position) {
return mRows.get(position);
}
@Override
public long getItemId(final int position) {
return position;
}
@Override
public View getView(final int position, final View convertView, final ViewGroup parent) {
final Row<SectionData, ItemType> item = getItem(position);
switch (item.type) {
case TYPE_CELLS_ROW:
LinearLayout rowLayout = (LinearLayout) convertView;
if (rowLayout == null) {
rowLayout = new LinearLayout(mContext);
rowLayout.setOrientation(LinearLayout.HORIZONTAL);
rowLayout.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT, mCellsRowHeight));
rowLayout.setWeightSum(mNumColumns);
}
final int childCount = rowLayout.getChildCount();
// reuse previous views of the row if possible
for (int i = 0; i < mNumColumns; ++i) {
// reuse old views if possible
final View cellConvertView = i < childCount ? rowLayout.getChildAt(i) : null;
// fill cell with data
final View cellView = getCellView(item.section, item.startIndex + i, cellConvertView, rowLayout);
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) cellView.getLayoutParams();
if (layoutParams == null) {
layoutParams = new LinearLayout.LayoutParams(0, mCellsRowHeight, 1);
cellView.setLayoutParams(layoutParams);
} else {
final boolean needSetting = layoutParams.weight != 1 || layoutParams.width != 0
|| layoutParams.height != mCellsRowHeight;
if (needSetting) {
layoutParams.width = 0;
layoutParams.height = mCellsRowHeight;
layoutParams.weight = 1;
cellView.setLayoutParams(layoutParams);
}
}
if (cellConvertView == null)
rowLayout.addView(cellView);
}
return rowLayout;
case TYPE_HEADER_ROW:
return getHeaderView(item.section, convertView, parent);
}
throw new UnsupportedOperationException("cannot create this type of row view");
}
@Override
public boolean areAllItemsEnabled() {
return false;
}
@Override
public boolean isEnabled(final int position) {
return false;
}
/** should handle getting a single header view */
public abstract View getHeaderView(Section<SectionData, ItemType> section, View convertView, ViewGroup parent);
/**
* should handle getting a single cell view. <br/>
* NOTE:read the parameters description carefully !
*
* @param section
* the section that this cell belongs to
* @param positionWithinSection
* the position within the section that we need to fill the data with. note that if it's larger than what
* the section can give you, it means we need an empty cell (same the the others, but shouldn't show
* anything, can be invisible if you wish)
* @param convertView
* a recycled row cell. you must use it when it's not null, and fill it with data
* @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(Section<SectionData, ItemType> section, int positionWithinSection,
View convertView, ViewGroup parent);
// ////////////////////////////////////
// Section//
// /////////
public static class Section<SectionData, ItemType> {
private final List<ItemType> mItems;
private final SectionData mSectionData;
public Section(final SectionData sectionData, final List<ItemType> items) {
this.mSectionData = sectionData;
this.mItems = items;
}
public SectionData getSectionData() {
return mSectionData;
}
public int getItemsCount() {
return mItems.size();
}
public ItemType getItem(final int posInSection) {
return mItems.get(posInSection);
}
@Override
public String toString() {
return mSectionData;
}
}
// ////////////////////////////////////
// Row//
// /////
private static class Row<SectionData, ItemType> {
int type, startIndex;
Section<SectionData, ItemType> section;
}
}