使用PagerSlidingTabStrip实现顶部导航栏

来源:互联网 发布:mac合盖屏幕亮 编辑:程序博客网 时间:2024/05/24 01:55
在开发中,我们有时会遇到顶部导航栏滑动切换页面的设计,如网易新闻。实现的方式有很多种,今天我们使用PagerSlidingTabStrip配合ViewPager实现顶部导航栏。

效果图如下。


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

(一)PagerSlidingTabStrip的使用

在使用之前,我们先来看一下PagerSlidingTabStrip中的自定义属性。
<declare-styleable name="PagerSlidingTabStrip">    <attr name="pstsIndicatorColor" format="color" />    <attr name="pstsUnderlineColor" format="color" />    <attr name="pstsDividerColor" format="color" />    <attr name="pstsIndicatorHeight" format="dimension" />    <attr name="pstsUnderlineHeight" format="dimension" />    <attr name="pstsDividerPadding" format="dimension" />    <attr name="pstsTabPaddingLeftRight" format="dimension" />    <attr name="pstsScrollOffset" format="dimension" />    <attr name="pstsTabBackground" format="reference" />    <attr name="pstsShouldExpand" format="boolean" />    <attr name="pstsTextAllCaps" format="boolean" /></declare-styleable>

各属性的详细介绍如下。
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'}

接下来添加布局文件。
<com.astuetz.PagerSlidingTabStrip xmlns:tab="http://schemas.android.com/apk/res-auto"          android:id="@+id/tab"          android:layout_width="match_parent"          android:layout_height="wrap_content"          android:background="#fafafa"          android:paddingBottom="10dp"          android:paddingTop="10dp"          android:textColor="#333333"          android:textSize="13sp"          tab:pstsDividerColor="@android:color/transparent"          tab:pstsIndicatorColor="#ed5955"          tab:pstsIndicatorHeight="2dp"          tab:pstsShouldExpand="true"          tab:pstsTabBackground="@android:color/transparent"          tab:pstsUnderlineColor="@android:color/transparent"/>

最后实现逻辑代码。

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

0 0
原创粉丝点击