可以折叠Gridview实现

1,612 阅读3分钟

可以折叠Gridview

ezgif

实现原理

1、折叠实现

重写gridview的setAdapter方法

    @Override
    public void setAdapter(ListAdapter adapter) {
        if (foldNm > 0) {
            //进行折叠
            adapter = new FoldViewGridAdapter(adapter, foldNm);
        }
        super.setAdapter(adapter);
    }

FoldViewGridAdapter是一个包装类,用来实现对当前的adapter包装,主要是重写getCount方法如下:

private class FoldViewGridAdapter implements WrapperListAdapter {
        ...
        @Override
        public int getCount() {
            //fn如果小于1,不进行折叠,根据是否折叠来判断,默认是折叠状态
            if (fn < 0) {
                return adapter.getCount();
            } else {
                //如果fn大于0,进行折叠处理
                if (expend) {
                    return adapter.getCount();
                } else {
                    return Math.min(adapter.getCount(), fn * getNumColumns());
                }
            }
        }
        @Override
        public ListAdapter getWrappedAdapter() {
            return this;
        }
        ...
    }

先判断fn,也就是折叠后显示的行数,fn初始值为-1,也就是不折叠,当设置了fn值大于0,那么就需要进行折叠判断,如果需要折叠,则返回

 return Math.min(adapter.getCount(), fn * getNumColumns());

两者中的最小值。fn * getNumColumns()是折叠时显示的行数和显示的列数的积,也就是显示的个数。

2、如何添加ExpendControlView

同样在重写setAdapter方法,对adapter进行再次封装。

HeaderViewGridAdapter headerViewGridAdapter = new HeaderViewGridAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);

添加ExpendControlView的逻辑主要在HeaderViewGridAdapter中,HeaderViewGrideAdapter要实现一下几点:

1、怎么使添加的ExpendControlView,独占一行。

    public void addExpendControlView(View v, Object data, boolean isSelectable, boolean isExpendControlView) {
 
        ViewGroup.LayoutParams lyp = v.getLayoutParams();

        FixedViewInfo info = new FixedViewInfo();
        FrameLayout fl = new FullWidthFixedViewLayout(getContext());

        if (lyp != null) {
            v.setLayoutParams(new FrameLayout.LayoutParams(lyp.width, lyp.height));
            fl.setLayoutParams(new LayoutParams(lyp.width, lyp.height));
        }
        fl.addView(v);
        info.view = v;
        info.viewContainer = fl;
        info.data = data;
        info.isSelectable = isSelectable;
        //控制是否可以折叠
        info.isExpendControlView = isExpendControlView;
        mFooterViewInfos.add(info);

        if (mAdapter != null) {
            ((HeaderViewGridAdapter) mAdapter).notifyDataSetChanged();
        }
    }

在addExpendControlView方法中,会先创建一个fl的viewGroup,然后再把controlview添加到fl中,FullWidthFixedViewLayout的代码如下:

    private class FullWidthFixedViewLayout extends FrameLayout {

        public FullWidthFixedViewLayout(Context context) {
            super(context);
        }

        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            int realLeft = GridViewEnableFooter.this.getPaddingLeft() + getPaddingLeft();
            // Try to make where it should be, from left, full width
            if (realLeft != left) {
                offsetLeftAndRight(realLeft - left);
            }
            super.onLayout(changed, left, top, right, bottom);
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int targetWidth = GridViewEnableFooter.this.getMeasuredWidth()
                    - GridViewEnableFooter.this.getPaddingLeft()
                    - GridViewEnableFooter.this.getPaddingRight();
            widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth,
                    MeasureSpec.getMode(widthMeasureSpec));
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

在onLayout和onMeasure的时候设置宽度和gridview的宽度一致,来设置fl的宽度和gridview的宽度一致,这样可以独占一行。

2、添加ExpendControlView后count的计算。

添加ExpendControlView需要重新计算adapter的getcount返回值,getCount方法如下:

        public int getCount() {
                return getExpendControlViewsCount() * mNumColumns + getAdapterAndPlaceHolderCount();
        }

因为一个ExpendControlView会占一行,所以需要乘以列数,getAdapterAndPlaceHolderCount方法实现如下:

        private int getAdapterAndPlaceHolderCount() {
            return (int) (Math.ceil(1f * mAdapter.getCount() / mNumColumns) * mNumColumns);
        }

getAdapterAndPlaceHolderCount用来补充数量,使得count个数为整数列的倍数。如果不是整数列,那么添加的ExpendControlView不会占用单独的行,显示有问题。

3、重写getView方法。

添加的ExpendControlView需要在getView中进行返回,所以还需要重写getView方法。

public View getView(int position, View convertView, ViewGroup parent) {

            // Adapter
            final int adjPosition = position - numHeadersAndPlaceholders;
            int adapterCount = 0;
            if (mAdapter != null) {
                adapterCount = getAdapterAndPlaceHolderCount();
                if (adjPosition < adapterCount) {
                    if (adjPosition < mAdapter.getCount()) {
                        return mAdapter.getView(adjPosition, convertView, parent);
                    } else {
                        if (convertView == null) {
                            convertView = new View(parent.getContext());
                        }
                        convertView.setVisibility(View.INVISIBLE);
                        convertView.setMinimumHeight(mRowHeight);
                        return convertView;
                    }
                }
            }
            // ExpendControlView
            final int footerPosition = adjPosition - adapterCount;
            if (footerPosition < getCount()) {
                View footViewContainer = mFooterViewInfos
                        .get(footerPosition / mNumColumns).viewContainer;
                if (position % mNumColumns == 0) {
                    return footViewContainer;
                } else {
                    if (convertView == null) {
                        convertView = new View(parent.getContext());
                    }
                    // We need to do this because GridView uses the height of the last item
                    // in a row to determine the height for the entire row.
                    convertView.setVisibility(View.INVISIBLE);
                    convertView.setMinimumHeight(footViewContainer.getHeight());
                    return convertView;
                }
            }
            throw new ArrayIndexOutOfBoundsException(position);
        }

方法

1、添加底部控制view

public void addExpendControlView(View view)

2、设置折叠时展示的行数

setFoldNum(int foldNm)

用于设置折叠时显示的行数

使用方法

//折叠控制view
gridView.addFooterView(LoadMoreView(this))

//设置折叠行数
gridView.setFoldNum(2)

gridView.adapter = object : BaseAdapter() {...}

上面的loadMoreView就是底部的折叠控制view,添加自己的控制view时,继承下面接口

interface IExpendControl {
    /**
     * 展开
     */
    fun expend()

    /**
     * 折叠
     */
    fun fold()
}

重写expend和fold方法,也就是展开和折叠,完成控制。

class LoadMoreView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : ConstraintLayout(context, attrs), IExpendControl {
    private var viewBinding: LoadMoreViewBinding =
        LoadMoreViewBinding.inflate(LayoutInflater.from(context), this)

    override fun expend() {
        //展开时操作
        viewBinding.loadMore.text = "收起"
        viewBinding.arrow.setImageResource(R.drawable.up_arrow)
    }

    override fun fold() {
        //折叠时操作
        viewBinding.loadMore.text = "展开全部"
        viewBinding.arrow.setImageResource(R.drawable.down_arrow)

    }
}

源码地址

github.com/hankinghu/E…

参考

github.com/liaohuqiu/a…