使用PagerSlidingTabStrip实现顶部导航栏

在开发中,我们有时会遇到顶部导航栏滑动切换页面的设计,如网易新闻。实现的方式有很多种,今天我们使用PagerSlidingTabStrip配合ViewPager实现顶部导航栏。

效果图如下。
使用PagerSlidingTabStrip实现顶部导航栏_第1张图片

PagerSlidingTabStrip是github上的一个开源项目,项目地址如下。
https://github.com/astuetz/PagerSlidingTabStrip

(一)PagerSlidingTabStrip的使用

在使用之前,我们先来看一下PagerSlidingTabStrip中的自定义属性。

    
    
    
    
    
    
    
    
    
    
    

各属性的详细介绍如下。
pstsIndicatorColor:滑动条的颜色。
pstsIndicatorHeight:滑动条的高度。
pstsUnderlineColor:底部线条的颜色。(底部线条会填充屏幕宽度)
pstsUnderlineHeight:底部线条的高度。
pstsDividerColor:tab之间的竖直分割线的颜色。
pstsDividerPadding:tab之间的竖直分割线,距离顶部和底部的距离,即它的paddingTop和paddingBottom。
pstsTabPaddingLeftRight:单个tab内部的左间距和右间距,即它的paddingLeft和paddingRight。
pstsTabBackground:单个tab的背景。
pstsScrollOffset:当前tab滚动的偏移量。
pstsShouldExpand:设置为ture,每个tab的权重一样,均分屏幕宽度,默认值false。
pstsTextAllCaps:是否将tab中的字母转换成大写,默认值true。

下面,我们将PagerSlidingTabStrip使用到具体项目中。


首先将PagerSlidingTabStrip添加到工程module的gradle中。
dependencies {
    compile 'com.astuetz:pagerslidingtabstrip:1.0.1'
}

接下来添加布局文件。

最后实现逻辑代码。

package net.csdn.blog.ruancoder;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.view.Window;

import com.astuetz.PagerSlidingTabStrip;

public class MainActivity extends FragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        final PagerSlidingTabStrip tabStrip = (PagerSlidingTabStrip) findViewById(R.id.tab);
        ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);

        Fragment[] fragments = {new NewsFragment(), new TechFragment(), new FinanceFragment(), new InternetFragment(), new 
                PhoneFragment()};
        String[] titles = {"头条", "科技", "财经", "互联网", "手机"};
        TabPagerAdapter adapter = new TabPagerAdapter(getSupportFragmentManager(), fragments, titles);
        viewPager.setAdapter(adapter);
        tabStrip.setViewPager(viewPager);
    }
}

Activity中使用到的TabPagerAdapter类:

package net.csdn.blog.ruancoder;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class TabPagerAdapter extends FragmentPagerAdapter {
    private Fragment[] mFragments;
    private String[] mTitles;

    public TabPagerAdapter(FragmentManager fm, Fragment[] fragments, String[] titles) {
        super(fm);
        this.mFragments = fragments;
        this.mTitles = titles;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragments[position];
    }

    @Override
    public int getCount() {
        return mFragments.length;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mTitles[position];
    }
}

(二)PagerSlidingTabStrip的源码分析

(1).类的声明。
package com.astuetz;

public class PagerSlidingTabStrip extends HorizontalScrollView {
}

PagerSlidingTabStrip继承自HorizontalScrillView,当tab数量较多超出屏幕时,可以横向滚动。

(2).构造方法。
public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        tabsContainer = new LinearLayout(context);
        tabsContainer.setOrientation(LinearLayout.HORIZONTAL);
        tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        addView(tabsContainer);

        // 这里省略了初始化各参数的代码

        rectPaint = new Paint();
        rectPaint.setAntiAlias(true);
        rectPaint.setStyle(Style.FILL);

        dividerPaint = new Paint();
        dividerPaint.setAntiAlias(true);
        dividerPaint.setStrokeWidth(dividerWidth);

        defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
        expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);
    }

在构造方法中,先是创建了一个水平线性布局tabsContainer,并将其添加到PagerSlidingTabStrip中。因为PagerSlidingTabStrip继承自HorizontalScrillView,而HorizontalScrillView内部只能有一个子View,所以之后的tab都将添加到tabsContainer布局中。

接下来创建了两个画笔rectPaint和dividerPaint,分别用来绘制滑动条和竖直分隔线。

最后创建了两个布局参数LayoutParams,默认为defaultTabLayoutParams,自适应tab的宽度,以及expandedTabLayoutParams,tab均分屏幕宽度。当自定义属性pstsShouldExpand声明为true时,使用expandedTabLayoutParams,相反则使用defaultTabLayoutParams。


(3).setViewPager(ViewPager pager)方法。

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

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

		pager.setOnPageChangeListener(pageListener);

		notifyDataSetChanged();
	}

当我们获取到PagerSlidingTabStrip对象后,会调用它的setViewPager(ViewPager pager)方法。

在该方法内部,为ViewPager对象添加了滑动监听PageListener,并调用了notifyDataSetChanged()方法。


(4).notifyDataSetChanged()方法。

public void notifyDataSetChanged() {

        tabsContainer.removeAllViews();

        tabCount = pager.getAdapter().getCount();

        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());
            }

        }

        updateTabStyles();

        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

            @SuppressWarnings("deprecation")
            @SuppressLint("NewApi")
            @Override
            public void onGlobalLayout() {

                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                    getViewTreeObserver().removeGlobalOnLayoutListener(this);
                } else {
                    getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }

                currentPosition = pager.getCurrentItem();
                scrollToChild(currentPosition, 0);
            }
        });

    }

方法内部,遍历ViewPager的Adapter中的标题,生成Tab并添加到tabsContainer布局中。然后将滑动条滚动到第一个Tab下。

(5).OnPageChangeListener的实现类,PageListener类。

private class PageListener implements ViewPager.OnPageChangeListener {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            currentPosition = position;
            currentPositionOffset = positionOffset;

            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) {
            // ......
        }
    }

PageListener类监听ViewPager的滑动。

这里重点是onPageScrolled()方法,当ViewPager在滑动过程中该方法不断被回调。在其中执行了2行代码,scrollToChild(),移动tab;invalidate(),触发View的onDraw()的调用。

(6).核心方法onDraw()。

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

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

        final int height = getHeight();

        // draw indicator line
        rectPaint.setColor(indicatorColor);

        // 默认在当前被选中的tab底部绘制滑动条
        View currentTab = tabsContainer.getChildAt(currentPosition);
        float lineLeft = currentTab.getLeft();
        float lineRight = currentTab.getRight();

        // 如果正在滑动,在当前tab和下一个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);
        }

        // 绘制滑动条,滑动条宽度为lineRight - lineLeft,高度为indicatorHeight
        canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint);

        // 绘制底部线条,线条宽度为PagerSlidingTabStrip的宽度,高度为underlineHeight
        rectPaint.setColor(underlineColor);
        canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);

        // 在每个tab的右侧,绘制竖直分割线,分割线宽度为dividerWidth,高度为height - dividerPadding * 2
        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);
        }
    }

通过在onPageScrolled()回调方法中得到的currentPosition和currentPositionOffset,实时绘制滑动条的位置,实现滑动条跟随手势移动。

最后附上完整工程下载链接。
http://download.csdn.net/detail/ruancoder/9582974

你可能感兴趣的:(开源项目)