TabLayout+ViewPager+Fragment的用法

public class TabLayoutActivity extends AppCompatActivity implements{
    TabLayout tabLayout;
    ViewPager viewPager;
    
    List<Fragment> fragments = new ArrayList<>();
    List<String> titles = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_layout);

        tabLayout = findViewById(R.id.tl_tabs);
        viewPager = findViewById(R.id.vp_content);

        fragments.add(MyFragment.newInstance("11111", "11111"));
        fragments.add(MyFragment.newInstance("22222", "22222"));
        fragments.add(MyFragment.newInstance("33333", "33333"));
        fragments.add(MyFragment.newInstance("44444", "44444"));
        fragments.add(MyFragment.newInstance("55555", "55555"));
        titles.add("fragment1");
        titles.add("fragment2");
        titles.add("fragment3");
        titles.add("fragment4");
        titles.add("fragment5");
        viewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return fragments.get(position);
            }

            @Override
            public int getCount() {
                return fragments.size();
            }

            @Override
            public void destroyItem(ViewGroup container, int position, Object object) {
                super.destroyItem(container, position, object);
            }

            @Nullable
            @Override
            public CharSequence getPageTitle(int position) {

                return titles.get(position);
            }
        });

        tabLayout.setupWithViewPager(viewPager);
    }
}

 

private void setupWithViewPager(@Nullable ViewPager viewPager, boolean autoRefresh, boolean implicitSetup) {
        if (this.viewPager != null) {
            if (this.pageChangeListener != null) {
                this.viewPager.removeOnPageChangeListener(this.pageChangeListener);
            }

            if (this.adapterChangeListener != null) {
                this.viewPager.removeOnAdapterChangeListener(this.adapterChangeListener);
            }
        }

        if (this.currentVpSelectedListener != null) {
            this.removeOnTabSelectedListener(this.currentVpSelectedListener);
            this.currentVpSelectedListener = null;
        }

        if (viewPager != null) {
            this.viewPager = viewPager;
            if (this.pageChangeListener == null) {
                this.pageChangeListener = new TabLayout.TabLayoutOnPageChangeListener(this);
            }

            this.pageChangeListener.reset();
            //设置ViewPager分页改变监听
            viewPager.addOnPageChangeListener(this.pageChangeListener);
            this.currentVpSelectedListener = new TabLayout.ViewPagerOnTabSelectedListener(viewPager);
            //设置tab 选择监听
            this.addOnTabSelectedListener(this.currentVpSelectedListener);
            PagerAdapter adapter = viewPager.getAdapter();
            if (adapter != null) {
                this.setPagerAdapter(adapter, autoRefresh);
            }

            if (this.adapterChangeListener == null) {
                this.adapterChangeListener = new TabLayout.AdapterChangeListener();
            }

            this.adapterChangeListener.setAutoRefresh(autoRefresh);
            viewPager.addOnAdapterChangeListener(this.adapterChangeListener);
            this.setScrollPosition(viewPager.getCurrentItem(), 0.0F, true);
        } else {
            this.viewPager = null;
            this.setPagerAdapter((PagerAdapter)null, false);
        }

        this.setupViewPagerImplicitly = implicitSetup;
    }

当使用手指滑动ViewPager时,源码的逻辑是:

 

public boolean onTouchEvent(MotionEvent ev) {
        ..........
        case MotionEvent.ACTION_UP:
                if (this.mIsBeingDragged) {
                    VelocityTracker velocityTracker = this.mVelocityTracker;
                    velocityTracker.computeCurrentVelocity(1000, (float)this.mMaximumVelocity);
                    int initialVelocity = (int)velocityTracker.getXVelocity(this.mActivePointerId);
                    this.mPopulatePending = true;
                    int width = this.getClientWidth();
                    int scrollX = this.getScrollX();
                    ViewPager.ItemInfo ii = this.infoForCurrentScrollPosition();
                    float marginOffset = (float)this.mPageMargin / (float)width;
                    int currentPage = ii.position;
                    float pageOffset = ((float)scrollX / (float)width - ii.offset) / (ii.widthFactor + marginOffset);
                    int activePointerIndex = ev.findPointerIndex(this.mActivePointerId);
                    float x = ev.getX(activePointerIndex);
                    int totalDelta = (int)(x - this.mInitialMotionX);
                    int nextPage = this.determineTargetPage(currentPage, pageOffset, initialVelocity, totalDelta);
                    //触发下一页、上一页
                    this.setCurrentItemInternal(nextPage, true, true, initialVelocity);
                    needsInvalidate = this.resetTouch();
                }
}

按下手指,滑动ViewPager,当抬起手指时,ViewPager的OnTouchEvent方法会触发

this.setCurrentItemInternal(nextPage, true, true, initialVelocity);方法

 void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
        if (this.mAdapter != null && this.mAdapter.getCount() > 0) {
            if (!always && this.mCurItem == item && this.mItems.size() != 0) {
                this.setScrollingCacheEnabled(false);
            } else {
               ........
                boolean dispatchSelected = this.mCurItem != item;
                if (this.mFirstLayout) {
                    this.mCurItem = item;
                    if (dispatchSelected) {
                        this.dispatchOnPageSelected(item);
                    }

                    this.requestLayout();
                } else {
                    this.populate(item);
                    //滚动到指定item
                    this.scrollToItem(item, smoothScroll, velocity, dispatchSelected);
                }

            }
        } else {
            this.setScrollingCacheEnabled(false);
        }
    }

 内部调用了this.scrollToItem(item, smoothScroll, velocity, dispatchSelected);  //滚动到指定item

 private void scrollToItem(int item, boolean smoothScroll, int velocity, boolean dispatchSelected) {
        ViewPager.ItemInfo curInfo = this.infoForPosition(item);
        int destX = 0;
        if (curInfo != null) {
            int width = this.getClientWidth();
            destX = (int)((float)width * Math.max(this.mFirstOffset, Math.min(curInfo.offset, this.mLastOffset)));
        }

        if (smoothScroll) {
            this.smoothScrollTo(destX, 0, velocity);
            if (dispatchSelected) {
                //通知监听器选中
                this.dispatchOnPageSelected(item);
            }
        } else {
            if (dispatchSelected) {
                //通知监听器选中
                this.dispatchOnPageSelected(item);
            }

            this.completeScroll(false);
            this.scrollTo(destX, 0);
            this.pageScrolled(destX);
        }

    }

内部调用了this.dispatchOnPageSelected(item);   //通知监听器选中

private void dispatchOnPageSelected(int position) {
        if (this.mOnPageChangeListener != null) {
            this.mOnPageChangeListener.onPageSelected(position);
        }

        if (this.mOnPageChangeListeners != null) {
            int i = 0;

            for(int z = this.mOnPageChangeListeners.size(); i < z; ++i) {
                ViewPager.OnPageChangeListener listener = (ViewPager.OnPageChangeListener)this.mOnPageChangeListeners.get(i);
                if (listener != null) {
                    //遍历监听器,通知选中position
                    listener.onPageSelected(position);
                }
            }
        }

        if (this.mInternalPageChangeListener != null) {
            this.mInternalPageChangeListener.onPageSelected(position);
        }

    }

遍历ViewPager.OnPageChangeListener监听器,列表,调用onPageSelected(position) 方法通知选中,由于在

TabLayout的setupWithViewPager方法中设置了分页改变监听,即

viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener()),此时会调用TabLayoutOnPageChangeListener的onPageSelected(int position)方法

public static class TabLayoutOnPageChangeListener implements OnPageChangeListener {
        private final WeakReference<TabLayout> tabLayoutRef;
        private int previousScrollState;
        private int scrollState;

        public TabLayoutOnPageChangeListener(TabLayout tabLayout) {
            this.tabLayoutRef = new WeakReference(tabLayout);
        }

        public void onPageScrollStateChanged(int state) {
            this.previousScrollState = this.scrollState;
            this.scrollState = state;
        }

        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            TabLayout tabLayout = (TabLayout)this.tabLayoutRef.get();
            if (tabLayout != null) {
                boolean updateText = this.scrollState != 2 || this.previousScrollState == 1;
                boolean updateIndicator = this.scrollState != 2 || this.previousScrollState != 0;
                tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator);
            }

        }

        public void onPageSelected(int position) {
            TabLayout tabLayout = (TabLayout)this.tabLayoutRef.get();
            if (tabLayout != null && tabLayout.getSelectedTabPosition() != position && position < tabLayout.getTabCount()) {
                //如果是直接通过viewPager.setCurrentItem的方式设置选中的话,需要更新指示器的位置
                boolean updateIndicator = this.scrollState == 0 || this.scrollState == 2 && this.previousScrollState == 0;
                //设置tab的选中
                tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator);
            }

        }

        void reset() {
            this.previousScrollState = this.scrollState = 0;
        }
    }

内部调用了tabLayout的selectTab()方法设置tab的选中

void selectTab(TabLayout.Tab tab, boolean updateIndicator) {
        TabLayout.Tab currentTab = this.selectedTab;
        if (currentTab == tab) {
            if (currentTab != null) {
                this.dispatchTabReselected(tab);
                this.animateToTab(tab.getPosition());
            }
        } else {
            int newPosition = tab != null ? tab.getPosition() : -1;
            if (updateIndicator) {
                if ((currentTab == null || currentTab.getPosition() == -1) && newPosition != -1) {
                    this.setScrollPosition(newPosition, 0.0F, true);
                } else {
                    this.animateToTab(newPosition);
                }

                if (newPosition != -1) {
                    this.setSelectedTabView(newPosition);
                }
            }

            this.selectedTab = tab;
            if (currentTab != null) {
                this.dispatchTabUnselected(currentTab);
            }

            if (tab != null) {
                //下发tab选中
                this.dispatchTabSelected(tab);
            }
        }

    }

private void dispatchTabSelected(@NonNull TabLayout.Tab tab) {
        for(int i = this.selectedListeners.size() - 1; i >= 0; --i) {
//遍历tab选择监听器,触发tab的选中监听
                                ((TabLayout.BaseOnTabSelectedListener)this.selectedListeners.get(i)).onTabSelected(tab);
        }

    }

 遍历tab选择监听器列表,,列表里面包含了setupWithViewPager时设置的监听器TabLayout.ViewPagerOnTabSelectedListener(viewPager)

 public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
        private final ViewPager viewPager;

        public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
            this.viewPager = viewPager;
        }

        public void onTabSelected(TabLayout.Tab tab) {
            //设置viewPager选中
            this.viewPager.setCurrentItem(tab.getPosition());
        }

        public void onTabUnselected(TabLayout.Tab tab) {
        }

        public void onTabReselected(TabLayout.Tab tab) {
        }
    }

这里是通过滑动ViewPager触发的该方法,所以会因为this.mCurItem == item的关系,结束流程。

 

当点击tab时,源码的逻辑是:

  //首先触发TbaLyaout.Tab.select()方法
public void select() {
            if (this.parent == null) {
                throw new IllegalArgumentException("Tab not attached to a TabLayout");
            } else {
                this.parent.selectTab(this);
            }
        }

//触发TbaLyaout.selectTab(TabLayout.Tab tab, boolean updateIndicator)方法
void selectTab(TabLayout.Tab tab, boolean updateIndicator) {
        TabLayout.Tab currentTab = this.selectedTab;
        if (currentTab == tab) {
            if (currentTab != null) {
                this.dispatchTabReselected(tab);
                this.animateToTab(tab.getPosition());
            }
        } else {
            int newPosition = tab != null ? tab.getPosition() : -1;
            if (updateIndicator) {
                if ((currentTab == null || currentTab.getPosition() == -1) && newPosition != -1) {
                    this.setScrollPosition(newPosition, 0.0F, true);
                } else {
                    this.animateToTab(newPosition);
                }

                if (newPosition != -1) {
                    this.setSelectedTabView(newPosition);
                }
            }

            this.selectedTab = tab;
            if (currentTab != null) {
                this.dispatchTabUnselected(currentTab);
            }

            if (tab != null) {
                //下发tab选中
                this.dispatchTabSelected(tab);
            }
        }

    }

private void dispatchTabSelected(@NonNull TabLayout.Tab tab) {
        for(int i = this.selectedListeners.size() - 1; i >= 0; --i) {
//遍历tab选择监听器,触发tab的选中监听
                                ((TabLayout.BaseOnTabSelectedListener)this.selectedListeners.get(i)).onTabSelected(tab);
        }

    }

 遍历tab选择监听器列表,,列表里面包含了setupWithViewPager时设置的监听器TabLayout.ViewPagerOnTabSelectedListener(viewPager) 

 public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
        private final ViewPager viewPager;

        public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
            this.viewPager = viewPager;
        }

        public void onTabSelected(TabLayout.Tab tab) {
            //设置viewPager选中
            this.viewPager.setCurrentItem(tab.getPosition());
        }

        public void onTabUnselected(TabLayout.Tab tab) {
        }

        public void onTabReselected(TabLayout.Tab tab) {
        }
    }

viewPager.setCurrentItem内部调用了setCurrentItemInternal方法

void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {
        if (this.mAdapter != null && this.mAdapter.getCount() > 0) {
            if (!always && this.mCurItem == item && this.mItems.size() != 0) {
                this.setScrollingCacheEnabled(false);
            } else {
                if (item < 0) {
                    item = 0;
                } else if (item >= this.mAdapter.getCount()) {
                    item = this.mAdapter.getCount() - 1;
                }

                int pageLimit = this.mOffscreenPageLimit;
                if (item > this.mCurItem + pageLimit || item < this.mCurItem - pageLimit) {
                    for(int i = 0; i < this.mItems.size(); ++i) {
                        ((ViewPager.ItemInfo)this.mItems.get(i)).scrolling = true;
                    }
                }

                boolean dispatchSelected = this.mCurItem != item;
                if (this.mFirstLayout) {
                    this.mCurItem = item;
                    if (dispatchSelected) {
                        this.dispatchOnPageSelected(item);
                    }

                    this.requestLayout();
                } else {
                    this.populate(item);
                    //滚动到指定item位置
                    this.scrollToItem(item, smoothScroll, velocity, dispatchSelected);
                }

            }
        } else {
            this.setScrollingCacheEnabled(false);
        }
    }

 

private void scrollToItem(int item, boolean smoothScroll, int velocity, boolean dispatchSelected) {
        ViewPager.ItemInfo curInfo = this.infoForPosition(item);
        int destX = 0;
        if (curInfo != null) {
            int width = this.getClientWidth();
            destX = (int)((float)width * Math.max(this.mFirstOffset, Math.min(curInfo.offset, this.mLastOffset)));
        }

        if (smoothScroll) {
            this.smoothScrollTo(destX, 0, velocity);
            if (dispatchSelected) {
                //通知监听器选中
                this.dispatchOnPageSelected(item);
            }
        } else {
            if (dispatchSelected) {
                //通知监听器选中
                this.dispatchOnPageSelected(item);
            }

            this.completeScroll(false);
            this.scrollTo(destX, 0);
            this.pageScrolled(destX);
        }

    }

内部调用了this.dispatchOnPageSelected(item);   //通知监听器选中

private void dispatchOnPageSelected(int position) {
        if (this.mOnPageChangeListener != null) {
            this.mOnPageChangeListener.onPageSelected(position);
        }

        if (this.mOnPageChangeListeners != null) {
            int i = 0;

            for(int z = this.mOnPageChangeListeners.size(); i < z; ++i) {
                ViewPager.OnPageChangeListener listener = (ViewPager.OnPageChangeListener)this.mOnPageChangeListeners.get(i);
                if (listener != null) {
                    //遍历监听器,通知选中position
                    listener.onPageSelected(position);
                }
            }
        }

        if (this.mInternalPageChangeListener != null) {
            this.mInternalPageChangeListener.onPageSelected(position);
        }

    }

遍历ViewPager.OnPageChangeListener监听器,列表,调用onPageSelected(position) 方法通知选中,由于在

TabLayout的setupWithViewPager方法中设置了分页改变监听,即

viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener()),此时会调用TabLayoutOnPageChangeListener的onPageSelected(int position)方法

public static class TabLayoutOnPageChangeListener implements OnPageChangeListener {
        private final WeakReference<TabLayout> tabLayoutRef;
        private int previousScrollState;
        private int scrollState;

        public TabLayoutOnPageChangeListener(TabLayout tabLayout) {
            this.tabLayoutRef = new WeakReference(tabLayout);
        }

        public void onPageSelected(int position) {
            TabLayout tabLayout = (TabLayout)this.tabLayoutRef.get();
            //由于当前选中tab的position=position,所以流程在这里终止
            if (tabLayout != null && tabLayout.getSelectedTabPosition() != position && position < tabLayout.getTabCount()) {
                boolean updateIndicator = this.scrollState == 0 || this.scrollState == 2 && this.previousScrollState == 0;
                tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator);
            }

        }

        void reset() {
            this.previousScrollState = this.scrollState = 0;
        }
    }

至此TabLayout跟ViewPager之间的滑动、选中tab逻辑相关讲解完毕

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐