TabLayout+ViewPager的使用

标签(空格分隔): 安卓UIjava


1、TabLayout的基本使用方式

TabLayout这个控件展现的效果很简单,就是一个水平的布局用来展现不一样的Tab,每一个Tab对应的View通常会结合其余控件一块儿来使用;android

一、TabLayout经常使用方法

TabLayout经常使用的方法就是新增Tab和Tab切换监听: addTab(@NonNull Tab tab)同类的方法:Add a tab to this layout. newTab():Create and return a new {@link Tab} removeTab(Tab tab):Remove a tab from the layout. setTabMode(@Mode int mode) :Set the behavior mode for the Tabs in this layout. setSelectedTabIndicatorColor(@ColorInt int color):Sets the tab indicator's color for the currently selected tab. setSelectedTabIndicatorHeight(int height):Sets the tab indicator's height for the currently selected tab. setScrollPosition(int position, float positionOffset, boolean updateSelectedText):Set the scroll position of the tabs. addOnTabSelectedListener(@NonNull OnTabSelectedListener listener):Add a {@link TabLayout.OnTabSelectedListener} that will be invoked when tab selection changes. TabLayout还提供了其余比较多的UI定制方法,能够直接参考源码;app

二、TabLayout+ViewPager使用

TabLayout控件只是提供了展现不一样Tab的布局,通常场景下,须要根据切换Tab,展现其余效果,使用比较多的就是TabLayout+ViewPager;关于ViewPager的使用见下文ViewPager介绍,目前只介绍是TabLayout+ViewPager如何结合使用; TabLayout提供了以下方法: setupWithViewPager(@Nullable ViewPager viewPager)、setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh)、setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh, boolean implicitSetup) ,这几个方法虽然参数不一样,可是做用是差很少的,Google官方解释:less

/** * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}. * * <p>This method will link the given ViewPager and this TabLayout together so that * changes in one are automatically reflected in the other. This includes scroll state changes * and clicks. The tabs displayed in this layout will be populated * from the ViewPager adapter's page titles.</p> * * <p>If {@code autoRefresh} is {@code true}, any changes in the {@link PagerAdapter} will * trigger this layout to re-populate itself from the adapter's titles.</p> * * <p>If the given ViewPager is non-null, it needs to already have a * {@link PagerAdapter} set.</p> 复制代码

从上面解释能够看到,经过上述的方法,就能够将TabLayout和ViewPager关联起来,任何一个控件的改变都会影响另外一个;ide

2、ViewPager的使用方法

ViewPager这个控件设置的初衷就是实现相似广告左右滑动的时候切换页面的效果,所以这个控件也相似于ListView同样,须要经过Adapater来指定Page的数据源;Google控件无处不在用到适配器模式,想一想适配器模式的应用效果 官方文档介绍以下:函数

/** * Layout manager that allows the user to flip left and right * through pages of data. You supply an implementation of a * {@link PagerAdapter} to generate the pages that the view shows. * * <p>ViewPager is most often used in conjunction with {@link android.app.Fragment}, * which is a convenient way to supply and manage the lifecycle of each page. * There are standard adapters implemented for using fragments with the ViewPager, * which cover the most common use cases. These are * {@link android.support.v4.app.FragmentPagerAdapter} and * {@link android.support.v4.app.FragmentStatePagerAdapter}; each of these * classes have simple code showing how to build a full user interface * with them. */
public class ViewPager extends ViewGroup {
    ......
}
复制代码

从上面的官方解释,总结起来有如下几点:布局

1.ViewPager类直接继承了ViewGroup类,全部它是一个容器类,能够在其中添加其余的view类。 2.ViewPager类须要一个PagerAdapter适配器类给它提供数据。 3.ViewPager常常和Fragment一块儿使用,而且提供了专门的FragmentPagerAdapter和FragmentStatePagerAdapter类供Fragment中的ViewPager使用。性能

从上面的表述能够看到,ViewPager的数据是经过PagerAdapter来提供;动画

2.一、PagerAdapter的使用

首先将官方文档中对PagerAdapter的主要介绍截取出来:ui

/** * Base class providing the adapter to populate pages inside of * a {@link ViewPager}. You will most likely want to use a more * specific implementation of this, such as * {@link android.support.v4.app.FragmentPagerAdapter} or * {@link android.support.v4.app.FragmentStatePagerAdapter}. * <p>When you implement a PagerAdapter, you must override the following methods * at minimum:</p> * <ul> * <li>{@link #instantiateItem(ViewGroup, int)}</li> * <li>{@link #destroyItem(ViewGroup, int, Object)}</li> * <li>{@link #getCount()}</li> * <li>{@link #isViewFromObject(View, Object)}</li> * </ul> * <p>PagerAdapter supports data set changes. Data set changes must occur on the * main thread and must end with a call to {@link #notifyDataSetChanged()} similar * to AdapterView adapters derived from {@link android.widget.BaseAdapter}. A data * set change may involve pages being added, removed, or changing position. The * ViewPager will keep the current page active provided the adapter implements * the method {@link #getItemPosition(Object)}.</p> */
public abstract class PagerAdapter {
    ......
}
复制代码

以上就能够将PagerAdapter概述为:

1.大多数状况下,咱们根据具体状况实现PagerAdapter而且更加具体的适配器; 2.当你实现一个PagerAdapter时,你至少须要重写下面的几个方法:instantiateItem、destroyItem、getCount、isViewFromObject; 3.ViewPager使用回调机制来显示一个更新步骤,而不是直接使用视图回收机制。 4.PagerAdapter支持数据集的改变。数据集的改变必须放在主线程中,而且在结束时调用notifyDataSetChanged()方法,这与经过BaseAdapter适配的AdapterView相似。

上面传达除了使用PagerAdapter最重要的讯息,根据具体的状况咱们实现符合咱们需求的PagerAdapter,须要重写四个方法;同时大部分场景下使用已有的FragmentPagerAdapter和FragmentStatePagerAdapter可以知足需求;PagerAdapter能够和ListView对应的Adapter对比,二者的实质是不同,只不过用于不一样的控件,展现的UI效果也就不同了;

一、简单实现PagerAdapter

实现一个最简单的PagerAdapter代码以下:

public class AdapterViewpager extends PagerAdapter {
    private List<View> mViewList;

    public AdapterViewpager(List<View> mViewList) {
        this.mViewList = mViewList;
    }

    @Override
    public int getCount() {//必须实现
        return mViewList.size();
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {//必须实现
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {//必须实现,实例化
        container.addView(mViewList.get(position));
        return mViewList.get(position);
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {//必须实现,销毁
        container.removeView(mViewList.get(position));
    }
}
复制代码

关注一下四个函数要实现的功能:

/** * Create the page for the given position. The adapter is responsible * for adding the view to the container given here, although it only * must ensure this is done by the time it returns from * {@link #finishUpdate(ViewGroup)}. * @param container The containing View in which the page will be shown. * @param position The page position to be instantiated. * @return Returns an Object representing the new page. This does not * need to be a View, but can be some other container of the page. */
public Object instantiateItem(ViewGroup container, int position) {
    ......
}
复制代码

instantiateItem最重要的做用就是将某个位置对应的Page添加到container,相似RecyclerView.adpter的onCreateView;

/** * Determines whether a page View is associated with a specific key object * as returned by {@link #instantiateItem(ViewGroup, int)}. This method is * required for a PagerAdapter to function properly. * @param view Page View to check for association with <code>object</code> * @param object Object to check for association with <code>view</code> * @return true if <code>view</code> is associated with the key object <code>object</code> */
public abstract boolean isViewFromObject(View view, Object object);
复制代码

isViewFromObject就是返回是否某个Page View和instantiateItem返回的对象对应; destroyItem、getCount也参考如上实现很是容易理解;

二、FragmentPagerAdapter

简单的实现FragmentPagerAdapter代码以下:

public class AdapterFragment extends FragmentPagerAdapter {
    private List<Fragment> mFragments;

    public AdapterFragment(FragmentManager fm, List<Fragment> mFragments) {
        super(fm);
        this.mFragments = mFragments;
    }

    @Override
    public Fragment getItem(int position) {//必须实现
        return mFragments.get(position);
    }

    @Override
    public int getCount() {//必须实现
        return mFragments.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {//选择性实现
        return mFragments.get(position).getClass().getSimpleName();
    }
}
复制代码

要使用FragmentPagerAdapter很简单,做如上实现便可,FragmentPagerAdapter是PagerAdapter的子类,看一下官方文档:

/** * <p>This version of the pager is best for use when there are a handful of * typically more static fragments to be paged through, such as a set of tabs. * The fragment of each page the user visits will be kept in memory, though its * view hierarchy may be destroyed when not visible. This can result in using * a significant amount of memory since fragment instances can hold on to an * arbitrary amount of state. For larger sets of pages, consider * {@link FragmentStatePagerAdapter}. * <p>Subclasses only need to implement {@link #getItem(int)} * and {@link #getCount()} to have a working adapter. */
public abstract class FragmentPagerAdapter extends PagerAdapter {
    ......
}
复制代码

从上面主要的简介能够看到,FragmentPagerAdapter对应的全部pager fragments会一直被持有放在内存,及时对应的Fragment不可见的时候;所以,FragmentPagerAdapter的使用必定要注意,若是FragmentPagerAdapter对应的pages数量比较大,不要使用FragmentPagerAdapter,可能引起内存问题; FragmentPagerAdapter最重要就是实现:

/** * Return the Fragment associated with a specified position. */
public abstract Fragment getItem(int position);
复制代码

三、FragmentStatePagerAdapter

要实现FragmentStatePagerAdapter和实现FragmentPagerAdapter其实是同样的,可是正如前面说到的内存问题,FragmentStatePagerAdapter内部实现了对内存的处理,具体代码参考: FragmentStatePagerAdapter的destroyItem:

@Override
public void destroyItem:(ViewGroup container, int position, Object object) {
    Fragment fragment = (Fragment) object;

    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
            + " v=" + ((Fragment)object).getView());
    while (mSavedState.size() <= position) {
        mSavedState.add(null);
    }
    mSavedState.set(position, fragment.isAdded()
            ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
    mFragments.set(position, null);

    mCurTransaction.remove(fragment);
}
复制代码

FragmentPagerAdapter的destroyItem:

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
            + " v=" + ((Fragment)object).getView());
    mCurTransaction.detach((Fragment)object);
}
复制代码

差异就在mCurTransaction.remove(fragment)mCurTransaction.detach((Fragment)object),参考FragmentTransaction对应的方法就能够理解;更多具体的实现能够参考源码实现;从上面的对比能够看到,FragmentStatePagerAdapter用于当pages比较多或者Fragment内存占用比较多的状况,而FragmentPagerAdapter用于占用内存不大,可是使用频率很高的状况,性能会更高;所以要根据具体状况使用; 那FragmentPagerAdapter占用的pages内存何时会释放呢?参考FragmentTransaction和源码须要进一步分析;

2.二、ViewPager支持更多的展现

Android Design的控件都支持更多的UI展现,ViewPager的效果是左右滑动实现页面翻转,固然也支持过场动画,具体实现参考ViewPager.PageTransformer接口;

/** * Apply a property transformation to the given page. * * @param page Apply the transformation to this page * @param position Position of page relative to the current front-and-center *position of the pager. 0 is front and center. 1 is one full *page position to the right, and -1 is one page position to the left. */
void transformPage(View page, float position);
复制代码

一、提高性能的setOffscreenPageLimit

ViewPager提供了一个setOffscreenPageLimit方法,官方解释以下:

/** * Set the number of pages that should be retained to either side of the * current page in the view hierarchy in an idle state. Pages beyond this * limit will be recreated from the adapter when needed. * * <p>This is offered as an optimization. If you know in advance the number * of pages you will need to support or have lazy-loading mechanisms in place * on your pages, tweaking this setting can have benefits in perceived smoothness * of paging animations and interaction. If you have a small number of pages (3-4) * that you can keep active all at once, less time will be spent in layout for * newly created view subtrees as the user pages back and forth.</p> * * <p>You should keep this limit low, especially if your pages have complex layouts. * This setting defaults to 1.</p> * * @param limit How many pages will be kept offscreen in an idle state. */
public void setOffscreenPageLimit(int limit) {
    if (limit < DEFAULT_OFFSCREEN_PAGES) {
        Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to "
                + DEFAULT_OFFSCREEN_PAGES);
        limit = DEFAULT_OFFSCREEN_PAGES;
    }
    if (limit != mOffscreenPageLimit) {
        mOffscreenPageLimit = limit;
        populate();
    }
}
复制代码

翻译过来几个重点:

1.当设置了限制数量m,当滑动的时候,超过m的page才会每次都要从新建立; 2.实际上这个函数的做用就是会保存相应数量的pages,由于viewpager可能存在频繁的左右滑动,所以若是老是重复绘制UI,性能和用户体验上可能会有影响; 3.仍是综合考虑性能和内存的影响,谨慎使用这个方法;

关于这个方法更多的介绍参考**populate(int newCurrentItem)**方法,能够清楚这个方法最终如何影响ViewPager的使用。

2.三、基于Java范形实现ViewPager

基于Java的范性来实现ViewPager: PagerAdapter:

public class QuickPageAdapter<T extends View> extends PagerAdapter {
  private List<T> mList;

  public QuickPageAdapter(List<T> mList) {
      this.mList = mList;
  }

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

  @Override
  public boolean isViewFromObject(View view, Object object) {
      return object == view;
  }

  @Override
  public Object instantiateItem(ViewGroup container, int position) {
      container.addView(mList.get(position));
      return mList.get(position);
  }

  @Override
  public void destroyItem(ViewGroup container, int position, Object object) {
      container.removeView(mList.get(position));
  }
复制代码

FragmentPagerAdapter:

public class QuickFragmentPageAdapter<T extends Fragment> extends FragmentPagerAdapter {
  private List<T> mList;
  private String[] mStrings;

  /** * @param fm * @param list * @param titles PageTitles */
  public QuickFragmentPageAdapter(FragmentManager fm, List<T> list, String[] titles) {
      super(fm);
      mList = list;
      mStrings = titles;
  }

  @Override
  public Fragment getItem(int position) {
      return mList.get(position);
  }

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

  @Override
  public CharSequence getPageTitle(int position) {
      return mStrings == null ? super.getPageTitle(position) : mStrings[position];
  }
}
复制代码
相关文章
相关标签/搜索