in Android ~ read.

Android使用GridView封装ListView(二)

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

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

上一篇文章写了用GridView封装带SectionListView

然而,在使用的时候发现还不够方便。在设置ListViewSectionRow时,往往需要设置相应的Section间隔和Row间隔。

这下就尴尬了。因此修改一下,让我们的ListView支持Section spacingRow spacing

RectItemDecoration

Android中设置GridViewSpacing,需要使用RecyclerView.ItemDecoration

如果要支持不同Item有不同的Rect,需要记录每个PositionRect,因此使用SparseArray来存储可以提高性能。

SparseArray<Rect> mRectMap;

同时,使用一个属性来记录全局Rect

Rect mRect;

因此Override方法getItemOffsets,判断是否有特定的Rect,若没有,则使用全局。

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    final int itemPosition = parent.getChildAdapterPosition(view);
    if (itemPosition == RecyclerView.NO_POSITION) {
        return;
    }

    Rect rect = mRectMap.get(itemPosition);
    if (rect != null) {
        outRect.top = rect.top;
        outRect.left = rect.left;
        outRect.right = rect.right;
        outRect.bottom = rect.bottom;
    } else {
        outRect.top = mRect.top;
        outRect.left = mRect.left;
        outRect.right = mRect.right;
        outRect.bottom = mRect.bottom;
    }
}

同时提供一个设置的对外接口。

/**
 * 添加Decoration
 * @param rect rect
 * @param position 位置,-1为全局
 */
public void putItemDecoration(Rect rect, int position) {
    if (position == -1) {
        this.mRect = rect;
    } else {
        mRectMap.put(position, rect);
    }
}

Clear

有时候需要清除所有Spacing的配置,提供clear接口。

/**
 * 清除配置
 */
public void clear() {
    mRectMap.clear();
}

SpaceItemDecoration

由于我们只需要ListView每一行的Spacing,所以继承RectItemDecoration派生出SpaceItemDecoration

提供对外接口的接口putItemSpacing

/**
 * 添加ItemSpacing
 * @param spacing 间距
 * @param position 位置,-1为全局
 */
public void putItemSpacing(int spacing, int position) {
    putItemDecoration(new Rect(0, 0, 0, spacing), position);
}

这样就能支持每个Item的间隔了。

Adaptar

由于前面的封装,我们不可能再对外提供item的接口。修改adaptar以适应SectionRowSpacing

提供懒加载获取的SpaceItemDecoration

private CHSpaceItemDecoration mItemDecoration;
private CHSpaceItemDecoration getItemDecoration() {
    if (mItemDecoration == null) {
        mItemDecoration = new CHSpaceItemDecoration();
        getListView().addItemDecoration(mItemDecoration, 0);
    }
    return mItemDecoration;
}

ListView

由于ItemDecoration的设置在ListView,我们又想所有配置统一又Adaptar来设置,所以用Adaptar来持有ListView

为了避免循环引用,这里要使用弱引用。

/**
 * listView弱引用
 */
private WeakReference<CHSectionListView> mWeakListView;
void setListView(CHSectionListView listView) {
    mWeakListView = new WeakReference<>(listView);

    // 重新加载Item状态
    reloadItemsStatus();
    // 添加ItemDecoration
    listView.addItemDecoration(getItemDecoration(), 0);
    // 重置Spacing(下文)
    resetSectionSpacing();
}

private CHSectionListView getListView() {
    return mWeakListView.get();
}

Spacing

设置行间距比较简单,加入全局的rowSpacing就可以了。由于希望第一行和最后一行都能加入间距,所以使用Padding来实现。

/**
 * row距离
 */
private int mRowSpacing = 0;
public int getRowSpacing() {
    return mRowSpacing;
}

public void setRowSpacing(int rowSpacing) {
    if (this.mRowSpacing != rowSpacing) {
        this.mRowSpacing = rowSpacing;
        setRowSpacing();
    }
}

/**
 * 设置行间隔
 */
private void setRowSpacing() {
    if (getListView() != null) {
        getItemDecoration().putItemSpacing(mRowSpacing, -1);
        CHSectionListView listView = getListView();
        // 希望第一行和最后一行都能加入间距
        listView.setPadding(listView.getPaddingLeft(), mRowSpacing, listView.getPaddingRight(), mRowSpacing);
    }
}

Section的间距需要找到每个Section(最后一个除外)最后一行的Position,再赋予响应的Spacing

/**
 * section距离
 */
private int mSectionSpacing = 0;
public int getSectionSpacing() {
    return mSectionSpacing;
}

public void setSectionSpacing(int mSectionSpacing) {
    if (this.mSectionSpacing != mSectionSpacing) {
        this.mSectionSpacing = mSectionSpacing;
        setSectionSpacing();
    }
}

/**
 * 设置section间隔
 */
private void setSectionSpacing() {
    if (getListView() != null) {
        // 清空非全局的spacing设置
        getItemDecoration().clear();
        int sectionCount = mItemsStatus.size() - 1;
        // 遍历Section(最后一个除外)
        for (int section = 0; section < sectionCount - 1; section++) {
            setSectionDecoration(mSectionSpacing, section);
        }
    }
}

/**
 * 获取section间距
 * @param spacing 间距
 * @param section section
 */
private void setSectionDecoration(int spacing, int section) {
    // 找到相应的Position
    int position = getPostionOfIndexPath(new CHIndexPath(section, mItemsStatus.get(section + 1) - 1));
    getItemDecoration().putItemSpacing(spacing, position);
}

ReloadData

相应的在reloadData方法中,加入重新设置

/**
 * 重置垂直间隔
 */
private void resetSectionSpacing() {
    setSectionSpacing();
    setRowSpacing();
}

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