Android Jetpack :ViewPager2 结合TabLayout使用

Android 有个ViewPager,但是在Androidx 的jetpack 中出现了ViewPager2,官方也建议使用这个类替代ViewPager,但是之前的TabLayout 都是绑定的ViewPager :

  public void setupWithViewPager(@Nullable ViewPager viewPager) {
    this.setupWithViewPager(viewPager, true);
  }

  public void setupWithViewPager(@Nullable ViewPager viewPager, boolean autoRefresh) {
    this.setupWithViewPager(viewPager, autoRefresh, false);
  }

那么怎么使用ViewPager2 和TabLayout呢?

方案一:

网上已经给出了一个教程,就是 Copy 一个类叫TabLayoutMediator的类,然后放在固定包名下面:com.google.android.material.tabs:

TabLayoutMediator.png

方案二:

更新material 库 到com.google.android.material:material:1.1.0-alpha09@aar及以上,你就会看到Google 爸爸已经自己更新到上面了,好东西不会留着自己用了。

TabLayoutMediator

用法

public class TestConfigActivity extends BaseActivity {
  private List mFragments;
  private final static String[] NAMES = {"a", "b", "c", "d"};

  private TabLayoutMediator mLayoutMediator;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.test_main);
    TabLayout tabLayout = findViewById(R.id.tab_layout);
    initData();
    ViewPager2 viewPager2 = findViewById(R.id.test_view_page);
    viewPager2.setAdapter(new FragmentStateAdapter(this) {
      @NonNull
      @Override
      public Fragment createFragment(int position) {
        return mFragments.get(position);
      }

      @Override
      public int getItemCount() {
        return mFragments.size();
      }
    });

    mLayoutMediator = new TabLayoutMediator(tabLayout, viewPager2, false, true,
        (tab, position) -> tab.setText(NAMES[position]));

    mLayoutMediator.attach();
  }


  private void initData() {
    mFragments = new ArrayList<>();
    mFragments.add(new TestConfigFragment());
    mFragments.add(new TestSwitchFragment());
    mFragments.add(new TestUIFragment());
    mFragments.add(new TestOtherFragment());
  }



  @Override
  protected void onDestroy() {
    super.onDestroy();
    mLayoutMediator.detach();
  }
}

这个是源码,为啥我会贴源码呢,因为之前这个类是在 Google内部的库的frameWork层,只有内部的一些框架能用到,不想更新 material库的可以直接cp源码,定义好包名的方式去搞。

TabLayoutMediator 源码

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.material.tabs;

import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_DRAGGING;
import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE;
import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import java.lang.ref.WeakReference;

/**
 * A mediator to link a TabLayout with a ViewPager2. The mediator will synchronize the ViewPager2's
 * position with the selected tab when a tab is selected, and the TabLayout's scroll position when
 * the user drags the ViewPager2.
 *
 * 

Establish the link by creating an instance of this class, make sure the ViewPager2 has an * adapter and then call {@link #attach()} on it. When creating an instance of this class, you must * supply an implementation of {@link OnConfigureTabCallback} in which you set the text of the tab, * and/or perform any styling of the tabs that you require. */ public final class TabLayoutMediator { @NonNull private final TabLayout tabLayout; @NonNull private final ViewPager2 viewPager; private final boolean autoRefresh; private final OnConfigureTabCallback onConfigureTabCallback; private RecyclerView.Adapter adapter; private boolean attached; private TabLayoutOnPageChangeCallback onPageChangeCallback; private TabLayout.OnTabSelectedListener onTabSelectedListener; private RecyclerView.AdapterDataObserver pagerAdapterObserver; /** * A callback interface that must be implemented to set the text and styling of newly created * tabs. */ public interface OnConfigureTabCallback { /** * Called to configure the tab for the page at the specified position. Typically calls {@link * TabLayout.Tab#setText(CharSequence)}, but any form of styling can be applied. * * @param tab The Tab which should be configured to represent the title of the item at the given * position in the data set. * @param position The position of the item within the adapter's data set. */ void onConfigureTab(@NonNull TabLayout.Tab tab, int position); } public TabLayoutMediator( @NonNull TabLayout tabLayout, @NonNull ViewPager2 viewPager, @NonNull OnConfigureTabCallback onConfigureTabCallback) { this(tabLayout, viewPager, true, onConfigureTabCallback); } public TabLayoutMediator( @NonNull TabLayout tabLayout, @NonNull ViewPager2 viewPager, boolean autoRefresh, @NonNull OnConfigureTabCallback onConfigureTabCallback) { this.tabLayout = tabLayout; this.viewPager = viewPager; this.autoRefresh = autoRefresh; this.onConfigureTabCallback = onConfigureTabCallback; } /** * Link the TabLayout and the ViewPager2 together. * * @throws IllegalStateException If the mediator is already attached, or the ViewPager2 has no * adapter. */ public void attach() { if (attached) { throw new IllegalStateException("TabLayoutMediator is already attached"); } adapter = viewPager.getAdapter(); if (adapter == null) { throw new IllegalStateException( "TabLayoutMediator attached before ViewPager2 has an " + "adapter"); } attached = true; // Add our custom OnPageChangeCallback to the ViewPager onPageChangeCallback = new TabLayoutOnPageChangeCallback(tabLayout); viewPager.registerOnPageChangeCallback(onPageChangeCallback); // Now we'll add a tab selected listener to set ViewPager's current item onTabSelectedListener = new ViewPagerOnTabSelectedListener(viewPager); tabLayout.addOnTabSelectedListener(onTabSelectedListener); // Now we'll populate ourselves from the pager adapter, adding an observer if // autoRefresh is enabled if (autoRefresh) { // Register our observer on the new adapter pagerAdapterObserver = new PagerAdapterObserver(); adapter.registerAdapterDataObserver(pagerAdapterObserver); } populateTabsFromPagerAdapter(); // Now update the scroll position to match the ViewPager's current item tabLayout.setScrollPosition(viewPager.getCurrentItem(), 0f, true); } /** Unlink the TabLayout and the ViewPager */ public void detach() { adapter.unregisterAdapterDataObserver(pagerAdapterObserver); tabLayout.removeOnTabSelectedListener(onTabSelectedListener); viewPager.unregisterOnPageChangeCallback(onPageChangeCallback); pagerAdapterObserver = null; onTabSelectedListener = null; onPageChangeCallback = null; attached = false; } @SuppressWarnings("WeakerAccess") void populateTabsFromPagerAdapter() { tabLayout.removeAllTabs(); if (adapter != null) { int adapterCount = adapter.getItemCount(); for (int i = 0; i < adapterCount; i++) { TabLayout.Tab tab = tabLayout.newTab(); onConfigureTabCallback.onConfigureTab(tab, i); tabLayout.addTab(tab, false); } // Make sure we reflect the currently set ViewPager item if (adapterCount > 0) { int currItem = viewPager.getCurrentItem(); if (currItem != tabLayout.getSelectedTabPosition()) { tabLayout.getTabAt(currItem).select(); } } } } /** * A {@link ViewPager2.OnPageChangeCallback} class which contains the necessary calls back to the * provided {@link TabLayout} so that the tab position is kept in sync. * *

This class stores the provided TabLayout weakly, meaning that you can use {@link * ViewPager2#registerOnPageChangeCallback(ViewPager2.OnPageChangeCallback)} without removing the * callback and not cause a leak. */ private static class TabLayoutOnPageChangeCallback extends ViewPager2.OnPageChangeCallback { private final WeakReference tabLayoutRef; private int previousScrollState; private int scrollState; TabLayoutOnPageChangeCallback(TabLayout tabLayout) { tabLayoutRef = new WeakReference<>(tabLayout); reset(); } @Override public void onPageScrollStateChanged(final int state) { previousScrollState = scrollState; scrollState = state; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { TabLayout tabLayout = tabLayoutRef.get(); if (tabLayout != null) { // Only update the text selection if we're not settling, or we are settling after // being dragged boolean updateText = scrollState != SCROLL_STATE_SETTLING || previousScrollState == SCROLL_STATE_DRAGGING; // Update the indicator if we're not settling after being idle. This is caused // from a setCurrentItem() call and will be handled by an animation from // onPageSelected() instead. boolean updateIndicator = !(scrollState == SCROLL_STATE_SETTLING && previousScrollState == SCROLL_STATE_IDLE); tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator); } } @Override public void onPageSelected(final int position) { TabLayout tabLayout = tabLayoutRef.get(); if (tabLayout != null && tabLayout.getSelectedTabPosition() != position && position < tabLayout.getTabCount()) { // Select the tab, only updating the indicator if we're not being dragged/settled // (since onPageScrolled will handle that). boolean updateIndicator = scrollState == SCROLL_STATE_IDLE || (scrollState == SCROLL_STATE_SETTLING && previousScrollState == SCROLL_STATE_IDLE); tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator); } } void reset() { previousScrollState = scrollState = SCROLL_STATE_IDLE; } } /** * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back to the * provided {@link ViewPager2} so that the tab position is kept in sync. */ private static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener { private final ViewPager2 viewPager; ViewPagerOnTabSelectedListener(ViewPager2 viewPager) { this.viewPager = viewPager; } @Override public void onTabSelected(TabLayout.Tab tab) { viewPager.setCurrentItem(tab.getPosition(), true); } @Override public void onTabUnselected(TabLayout.Tab tab) { // No-op } @Override public void onTabReselected(TabLayout.Tab tab) { // No-op } } private class PagerAdapterObserver extends RecyclerView.AdapterDataObserver { PagerAdapterObserver() {} @Override public void onChanged() { populateTabsFromPagerAdapter(); } @Override public void onItemRangeChanged(int positionStart, int itemCount) { populateTabsFromPagerAdapter(); } @Override public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) { populateTabsFromPagerAdapter(); } @Override public void onItemRangeInserted(int positionStart, int itemCount) { populateTabsFromPagerAdapter(); } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { populateTabsFromPagerAdapter(); } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { populateTabsFromPagerAdapter(); } } }

你可能感兴趣的:(Android Jetpack :ViewPager2 结合TabLayout使用)