Android仿Oppo手表应用列表-CopyOppoWatch

2020-04-10  本文已影响0人  面向星辰大海的程序员

地址https://github.com/dubaofeng/CopyOppoWatcheLayoutManager

效果图

image

参考并使用了一些代码
1、 张旭童的掌握自定义LayoutManager(一) 系列开篇 常见误区、问题、注意事项,常用API
2、张旭童的掌握自定义LayoutManager(二) 实现流式布局
3、陈小缘的自定义LayoutManager第十一式之飞龙在天
4、勇潮陈的Android仿豆瓣书影音频道推荐表单堆叠列表RecyclerView-LayoutManager

要自定义自己的LayoutManager的几个必要的方法

public class CopyOppoWatcheLayoutManagerextends RecyclerView.LayoutManager{

/*** {@inheritDoc}*/
@Override
public RecyclerView.LayoutParamsgenerateDefaultLayoutParams() {
//1.必须重写的方法,直接复制我们常用LinearLayoutManager里的过来就可以了
 return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,         ViewGroup.LayoutParams.WRAP_CONTENT);
}

@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
//2.入口,初始化列表时,列表滑动时有回调
}

@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
//3.列表滑动时回调,dy就是滑动的距离>0列表上滚动,还有水平方向的方法,
return dy;
}

@Override
public boolean canScrollVertically() {
//4.能否上下滑动,true表示可以,还有左右的方法
    return true;
}
}

实现思路:

控制滑动:
@Override
 public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
//没有位移或者没有子View直接返回0
        if (dy ==0 || getChildCount() ==0) {
            return 0;
        }
        int realOffset = dy;//实际滑动的距离
        mCurrentOffset += realOffset;//累加实际滑动距离,通过与itemHSize求余得出每个要摆放的子view的垂直方向的偏移量。
        if (mCurrentOffset <= -itemHSize) {//<0列表向下滚动
            realOffset =0;//返回0列表就划不动了
/*限制允许下拉出一个item的空白高度,不能超过一个item的高度,超了会改变下标,所以限制1个像素**/
 mCurrentOffset =(int) -itemHSize +1;
        }
//底部偏移和上部偏移一个道理,totalOffset总偏移是通过摆放多少个正常大小的item计算出来的
        if (mCurrentOffset >=totalOffset) {
            mCurrentOffset =totalOffset -1;//防止越界
            realOffset =0;//列表不滑动
        } 
        fill(recycler, state, realOffset);//填充子view,真正做布局的地方,realOffset其实没用到
// offsetChildrenVertical(-realOffset);//滑动,这个方法可以滑动所有摆放的子View 没有用到它
        return realOffset;//返回多少就滑动多少,0则不滑动,在边界滑动会出现表示拉不动的弧
    }

//填充:

 private int fill(RecyclerView.Recycler recycler, RecyclerView.State state, int dy) {
        detachAndScrapAttachedViews(recycler);


        int fs = (int) Math.floor(Math.abs(mCurrentOffset) / itemHSize) * hCount;
        if (fs <= getItemCount() - 1) {
            mFirsItemPosition = fs;
        }
        int LastItemPosition = mFirsItemPosition + screenItemCount - 1 + hCount;
        if (LastItemPosition > getItemCount() - 1) {
            LastItemPosition = getItemCount() - 1;
        }
        mLastItemPosition = LastItemPosition;

        int buttomItemCount = (mLastItemPosition + 1) % hCount;
        if (buttomItemCount == 0) {
            buttomItemCount = hCount;
        }
        int leftOffset = getPaddingLeft();
        int lastOffset = (vCount - 1) * itemHSize + mSetOffset;
        float itemOffset = (mCurrentOffset + 0f) % itemHSize;
        float frac = itemOffset / itemHSize;
        if (mCurrentOffset >= totalOffset) {
            frac = 1f;
        }
        int scrollY = (int) (itemHSize * frac);
        int viewTopOffset = getPaddingTop() + mSetOffset;
        viewTopOffset -= scrollY;//偏移量


//顶部小item
        if (mFirsItemPosition >= hCount) {

            for (int k = mFirsItemPosition - hCount; k < mFirsItemPosition; k++) {
                View child = recycler.getViewForPosition(k);
                addChild(child, leftOffset, smallCircleTopToScreenOffset, leftOffset + itemWSize, smallCircleTopToScreenOffset + itemHSize, minScale);
                leftOffset += itemWSize;
            }
        }

//画底部小圆
        leftOffset = getPaddingLeft();
        int startButItemPosition = 0;
        int endButItemPosition = 0;
        if (mLastItemPosition + hCount < getItemCount()) {
            startButItemPosition = mLastItemPosition + 1;
            endButItemPosition = mLastItemPosition + hCount;
        } else {
            startButItemPosition = mLastItemPosition + 1;
            endButItemPosition = getItemCount() - 1;
        }
        if (startButItemPosition != 0 && endButItemPosition != 0) {
            for (int i = mLastItemPosition + 1; i < getItemCount(); i++) {
                View child = recycler.getViewForPosition(i);
                addChild(child, leftOffset, bottomSmallCircleTopOffset, leftOffset + itemWSize, bottomSmallCircleTopOffset + itemHSize, minScale);
                leftOffset += itemWSize;
            }
        }

//有最后一行就得干,下滑,从底部开始布局,解决Android4.4没有设置view Z轴方法
        if (mCurrentOffset < 0 && mLastItemPosition >= (vCount - 1) * hCount) {
            for (int i = mLastItemPosition; i >= 0; i--) {
                View child = recycler.getViewForPosition(i);
                int iwCount = i % hCount + 1;//有几个宽度
                int l = iwCount * itemWSize + getPaddingLeft() - itemWSize;
                int r = iwCount * itemWSize + getPaddingLeft();
                int ihCount = i / hCount + 0;//在第几行

                if (i >= vCount * hCount) {
                    addChild(child, l, bottomSmallCircleTopOffset, r, bottomSmallCircleTopOffset + itemHSize, minScale);
                } else if (i >= (vCount - 1) * hCount) {
                    int interceptOffset = lastOffset + viewTopOffset - mSetOffset;
                    if (interceptOffset > bottomSmallCircleTopOffset) {
                        interceptOffset = bottomSmallCircleTopOffset;
                    }
                    float realScale = 1f - (1f - minScale) * (interceptOffset - lastOffset) / withSmallCircleDist;
                    addChild(child, l, interceptOffset, r, interceptOffset + itemHSize, realScale);

                } else {
                    addChild(child, l, ihCount * itemHSize + viewTopOffset, r, (ihCount + 1) * itemHSize + viewTopOffset, 1f);
                }
            }
            return dy;
        }

        leftOffset = getHorizontalSpace() + getPaddingLeft();
        for (int i = mFirsItemPosition; i <= mLastItemPosition; i++) {
            View child = recycler.getViewForPosition(i);
            int iwCount = i % hCount + 1;//有几个宽度
            int l = iwCount * itemWSize + getPaddingLeft() - itemWSize;
            int r = iwCount * itemWSize + getPaddingLeft();
            if (i - mFirsItemPosition < hCount) {
//第一行
                int oneLineTopOffset = 0;
                float realScale = 1f - (1f - minScale) * frac;
                if (viewTopOffset < mSetOffset) {
                    realScale = 1f - (1f - minScale) * (mSetOffset - viewTopOffset) / withSmallCircleDist; //计算出滑动到小item的百分比
                }
                oneLineTopOffset = viewTopOffset;
                if (viewTopOffset <= smallCircleTopToScreenOffset) {
                    oneLineTopOffset = smallCircleTopToScreenOffset;//上滑到此停住
                }
                addChild(child, l, oneLineTopOffset, l + itemWSize, oneLineTopOffset + itemHSize, realScale);
            } else if (i > mLastItemPosition - buttomItemCount && i >= screenItemCount) {
//画底部小圆,下滑时秒变正常大小item的最后一行,要注意
                float realScale = minScale + (1f - minScale) * frac;

                if (viewTopOffset < lastOffset) {
//计算出滑动到小item的百分比
                    realScale = minScale + (1f - minScale) * (lastOffset - viewTopOffset + smallCircleTopToScreenOffset) / withSmallCircleDist;
                }
                int myTopoffset = bottomSmallCircleTopOffset;//固定住底部小item
                if (viewTopOffset <= bottomSmallCircleTopOffset - itemHSize) {//上滑时跟随上滑
                    myTopoffset = viewTopOffset + itemHSize;
                }
                if (myTopoffset > bottomSmallCircleTopOffset) {//下滑时固定
                    myTopoffset = bottomSmallCircleTopOffset;
                }
                if (mCurrentOffset > totalOffset - itemHSize - 1) {//最后一行正常大小显示
                    myTopoffset = viewTopOffset + itemHSize;
                    realScale = 1f;

                }
                addChild(child, l, myTopoffset, r, myTopoffset + itemHSize, realScale);

            } else { //正常大小item的第二行到倒数第二行
                if (leftOffset + itemWSize <= getHorizontalSpace() + getPaddingLeft()) { //当前行还排列的下
                    addChild(child, leftOffset, viewTopOffset, leftOffset + itemWSize, viewTopOffset + itemHSize, 1f);
                    leftOffset += itemWSize;
                } else {
                    leftOffset = getPaddingLeft();
                    viewTopOffset += itemHSize;
                    addChild(child, leftOffset, viewTopOffset, leftOffset + itemWSize, viewTopOffset + itemHSize, 1f);
                    leftOffset += itemWSize;
                }
            }

        }
        return dy;
    }

完整的源码 https://github.com/dubaofeng/CopyOppoWatcheLayoutManager
不足的地方欢迎在评论指出,欢迎大家分享更优的实现!多多交流,相互学习!

上一篇下一篇

猜你喜欢

热点阅读