美文网首页
实现大众点评列表页快速引导功能

实现大众点评列表页快速引导功能

作者: wp_nine | 来源:发表于2018-01-20 15:28 被阅读0次
    ezgif-5-efd0e04e57.gif

    效果如图展示:
    1.TabLayout 跟着列表移动,直到滚到顶部
    2.列表滚动到对应的内容上时,TabLayout会自动切换
    3.点击TabLayout上任一选项时,列表会自动滚动到相应内容的位置

    该代码在我github上一个开源中的例子,需要源码可以去看看,实现的思路如下

    需要组件:RecyclerView + TabLayout

     RecyclerView mRvDetail;
     TabLayout mTlQuickLeader;
      //....省略对控件的初始化
    

    实现TabLayout跟着列表移动,代码如下:

     //初始化tab的滚动事件
    mRvDetail.addOnScrollListener(new RecyclerView.OnScrollListener() {
    
        @Override
        public void onScrolled(RecyclerView recyclerView,int dx,int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //mItemProvider.tabLayoutRectItem  是在RecyclerView上的一个占位item
            int bindPosition =  mDelegate.getItemManager()
                 .getContentStartIndex(mItemProvider.tabLayoutRectItem);
    
            int showStartPosition = recyclerView
                  .getChildAdapterPosition(recyclerView.getChildAt(0));
            int showEndPosition = recyclerView
                 .getChildAdapterPosition(recyclerView.getChildAt(recyclerView.getChildCount() -1));
    
            if(bindPosition < showStartPosition){
                mTlQuickLeader.setVisibility(View.VISIBLE);
                setQuickLeaderY(0);
            }else if(bindPosition > showEndPosition){
                mTlQuickLeader.setVisibility(View.INVISIBLE);
            }else{
                mTlQuickLeader.setVisibility(View.VISIBLE);
                int bindIndex = bindPosition - showStartPosition;
                View view = recyclerView.getChildAt(bindIndex);
                setQuickLeaderY(
                    Math.max(view.getTop() + getResources().getDisplayMetrics().density *10,0));
            }
        }
      });
    
    //实现tabLayout控件的移动
    private void setQuickLeaderY(float y){
        mTlQuickLeader.setY(y);
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if(y ==0){
                mTlQuickLeader.setElevation(getResources().getDisplayMetrics().density *3);
            }else{
                mTlQuickLeader.setElevation(0);
            }
        }
    }
    

    这一部分的代码实际就是为RecyclerView做OnScrollListener,通过检测对应的占位控件在哪里而不断修改TabLayout 的y值,最终置顶。

    实现列表滚动,TabLayout会自动切换,代码如下:

    /*** 位置提供,划分每一个tab所包含的范围*/
    public interface OnPositionProvider{
        /**
            * @param tabIndex 指定tab的下标
            * @return 获取tab所管的范围组,例:返回[1,5]意思从第二个item到第5个item的范围
        */
        int[] getTabManagerScrop(int tabIndex);
    }
    
    mRecyclcerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView,int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            mCurState = newState;
        }
    
        @Override
        public void onScrolled(RecyclerView recyclerView,int dx,int dy) {
            super.onScrolled(recyclerView, dx, dy);
            if(mCurState == RecyclerView.SCROLL_STATE_IDLE){
                return;
            }
    
            int showCount = recyclerView.getChildCount();
            if(showCount ==0){
                return;
            }
    
            //当前recycler显示的第一个item的position
            int showStartPosition = recyclerView
                .getChildAdapterPosition(recyclerView.getChildAt(0));
    
            //显示的最后一个item的position
            int showEndPosition = recyclerView
                .getChildAdapterPosition(recyclerView.getChildAt(showCount -1));
    
            int bestPosition =0;
            int curShowContentHeight =0;
            boolean isFullContent =false;
    
            for(int i =0;i < mTabLayout.getTabCount();i++){
                int[] scrop =mPositionProvider.getTabManagerScrop(i);
                int startScrop = scrop[0];    //获取tab所包含范围组的第一个item的position
                int endScrop = scrop[1];       //范围组最后一个item的position
    
                //还没有到达下一层的内容范围,不需要再往下判断
                if(showEndPosition < startScrop){
                    break;
                }
    
               //说明已经过了当前结点的选择范围,直接忽略当前的
              if(showStartPosition > endScrop){
                    continue;
                }
    
                //计算出内容在recyclerView的显示上占的position
                int measureStartPosition = Math.max(startScrop,showStartPosition);
                int measureEndPosition = Math.min(endScrop,showEndPosition);
    
                //计算在recyclerView已展示的view 的下标,并且显示的item数量
                int measureStartIndex = measureStartPosition - showStartPosition;
                int measureCount = measureEndPosition - measureStartPosition +1;
    
                int top = recyclerView.getChildAt(measureStartIndex).getTop();
                int bottom = recyclerView.getChildAt( 
                     measureStartIndex + measureCount -1).getBottom();
    
                //计算出显示内容的高度
                int tempHeight = bottom - top;
                //若最底部和最底部的item有超过屏幕未显示的部分,需要进行减去
                int topInsert = Math.min(top,0);
                int bottomInsert = bottom -mRecyclcerView.getHeight();
                bottomInsert = Math.max(bottomInsert,0);
                tempHeight = tempHeight + topInsert - bottomInsert;
    
                //判断是否当前内容范围是否完全显示在屏幕中,如果全部展示,则tabLayout忧先选择在当前的范围    
                if(measureCount == endStrop - startScrop +1 
                    && topInsert ==0 && bottomInsert ==0){
    
                    isFullContent =true;//说明整个内容已经展示在屏幕中
                    curShowContentHeight = tempHeight;
                    bestPosition = i;
                    continue;
                }
    
                if(isFullContent){//说明上一个是完整内容显示,但当前的已经不是完整内容,直接跳过        
                    break;
                }
    
                if(curShowContentHeight < tempHeight){//内容比上次的内容占比相对多    
                    curShowContentHeight = tempHeight;
                    bestPosition = i;
                }else{//内容没展示完整,内容占比没有多过,则说明不需要往下面判断了
                    break;
                }
            }
            changeTabIndex(bestPosition);
        }});
    }
    
    //改变当前tabLayout所选中的tab
    private void changeTabIndex(int tabIndex){
        if(mCurSelectionIndex == tabIndex){
            return;
        }
    
        mCurSelectionIndex = tabIndex;
        mTabLayout.getTabAt(mCurSelectionIndex).select();
    }
    

    该实现也是监听RecyclerView的滚动事件,滚动到对应的范围内会做tab的切换
    切换规则如下:
    1.优先选中在屏幕中内容展示完整的组的tab,若一个界面中出现多个内容展示完整的组,则以下面的为优先
    2.若都没展示完整,则优先选取在屏幕中展示内容占比最多的
    3.若都没有任意内容组展示在屏幕中,则选中第一个

    实现点击TabLayout,代码如下:

    mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            if(mCurState == RecyclerView.SCROLL_STATE_IDLE){
                onTabClick(tab.getPosition());
            }
        }
    
        @Override
        public void onTabUnselected(TabLayout.Tab tab) {
        }
    
        @Override
        public void onTabReselected(TabLayout.Tab tab) {
        }
    });
    
    /**
    * tab被点击
    * @param tabIndex tab的下标
    */
    private void onTabClick(int tabIndex){
        mCurSelectionIndex = tabIndex;
        int[] scrop =mPositionProvider.getTabManagerScrop(tabIndex);
        int startLocation = scrop[0];
        ((LinearLayoutManager)mRecyclcerView.getLayoutManager())
             .scrollToPositionWithOffset( startLocation,mTabLayout.getHeight());
    
    }
    

    该实现比较简单,添加OnTabSelectedListener则可以监听tab的切换,然后使recyclerView移动到对应的内容即可

    相关文章

      网友评论

          本文标题:实现大众点评列表页快速引导功能

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