博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
StaggeredGridView 实现分析--滑动处理(二)计算、移动、回收,以及填充
阅读量:5877 次
发布时间:2019-06-19

本文共 8984 字,大约阅读时间需要 29 分钟。

hot3.png

moveTheChildren:

moveTheChildren 首先根据 incrementalDeltaY 计算滑动后有哪些child变为不可见状态,然后将这些child view 加入recycleBin,然后detach掉,并将剩余的child view挪动到新的位置;

 最后,滑动后如果有空白区域(上滑时下方可能有空白区域,下滑时相反)再通过fillGap方法填补。

计算滑动后上、下部的空白区域时需要用到 getFirstChildTop, getLastChildBottom 方法, 后面我们还会看到getChildTop、getChildBottom、 getChildLeft、getChildRight等方法,通过这些方法得到的值可以确定新的view方法在什么位置。StaggeredGridView正是通过覆盖这些方法实现瀑布流的逻辑的。

1, 向上滑动时回收顶部的view的过程:

第一次滑动时, mFirstPosition 是0, 

mFirstPosition 对应的是Adapter提供的数据中第一项被显示的, 它对应的view肯定是 某一列 view的第一个,但是不能确定具体是哪一列 , 需要用AbsListView.LayoutParams的 position 变量确认。

    private boolean moveTheChildren(int deltaY, int incrementalDeltaY) {               if (!hasChildren()) return true;        //如果只有一列的话,就是第0个child的top值, 该child部分可见时,其top小于0 ; 和下面的spaceAbove有关        final int highestChildTop = getHighestChildTop();        //如果只有一列,就是最后一个child的bottom值, 该child部分可见时,其bottom大于parent的height; 和下面的spaceBelow有关        final int lowestChildBottom = getLowestChildBottom();        int effectivePaddingTop = 0;        int effectivePaddingBottom = 0;        if (mClipToPadding) {            effectivePaddingTop = getListPaddingTop();            effectivePaddingBottom = getListPaddingBottom();        }        final int gridHeight = getHeight();        final int spaceAbove = effectivePaddingTop - getFirstChildTop(); //getFirstChildTop() got the lowest column top         final int end = gridHeight - effectivePaddingBottom;        final int spaceBelow = getLastChildBottom() - end; // getLastChildBottom() got the lowest column bottom        final int height = gridHeight - getListPaddingBottom() - getListPaddingTop();        if (incrementalDeltaY < 0) {            incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);        }        else {            incrementalDeltaY = Math.min(height - 1, incrementalDeltaY);        }        final int firstPosition = mFirstPosition;         int maxTop = getListPaddingTop();        int maxBottom = gridHeight - getListPaddingBottom();        int childCount = getChildCount();        final boolean cannotScrollDown = (firstPosition == 0 &&                highestChildTop >= maxTop && incrementalDeltaY >= 0);        final boolean cannotScrollUp = (firstPosition + childCount == mItemCount &&                lowestChildBottom <= maxBottom && incrementalDeltaY <= 0);        if (cannotScrollDown) {            return incrementalDeltaY != 0;        }        if (cannotScrollUp) {            return incrementalDeltaY != 0;        }        // isDown 为true表示向上滑动,这里好像命名有误!!        final boolean isDown = incrementalDeltaY < 0;        final int headerViewsCount = getHeaderViewsCount();        final int footerViewsStart = mItemCount - getFooterViewsCount();        int start = 0;        int count = 0;        // 向上滑        if (isDown) {            int newTop = -incrementalDeltaY;            if (mClipToPadding) {                newTop += getListPaddingTop();            }            //计算每个child view滑动后是否可见,            //不可见就回收掉(放入recycleBin,但是并没有从 parent detach掉);从上往下遍历            for (int i = 0; i < childCount; i++) {                final View child = getChildAt(i);                if (child.getBottom() >= newTop) {                    break;                }                else {                    count++;                    int position = firstPosition + i;                    if (position >= headerViewsCount && position < footerViewsStart) {                        mRecycleBin.addScrapView(child, position);                    }                }            }        }        //向下滑        else {            int bottom = gridHeight - incrementalDeltaY;            if (mClipToPadding) {                bottom -= getListPaddingBottom();            }             //计算每个child view滑动后是否可见,             //不可见就回收掉(放入recycleBin,但是并没有从 parent detach掉);从下往上遍历            for (int i = childCount - 1; i >= 0; i--) {                final View child = getChildAt(i);                if (child.getTop() <= bottom) {                    break;                }                else {                    start = i;                    count++;                    int position = firstPosition + i;                    if (position >= headerViewsCount && position < footerViewsStart) {                        mRecycleBin.addScrapView(child, position);                    }                }            }        }        mBlockLayoutRequests = true;        // 从 listView中移除        if (count > 0) {            detachViewsFromParent(start, count);            //skippedscrap是指由于addScrap时动画未结束而处于Transient状态的view            mRecycleBin.removeSkippedScrap();            onChildrenDetached(start, count); // 更新记录数据!!        }        // invalidate before moving the children to avoid unnecessary invalidate        // calls to bubble up from the children all the way to the top        if (!awakenScrollBars()) {            invalidate();        }        // 如果是上滑动,剩余view向上挪动        offsetChildrenTopAndBottom(incrementalDeltaY);        if (isDown) {            mFirstPosition += count;        }        //如果有空白,填充        final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);        if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {            fillGap(isDown);        }        // TODO : touch mode selector handling        mBlockLayoutRequests = false;        invokeOnItemScrollListener();        return false;    }

    getFirstChildTop: 由于listView刚填充完,所以 lowestpositionedTop 是 0

 //StaggeredGridView.java @Override    protected int getFirstChildTop() {        if (isHeaderOrFooter(mFirstPosition)) {            return super.getFirstChildTop();        }        return getLowestPositionedTop();    }

在staggeredGridView的 onChildrenDetached(..) 方法中对记录记录数据进行了更新:

//StaggeredGridView.java@Override    protected void onChildrenDetached(final int start, final int count) {        super.onChildrenDetached(start, count);        // go through our remaining views and sync the top and bottom stash.        // Repair the top and bottom column boundaries from the views we still have        Arrays.fill(mColumnTops, Integer.MAX_VALUE);        Arrays.fill(mColumnBottoms, 0);        for (int i = 0; i < getChildCount(); i++) {            final View child = getChildAt(i);            if (child != null) {                final LayoutParams childParams = (LayoutParams) child.getLayoutParams();                if (childParams.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER &&                        childParams instanceof GridLayoutParams) {                    GridLayoutParams layoutParams = (GridLayoutParams) childParams;                    int column = layoutParams.column;                    int position = layoutParams.position;                    final int childTop = child.getTop();                    if (childTop < mColumnTops[column]) {                        mColumnTops[column] = childTop - getChildTopMargin(position);                    }                    final int childBottom = child.getBottom();                    if (childBottom > mColumnBottoms[column]) {                        mColumnBottoms[column] = childBottom + getChildBottomMargin();                    }                }                else {                    // the header and footer here                    final int childTop = child.getTop();                    final int childBottom = child.getBottom();                    for (int col = 0; col < mColumnCount; col++) {                        if (childTop < mColumnTops[col]) {                            mColumnTops[col] = childTop;                        }                        if (childBottom > mColumnBottoms[col]) {                            mColumnBottoms[col] = childBottom;                        }                    }                }            }        }    }

2, 下面看填补空白区域的方法:

fillGap最终由fillDown 和 fillUp方法完成,在“首次填充”分析中,我们对 fillDown方法进行过介绍,它会用child view将剩余空间向下填满为止。

    protected void fillGap(boolean down) {        final int count = getChildCount();        if (down) {            int itemPos = mFirstPosition + count;            final int startOffset = getChildTop(itemPos); // 第 itemPos个数据项对应的view应处的top处置            fillDown(position, startOffset);        }        else {            int position = mFirstPosition - 1;            final int startOffset = getChildBottom(position);            fillUp(position, startOffset);        }        adjustViewsAfterFillGap(down);    }

3, 对于向上滑动的过程,同样有view的回收、移动和记录数据的更新, 最后通过fillUp方法对上部空白进行填充。 填充逻辑与向下填充是对称的, 从top位置最大的(视觉上是最低的)列开始。

逻辑上,每添加一个view就将mFirstPosition的值减少1 。

    private View fillUp(int pos, int nextBottom) {        View selectedView = null;        int end = mClipToPadding ? getListPaddingTop() : 0;        while ((nextBottom > end || hasSpaceUp()) && pos >= 0) {            makeAndAddView(pos, nextBottom, false, false);            pos--;            nextBottom = getNextChildUpsBottom(pos);        }        mFirstPosition = pos + 1;        return selectedView;    }

总结:

将"”过程分析和 结合起来,就能明白StaggeredGridView的具体工作过程了。

转载于:https://my.oschina.net/u/255456/blog/343610

你可能感兴趣的文章
Xtreme8.0 - Kabloom dp
查看>>
jquery css3问卷答题卡翻页动画效果
查看>>
MDK5.00中*** error 65: access violation at 0xFFFFFFFC : no 'write' permission的一种解决方法
查看>>
Android 集成支付宝支付详解
查看>>
SQL分布式查询、跨数据库查询
查看>>
C#------连接SQLServer和MySQL字符串
查看>>
Arcgis Licensemanager 不能启动的原因之一(转载)
查看>>
(原)Android在子线程用handler发送的消息,主线程是怎么loop到的?
查看>>
$digest already in progress 解决办法——续
查看>>
虚拟机 centos设置代理上网
查看>>
Struts2中Date日期转换的问题
查看>>
mysql 数据类型
查看>>
Ubuntu 设置当前用户sudo免密码
查看>>
设置tomcat远程debug
查看>>
android 电池(一):锂电池基本原理篇【转】
查看>>
Total Command 常用快捷键
查看>>
ionic 调用手机的打电话功能
查看>>
怎么使用阿里云直播服务应用到现在主流直播平台中
查看>>
Xcode全局替换内容,一键Replace
查看>>
1000 加密算法
查看>>