美文网首页
Android仿Oppo手表应用列表-CopyOppoWatch

Android仿Oppo手表应用列表-CopyOppoWatch

作者: 面向星辰大海的程序员 | 来源:发表于2020-04-10 23:37 被阅读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
    不足的地方欢迎在评论指出,欢迎大家分享更优的实现!多多交流,相互学习!

    相关文章

      网友评论

          本文标题:Android仿Oppo手表应用列表-CopyOppoWatch

          本文链接:https://www.haomeiwen.com/subject/kwajmhtx.html