in Android ~ read.

Android使用GridView封装ListView(一)

本文地址:https://www.chihoc.com/android-listview-encapsulation-1/
欢迎转载,请注明出处,谢谢

Demo地址:https://github.com/ChiHoc/CHListView

Android的GridView有很高的可定制性,但是对于普通的ListView,显得太麻烦了点。而且如果像要求有Section的布局,就相当复杂了。因此,我们使用GridView封装一个ListView

IndexPath

既然我们需要有SectionListView,那应该有相应的数据结构来指示SectionRow。决定就是它了!IndexPath

public class CHIndexPath {
    protected int mSection;
    protected int mRow;
}

存储每个Section中Item数量

在计算Section的过程中,不可避免要多次获取每个Section的数量,因此使用一个数组来存储item总数和每个SectionRowCount

/**
 * 重新加载item情况
 */
private void reloadItemsStatus() {
    int sectionCount = getSectionCount();
    mItemsStatus.clear();
    Integer sum = 0;
    //先塞个item总数进去
    mItemsStatus.add(sum);
    for (int section = 0; section < sectionCount; section ++) {
        //计算每个Section的item数
        int rowCount = getRowCountAtSection(section);
        mItemsStatus.add(rowCount);
        sum += rowCount;
    }
    mItemsStatus.set(0, sum);
}

ItemPosition转换成IndexPath

要实现Section,中间必然需要经过PositionIndexPath的转换。

/**
 * 获取position相应位置
 * @param position position
 * @return 位置
 */
private CHIndexPath getIndexPathOfPostion(int position) {
    int section;
    int row = position;
    int sectionCount = mItemsStatus.size() - 1;
    //遍历计算出position是哪个section,哪个row
    for (section = 0; section < sectionCount; section ++) {
        int rowCount = mItemsStatus.get(section + 1);
        if (row < rowCount) {
            break;
        }
        row -= rowCount;
    }
    CHIndexPath indexPath = new CHIndexPath(section, row);
    return indexPath;
}

/**
 * 获取indexPath相应位置position
 * @param indexPath indexPath
 * @return position
 */
private int getPostionOfIndexPath(CHIndexPath indexPath) {
    int position = 0;
    for (int section = 0; section < indexPath.mSection; section ++) {
        position += mItemsStatus.get(section + 1);
    }
    position += indexPath.mRow;
    return position;
}

Adapter

如果要使用GridView,要实现RecyclerView.Adapter以下几个方法。

public int getItemCount()

public int getItemViewType(int position);

public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

public void onBindViewHolder(RecyclerView.ViewHolder holder, int position);

下面一个个讲解怎么转换。

getItemCount

获取item数目其实就是每个SectionRowCount相加,声明下面的两个抽象方法获取Section总数和每个SectionRowCount

/**
 * 获取 section数
 * @return section数
 */
protected abstract int getSectionCount();

/**
 * 获取section对应行数
 * @param section section
 * @return 行数
 */
protected abstract int getRowCountAtSection(int section);

鉴于上面已经用Status存下总数,事实上直接取出来就可以了。

@Override
final public int getItemCount() {
    return mItemsStatus.get(0);
}

getItemViewType

ListView支持每一行有不同的样式,因此声明一个抽象方法来获取不同的ViewType,当然也可以只返回一种。

/**
 * 获取indexPath对应item类型
 * @param indexPath indexPath
 * @return item类型
 */
protected abstract int getItemViewType(CHIndexPath indexPath);

中间经过position转换IndexPath

@Override
final public int getItemViewType(int position)
{
    return getItemViewType(getIndexPathOfPostion(position));
}

onCreateViewHolder

事实上这个方法现阶段是不需要转换的,直接子类去实现就行了。之后因为加入其它内容,才涉及到复写。因此这个方法不用复写。

onBindViewHolder

声明一个抽象方法来绑定ViewHolder

/**
 * 绑定holder
 * @param holder holder
 * @param indexPath indexPath
 */
protected abstract void onBindViewHolder(RecyclerView.ViewHolder holder, CHIndexPath indexPath);

同样的,中间经过position转换IndexPath

@Override
final public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    onBindViewHolder(holder, getIndexPathOfPostion(position));
}

ReloadData

由于中间有转换过程,所以额外提供reloadData的接口供外部使用。

/**
 * 重新加载数据
 */
public void reloadData() {
    reloadItemsStatus();
    notifyDataSetChanged();
}