由于我找不到任何其他解决方案,我决定制作自己的解决方案(基于我制作的另一个代码的代码,here https://stackoverflow.com/a/21504515/878126)
它被用在 ListView 上,但效果很好。您只需在 listView 上设置适配器即可。您可以准确设置标题的外观以及每个单元格的外观。
它的工作原理是有两种类型的行: header-rows 和 cells-rows 。
这不是最好的解决方案,因为它创建了额外的视图,而不是让 ListView/GridView (或您使用的任何内容)正确放置单元格,但它工作正常并且不会崩溃
它也没有点击项目(因为它用于 listView),但对于使用此代码的人来说添加应该不难。
遗憾的是它也没有标题作为固定标题,但也许可以与这个库(PinnedHeaderListView) https://github.com/JimiSmith/PinnedHeaderListView .
这是代码:
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;
}
}