PagerSlidingTabStrip源码解析

PagerSlidingTabStrip源码解析


集成使用

导包

在Android Studio中,直接在build.gradle文件中增加如下依赖:

dependencies { 
    compile 'com.astuetz:pagerslidingtabstrip:1.0.1' 
}

XML文件布局

在XML文件中使用时,PagerSlidingTabStrip通常声明在ViewPager的上方.

<com.yunos.sprd.appstore.widget.PagerSlidingTabStrip
    android:id="@+id/tabs"
    android:layout_width="match_parent"
    android:layout_height="@dimen/title_page_indicator_height"
    android:background="@color/colorCCFF7E3B"
    android:textColor="@android:color/white"
    android:textSize="@dimen/tab_title_textsize"
    android:focusable="true"
    app:pstsActivateTextColor="@color/colorFFFFFFFF"
    app:pstsDeactiveTextColor="@color/color99FFFFFF"
    app:pstsIndicatorColor="@android:color/white"
    app:pstsIndicatorHeight="@dimen/psts_indicator_height"
    app:pstsShouldExpand="true"
    app:pstsTabSwitch="true"
    app:pstsTabPaddingLeftRight="0px"/>

<android.support.v4.view.ViewPager
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1" />

Activity或Fragment中初始化

在Activity的onCreate方法中(或者Fragment的onCreateView)中,绑定PagerSlidingTabStrip到ViewPager.

// Initialize the ViewPager and set an adapter
ViewPager pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(new TestAdapter(getSupportFragmentManager()));

// Bind the tabs to the ViewPager
PagerSlidingTabStrip tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);
tabs.setViewPager(pager);

// set OnPageChangeListener on PagerSlidingTabStrip instead of ViewPager
tabs.setOnPageChangeListener(mPageChangeListener);

源码分析

构造函数

PagerSlidingTabStrip在构造函数中主要是对变量进行初始化:

public PagerSlidingTabStrip(Context context) {
     this(context, null);
 }

 public PagerSlidingTabStrip(Context context, AttributeSet attrs) {
     this(context, attrs, 0);
 }

 public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) {
     super(context, attrs, defStyle);

     // 允许组件中的控件去充满自己
     setFillViewport(true);
     setWillNotDraw(false);

     // 定义一个横向的LinearLayout
     tabsContainer = new LinearLayout(context);
     tabsContainer.setOrientation(LinearLayout.HORIZONTAL);
     tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
     addView(tabsContainer);

     DisplayMetrics dm = getResources().getDisplayMetrics();

     // 获取预设的属性值
     scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm);
     indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm);
     underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm);
     dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm);
     tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm);
     dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm);
     tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm);

     // get system attrs (android:textSize and android:textColor)
     TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
     tabTextSize = a.getDimensionPixelSize(0, tabTextSize);
     tabTextColor = a.getColor(1, tabTextColor);
     a.recycle();

     // 获取自定义属性值
     a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip);
     indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsIndicatorColor, indicatorColor);
     underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsUnderlineColor, underlineColor);
     dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor, dividerColor);
     indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight, indicatorHeight);
     underlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsUnderlineHeight, underlineHeight);
     dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerPadding, dividerPadding);
     tabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeftRight, tabPadding);
     tabBackgroundResId = a.getResourceId(R.styleable.PagerSlidingTabStrip_pstsTabBackground, tabBackgroundResId);
     shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand, shouldExpand);
     scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsScrollOffset, scrollOffset);
     textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps);
     tabSwitch = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsTabSwitch, tabSwitch);
     tabActiveTextColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsActivateTextColor, tabActiveTextColor);
     tabDeactiveTextColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsDeactiveTextColor, tabDeactiveTextColor);
     a.recycle();

     // Tab的滑动指示器画笔
     rectPaint = new Paint();
     rectPaint.setAntiAlias(true);
     rectPaint.setStyle(Style.FILL);

     // 垂直分隔线的画笔
     dividerPaint = new Paint();
     dividerPaint.setAntiAlias(true);
     dividerPaint.setStrokeWidth(dividerWidth);

     // 设置默认的Tab属性
     defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
     // 设置均分的tab属性
     expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);

     if (locale == null) {
         locale = getResources().getConfiguration().locale;
     }
 }

onDraw方法实现

中文注释的onDraw函数源码如下:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    if (isInEditMode() || tabCount == 0) {
        return;
    }

    final int height = getHeight();

    // 设置滑动指示器画笔的颜色
    rectPaint.setColor(indicatorColor);

    // 获取当前的Tab,确定Tab的左右坐标,在当前Tab的下面绘制指示器
    View currentTab = tabsContainer.getChildAt(currentPosition);
    float lineLeft = currentTab.getLeft();
    float lineRight = currentTab.getRight();

    // 如果ViewPager存在滑动偏移量,则需要根据偏移量来更新Tab的左右坐标
    // currentPositionOffset为float,介于0~1,代表相对于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);
    }

    // 绘制当前tab下面的指示器
    canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint);

    // 绘制整个导航栏
    rectPaint.setColor(underlineColor);
    canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);

    // 绘制垂直分隔线
    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);
    }
}

联动实现

接下来,我们进入绘制的关键代码。PagerSlidingTabStrip是如何与ViewPager实现滑动联动的.
PagerSlidingTabStrip之所以能实现和ViewPager的联动,是因为它引用了ViewPager的OnPageChangeListener.关键代码:

public void setViewPager(ViewPager pager) {
    this.pager = pager;

    if (pager.getAdapter() == null) {
        throw new IllegalStateException("ViewPager does not have adapter instance.");
    }

    pager.addOnPageChangeListener(pageListener);

    notifyDataSetChanged();
}

从代码中,我们可以分析出:PagerSlidingTabStrip保持了ViewPager的句柄,同时为ViewPager设置了一个OnPageChangeListener接口.
这个接口的中文注释源码如下:

/**
 * delegatePageListener是用户设置的OnPageChangeListener,因为不响应滑动实现,我将其代码删除了.
 */
private class PageListener implements OnPageChangeListener {

    /**
     * 这个方法在屏幕滚动时不断被调用
     * @param position 当前页面.
     * @param positionOffset 当前页面的偏移比例.如果页面向右翻动,这个值不断变大,最后在趋近于1的情况下突变为0.如果页面向左翻动,这个值不断变小,最后Wie0.
     * @param positionOffsetPixels 当前页面因为滑动偏移了多少像素.
     */
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        currentPosition = position;
        currentPositionOffset = positionOffset;
        // 根据View的位置和偏移量,来同步LinearLayout容器中tab的位置和偏移量
        scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));

        invalidate();
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            scrollToChild(pager.getCurrentItem(), 0);
        }
    }

    @Override
    public void onPageSelected(int position) {
        if (tabSwitch) {
            updateActivateTab(position);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/zinss26914/article/details/73810731