PagerSlidingTabStrip 源码解析

PagerSlidingTabStrip 源码解析

本文为 Android 开源项目源码解析 中 ${PagerSlidingTabStrip} 部分

项目地址:[PagerSlidingTabStrip]({https://github.com/astuetz/PagerSlidingTabStrip}),分析的版本:1.0.1,Demo 地址:PagerSlidingTabStrip Demo

分析者:ayyb1988,分析状态:已完成

校对者:,校对状态:未开始

Demo效果展示如下

上面tab为textviewtab。下面tab为icontab。

1. 总体设计

pagerSlidingTabStrip实现联动效果的原理是,它引用了ViewPager的OnPageChangeListener。

但是viewpager注册的listener不是自身的OnPageChangeListener,而是pagerSlidingTabStrip内部类PageListener。

通过PageListener实现对对viewpager和tab的封装。从而实现滑动联动效果。

可以设置tab的类型为textview还是icon。对于textview可设置字库属性。

通过提供方法如滑动指示器 下划线 tab风格线 tab权重等达到自定义的效果。

2. 流程图

3. 功能介绍

3.1 特性介绍

  • 兼容Android支持库中的ViewPager 的一个滑动分页指示器的控件。
  • 滑动时实现TextView颜色状态的联动
  • 支持文字导航指示,可指定选中的pager导航字体属性
  • 支持图片导航指示,可高亮选中pager页导航背景
  • 很好的扩展性

3.2 集成及使用指南

3.2.1
在 gradle 中
dependencies {
    compile ‘com.astuetz:pagerslidingtabstrip:1.0.1‘
}
3.2.2 在layout布局文件中引入PagerSlidingTabStrip,通常布局在viewpager上面。如下:
" data-snippet-id="ext.70f9ac1ecd734202407c447517d62cfb" data-snippet-saved="false" data-codota-status="done"><com.astuetz.PagerSlidingTabStrip
    android:id="@+id/tabs"
    android:layout_width="match_parent"
    android:layout_height="48dip" />
3.2.3 在oncreate方法中(或Fragment的onCreateView)中,绑定PagerSlidingTabStrip到Viewpager
 // 初始化ViewPager和Adapter
 ViewPager pager = (ViewPager) findViewById(R.id.pager);
 pager.setAdapter(new TestAdapter(getSupportFragmentManager()));

 // 绑定PagerSlidingTabStrip到ViewPager上
 PagerSlidingTabStrip tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
 tabs.setViewPager(pager);
3.2.4 如果你的view pager使用到OnPageChangeListener。你应该通过这个PagerSlidingTabStrip控件设置而不是Viewpager。如下:
 // continued from above
 tabs.setOnPageChangeListener(mPageChangeListener);

3.3 用户定制

根据你的需要修改下面的值

* pstsIndicatorColor 滑动指示器的颜色

* pstsUnderlineColor 整个view【PagerSlidingTabStrip】下划线的颜色

* pstsDividerColor tabs之间分割线的颜色

* pstsIndicatorHeight 滑动指示器的高度

* pstsUnderlineHeight 整个View【PagerSlidingTabStrip】下滑线的高度

* pstsDivviderPadding 分割线上部、下部的内间距

* pstsTabPaddingLeftRight 每个tab左右内间距

* pstsScrollOffset 选中tab的滑动的距离

* pstsTabBackground 每个tab的背景图片,使用StateListDrawable

* pstsShouldExpand 如果设置为true,每个tab的宽度拥有相同的权重

* pstsTextAllCaps 如果设置为true,所有的tab字体转为大写

###4. 详细设计

####4.1 类详细介绍

#### 4.2 核心方法及功能介绍

pagerSlidingTabStrip实现联动效果的原理是,它引用了ViewPager的 OnPageChangeListener。但是viewpager注册的listener不是自身的OnPageChangeListener,而是pagerSlidingTabStrip内部类PageListener。通过PageListener实现对对viewpager和tab的封装。从而实现滑动联动效果。下面结合代码详细说明

 private class PageListener implements OnPageChangeListener {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            //当前view的位置也即tab的位置
            currentPosition = position;
            //当前view滑动的距离 。其中currentPositionOffset 为float,介于0~1 代表相对于tab宽偏移的比例
            currentPositionOffset = positionOffset;
            //根据上面得到的view的位置和偏移位置,来同步tab的位置和偏移距离。
            scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));
            //重绘view,实现tab滑动的效果。
            invalidate();
            //下面的delegatePageListener就是我们设置的viewpager.setOnPageChangeListener.而现在把它封装在整个pagerSlidingTabStrip中,实现viewpager滑动的效果。
            if (delegatePageListener != null) {
                delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
            }
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            //滑动结束。positionOffset归零
            if (state == ViewPager.SCROLL_STATE_IDLE) {
                scrollToChild(pager.getCurrentItem(), 0);
            }
            //调用viewpager.setOnPageChangeListener
            if (delegatePageListener != null) {
                delegatePageListener.onPageScrollStateChanged(state);
            }
        }

        @Override
        public void onPageSelected(int position) {
        //调用viewpager.setOnPageChangeListener
            if (delegatePageListener != null) {
                delegatePageListener.onPageSelected(position);
            }
        }

    }

scrollToChild,tab的滑动位置 实现如下:

 0 || offset > 0) {
            newScrollX -= scrollOffset;
        }

        //滑动到的位置。
        if (newScrollX != lastScrollX) {
            lastScrollX = newScrollX;
            scrollTo(newScrollX, 0);
        }

    }" data-snippet-id="ext.64c19771712837aa261f444b2c108f9e" data-snippet-saved="false" data-csrftoken="arb3GGVt-tBpiqIxu8pnq28rmcGyYqTIR_v4" data-codota-status="done">    private void scrollToChild(int position, int offset) {

        if (tabCount == 0) {
            return;
        }

        int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset;

        if (position > 0 || offset > 0) {
            newScrollX -= scrollOffset;
        }

        //滑动到的位置。
        if (newScrollX != lastScrollX) {
            lastScrollX = newScrollX;
            scrollTo(newScrollX, 0);
        }

    }

接下来说下 addTextTab **addIconTab**66。即tab是text还是icon。如果是icon的话,通过viewpager的adapter实现接口IconTabProvider。来确定icontab。

        for (int i = 0; i < tabCount; i++) {

            if (pager.getAdapter() instanceof IconTabProvider) {
                addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i));
            } else {
                addTextTab(i, pager.getAdapter().getPageTitle(i).toString());
            }

        }

####4.3 View绘制机制

请参考公共技术点viewdrawflow部分

在pagerSlidingTabStrip中重写了onDraw函数

绘画滑动指示器; 绘画整个tabs下划线; 绘画tab之间间隔线。代码如下

 0f && currentPosition  // draw indicator line

        rectPaint.setColor(indicatorColor);

        // default: line below current tab
        View currentTab = tabsContainer.getChildAt(currentPosition);
        float lineLeft = currentTab.getLeft();
        float lineRight = currentTab.getRight();

        // if there is an offset, start interpolating left and right coordinates between current and next tab
        if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {

            View nextTab = tabsContainer.getChildAt(currentPosition + 1);
            final float nextTabLeft = nextTab.getLeft();
            final float nextTabRight = nextTab.getRight();

            lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft);
            lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight);
        }

        canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint);

        // draw underline

        rectPaint.setColor(underlineColor);
        canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);

        // draw divider

        dividerPaint.setColor(dividerColor);
        for (int i = 0; i < tabCount - 1; i++) {
            View tab = tabsContainer.getChildAt(i);
            canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint);
        }

###5. 杂谈

该库有很好的自定义性和扩展性。比如修改滑动指示器为一张图片【目前为设定颜色值和高度来决定】

###参考文献

ViewPagerindicator 源码解析

View的绘制:

  1. How Android Draws Views
  2. View 绘制流程
  3. Android View绘制流程
  4. Android中View绘制流程以及invalidate()等相关方法分析
  5. Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)

TOUCH事件处理

  1. View 事件传递
  2. Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制
  3. Android 中Touch(触屏)事件传递机制

Demo 下载

时间: 2024-08-02 15:50:39

PagerSlidingTabStrip 源码解析的相关文章

Android 开源项目源码解析(第二期)

Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations 源码解析 SlidingMenu 源码解析 Cling 源码解析 BaseAdapterHelper 源码分析 Side Menu.Android 源码解析 DiscreteSeekBar 源码解析 CalendarListView 源码解析 PagerSlidingTabStrip 源码解析 公共

[转]ViewPagerindicator 源码解析

转自:http://www.codekk.com/open-source-project-analysis/detail/Android/lightSky/ViewPagerindicator%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90 ViewPagerindicator 源码解析 本文为 Android 开源项目源码解析 中 ViewPagerindicator 部分项目地址:ViewPagerIndicator,分析的版本:8cd549f,Demo 地址:

ChrisRenke/DrawerArrowDrawable源码解析

转载请注明出处http://blog.csdn.net/crazy__chen/article/details/46334843 源码下载地址http://download.csdn.net/detail/kangaroo835127729/8765757 这次解析的控件DrawerArrowDrawable是一款侧拉抽屉效果的控件,在很多应用上我们都可以看到(例如知乎),控件的github地址为https://github.com/ChrisRenke/DrawerArrowDrawable

五.jQuery源码解析之jQuery.extend(),jQuery.fn.extend()

给jQuery做过扩展或者制作过jQuery插件的人这两个方法东西可能不陌生.jQuery.extend([deep],target,object1,,object2...[objectN]) jQuery.fn.extend([deep],target,object1,,object2...[objectN])这两个属性都是用于合并两个或多个对象的属性到target对象.deep是布尔值,表示是否进行深度合并,默认是false,不执行深度合并.通过这种方式可以在jQuery或jQuery.fn

eclipse中导入jdk源码、SpringMVC注解@RequestParam、SpringMVC文件上传源码解析、ajax上传excel文件

eclipse中导入jdk源码:http://blog.csdn.net/evolly/article/details/18403321, http://www.codingwhy.com/view/799.html. ------------------------------- SpringMVC注解@RequestParam:http://825635381.iteye.com/blog/2196911. --------------------------- SpringMVC文件上传源

String源码解析(一)

本篇文章内的方法介绍,在方法的上面的注释讲解的很清楚,这里只阐述一些要点. Java中的String类的定义如下: 1 public final class String 2 implements java.io.Serializable, Comparable<String>, CharSequence { ...} 可以看到,String是final的,而且继承了Serializable.Comparable和CharSequence接口. 正是因为这个特性,字符串对象可以被共享,例如下面

Flume-ng源码解析之Channel组件

如果还没看过Flume-ng源码解析之启动流程,可以点击Flume-ng源码解析之启动流程 查看 1 接口介绍 组件的分析顺序是按照上一篇中启动顺序来分析的,首先是Channel,然后是Sink,最后是Source,在开始看组件源码之前我们先来看一下两个重要的接口,一个是LifecycleAware ,另一个是NamedComponent 1.1 LifecycleAware @[email protected] interface LifecycleAware {  public void s

Spring源码解析-applicationContext

Demo uml类图 ApplicationContext ApplicationListener 源码解析 主流程 obtainFreshBeanFactory prepareBeanFactory invokeBeanFactoryPostProcessors registerBeanPostProcessors registerListeners finishRefresh 总结 在已经有BeanFactory可以完成Ioc功能情况下,spring又提供了ApplicationContex

socketserver源码解析和协程版socketserver

来,贴上一段代码让你仰慕一下欧socketserver的魅力,看欧怎么完美实现多并发的魅力 client import socket ip_port = ('127.0.0.1',8009) sk = socket.socket() sk.connect(ip_port) sk.settimeout(5) while True: data = sk.recv(1024) print('receive:',data.decode()) inp = input('please input:') sk