使用PagerSlidingTabStrip实现顶部导航栏

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

效果图如下。

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

(一)PagerSlidingTabStrip的使用

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

[html] view plain copy

  1. <declare-styleable name="PagerSlidingTabStrip">
  2. <attr name="pstsIndicatorColor" format="color" />
  3. <attr name="pstsUnderlineColor" format="color" />
  4. <attr name="pstsDividerColor" format="color" />
  5. <attr name="pstsIndicatorHeight" format="dimension" />
  6. <attr name="pstsUnderlineHeight" format="dimension" />
  7. <attr name="pstsDividerPadding" format="dimension" />
  8. <attr name="pstsTabPaddingLeftRight" format="dimension" />
  9. <attr name="pstsScrollOffset" format="dimension" />
  10. <attr name="pstsTabBackground" format="reference" />
  11. <attr name="pstsShouldExpand" format="boolean" />
  12. <attr name="pstsTextAllCaps" format="boolean" />
  13. </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中。

[html] view plain copy

  1. dependencies {
  2. compile ‘com.astuetz:pagerslidingtabstrip:1.0.1‘
  3. }

接下来添加布局文件。

[html] view plain copy

  1. <com.astuetz.PagerSlidingTabStrip xmlns:tab="http://schemas.android.com/apk/res-auto"
  2. android:id="@+id/tab"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. android:background="#fafafa"
  6. android:paddingBottom="10dp"
  7. android:paddingTop="10dp"
  8. android:textColor="#333333"
  9. android:textSize="13sp"
  10. tab:pstsDividerColor="@android:color/transparent"
  11. tab:pstsIndicatorColor="#ed5955"
  12. tab:pstsIndicatorHeight="2dp"
  13. tab:pstsShouldExpand="true"
  14. tab:pstsTabBackground="@android:color/transparent"
  15. tab:pstsUnderlineColor="@android:color/transparent"/>

最后实现逻辑代码。

[java] view plain copy

  1. package net.csdn.blog.ruancoder;
  2. import android.os.Bundle;
  3. import android.support.v4.app.Fragment;
  4. import android.support.v4.app.FragmentActivity;
  5. import android.support.v4.view.ViewPager;
  6. import android.view.Window;
  7. import com.astuetz.PagerSlidingTabStrip;
  8. public class MainActivity extends FragmentActivity {
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. requestWindowFeature(Window.FEATURE_NO_TITLE);
  13. setContentView(R.layout.activity_main);
  14. final PagerSlidingTabStrip tabStrip = (PagerSlidingTabStrip) findViewById(R.id.tab);
  15. ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
  16. Fragment[] fragments = {new NewsFragment(), new TechFragment(), new FinanceFragment(), new InternetFragment(), new
  17. PhoneFragment()};
  18. String[] titles = {"头条", "科技", "财经", "互联网", "手机"};
  19. TabPagerAdapter adapter = new TabPagerAdapter(getSupportFragmentManager(), fragments, titles);
  20. viewPager.setAdapter(adapter);
  21. tabStrip.setViewPager(viewPager);
  22. }
  23. }

Activity中使用到的TabPagerAdapter类:

[java] view plain copy

  1. package net.csdn.blog.ruancoder;
  2. import android.support.v4.app.Fragment;
  3. import android.support.v4.app.FragmentManager;
  4. import android.support.v4.app.FragmentPagerAdapter;
  5. public class TabPagerAdapter extends FragmentPagerAdapter {
  6. private Fragment[] mFragments;
  7. private String[] mTitles;
  8. public TabPagerAdapter(FragmentManager fm, Fragment[] fragments, String[] titles) {
  9. super(fm);
  10. this.mFragments = fragments;
  11. this.mTitles = titles;
  12. }
  13. @Override
  14. public Fragment getItem(int position) {
  15. return mFragments[position];
  16. }
  17. @Override
  18. public int getCount() {
  19. return mFragments.length;
  20. }
  21. @Override
  22. public CharSequence getPageTitle(int position) {
  23. return mTitles[position];
  24. }
  25. }

(二)PagerSlidingTabStrip的源码分析

(1).类的声明。

[java] view plain copy

  1. package com.astuetz;
  2. public class PagerSlidingTabStrip extends HorizontalScrollView {
  3. }

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

(2).构造方法。

[java] view plain copy

  1. public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) {
  2. super(context, attrs, defStyle);
  3. tabsContainer = new LinearLayout(context);
  4. tabsContainer.setOrientation(LinearLayout.HORIZONTAL);
  5. tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
  6. addView(tabsContainer);
  7. // 这里省略了初始化各参数的代码
  8. rectPaint = new Paint();
  9. rectPaint.setAntiAlias(true);
  10. rectPaint.setStyle(Style.FILL);
  11. dividerPaint = new Paint();
  12. dividerPaint.setAntiAlias(true);
  13. dividerPaint.setStrokeWidth(dividerWidth);
  14. defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
  15. expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);
  16. }

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

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

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

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

[java] view plain copy

  1. public void setViewPager(ViewPager pager) {
  2. this.pager = pager;
  3. if (pager.getAdapter() == null) {
  4. throw new IllegalStateException("ViewPager does not have adapter instance.");
  5. }
  6. pager.setOnPageChangeListener(pageListener);
  7. notifyDataSetChanged();
  8. }

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

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

(4).notifyDataSetChanged()方法。

[java] view plain copy

  1. public void notifyDataSetChanged() {
  2. tabsContainer.removeAllViews();
  3. tabCount = pager.getAdapter().getCount();
  4. for (int i = 0; i < tabCount; i++) {
  5. if (pager.getAdapter() instanceof IconTabProvider) {
  6. addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i));
  7. } else {
  8. addTextTab(i, pager.getAdapter().getPageTitle(i).toString());
  9. }
  10. }
  11. updateTabStyles();
  12. getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
  13. @SuppressWarnings("deprecation")
  14. @SuppressLint("NewApi")
  15. @Override
  16. public void onGlobalLayout() {
  17. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
  18. getViewTreeObserver().removeGlobalOnLayoutListener(this);
  19. } else {
  20. getViewTreeObserver().removeOnGlobalLayoutListener(this);
  21. }
  22. currentPosition = pager.getCurrentItem();
  23. scrollToChild(currentPosition, 0);
  24. }
  25. });
  26. }

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

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

[java] view plain copy

  1. private class PageListener implements ViewPager.OnPageChangeListener {
  2. @Override
  3. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  4. currentPosition = position;
  5. currentPositionOffset = positionOffset;
  6. scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));
  7. invalidate();
  8. // ......
  9. }
  10. @Override
  11. public void onPageScrollStateChanged(int state) {
  12. if (state == ViewPager.SCROLL_STATE_IDLE) {
  13. scrollToChild(pager.getCurrentItem(), 0);
  14. }
  15. // ......
  16. }
  17. @Override
  18. public void onPageSelected(int position) {
  19. // ......
  20. }
  21. }

PageListener类监听ViewPager的滑动。

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

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

[java] view plain copy

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. super.onDraw(canvas);
  4. if (isInEditMode() || tabCount == 0) {
  5. return;
  6. }
  7. final int height = getHeight();
  8. // draw indicator line
  9. rectPaint.setColor(indicatorColor);
  10. // 默认在当前被选中的tab底部绘制滑动条
  11. View currentTab = tabsContainer.getChildAt(currentPosition);
  12. float lineLeft = currentTab.getLeft();
  13. float lineRight = currentTab.getRight();
  14. // 如果正在滑动,在当前tab和下一个tab之间绘制滑动条
  15. if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {
  16. View nextTab = tabsContainer.getChildAt(currentPosition + 1);
  17. final float nextTabLeft = nextTab.getLeft();
  18. final float nextTabRight = nextTab.getRight();
  19. lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft);
  20. lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight);
  21. }
  22. // 绘制滑动条,滑动条宽度为lineRight - lineLeft,高度为indicatorHeight
  23. canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint);
  24. // 绘制底部线条,线条宽度为PagerSlidingTabStrip的宽度,高度为underlineHeight
  25. rectPaint.setColor(underlineColor);
  26. canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);
  27. // 在每个tab的右侧,绘制竖直分割线,分割线宽度为dividerWidth,高度为height - dividerPadding * 2
  28. dividerPaint.setColor(dividerColor);
  29. for (int i = 0; i < tabCount - 1; i++) {
  30. View tab = tabsContainer.getChildAt(i);
  31. canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint);
  32. }
  33. }

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

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

原文地址:https://www.cnblogs.com/wushanmanong/p/8877210.html

时间: 2024-10-18 23:35:15

使用PagerSlidingTabStrip实现顶部导航栏的相关文章

微信小程序--仿京东UI样式顶部导航栏

我们先来看看京东的效果 分析 上端导航栏可以放置多个分类,可滑动 点击导航栏最右端按钮可以查看所有分类,同时背景模糊 内容部分右拉跳转到另外的分类 点击分类时导航栏的滑动部分自动滑动到合适的位置 我的实现 代码展示: js /** * categoryView.js - 分类页面 */ var fakeData = require('../../common/fakeData.js') Page( { data: { categories: ['全部'], currentTab: 0, scro

Android之仿今日头条顶部导航栏效果

随着时间的推移现在的软件要求显示的内容越来越多,所以要在小的屏幕上能够更好的显示更多的内容,首先我们会想到底部菜单栏,但是有时候像今日头条新闻客户端要显示的内容太多,而且又想在主界面全部显示出来,所以有加了顶部导航栏. 今日头条顶部导航栏区域的主要部分是一个导航菜单.导航菜单是一组标签的集合,在新闻客户端中,每个标签标示一个新闻类别,对应下面ViewPager控件的一个分页面.当用户在ViewPager区域滑动页面时,对应的导航菜单标签也会相应的被选中,选中的标签通过一个矩形红框高亮显示,红框背

固定顶部导航栏和左侧边栏滚动条为右下方的一类布局分析

在管理界面或者一些需要固定显示信息的界面,经常遇到这样的布局:顶部导航栏固定不动,左侧有一个侧边栏用来显示详细信息,右下侧为主要内容所在区域.如下图所示: 这是需要达到的目标效果.其中Header和SideNav部分之间用固定定位实现就可以了,但是MainContent部分右侧的scroll bar确实不太好处理,主要存在两个问题: - 按照普通的方式基本都会铺满右侧整个可视区域,这使得顶部的Header的右侧有了一个滚动条区域,这对于顶部固定这个效果反差太明显. - 第二个问题是,如果Main

android实现底部导航栏和顶部导航栏(相当于网页上的一级菜单和二级菜单)

直接采用图片进行导航,实现activity跳转,虽然功能实现了,但是界面实在太丑,所以采用顶部导航栏和底部导航栏进行控制,在这个学习的过程中,发现了很多好的资料,不仅对控件进行了详细的讲解和演示,而且还附带源码以供下载.再次记录,供大家参考学习: 1.http://www.linuxidc.com/Linux/2012-07/66327.htm 2.http://blog.csdn.net/yalinfendou/article/details/44727299 3.http://blog.cs

天书笔记:HTML+CSS实现顶部导航栏

此笔记纯属本人脑残笔记,阅读困难不理解属正常现象,初学者尽量不要阅读,否则轻则口吐白沫重则走火入魔,切记切记 先晒效果图: 效果要求类似于b站的顶部导航(..原谅我老是拿b站做例子:) ) 然后是代码 下面逐条分析这一堆东西 HTML部分主要是这个: 1 <div class="topbar"> 2 <div class="topbody"> 3 <a class="title" href=""&

android 自定义组合控件 顶部导航栏

在软件开发过程中,经常见到,就是APP 的标题栏样式几乎都是一样的,只是文字不同而已,两边图标不同.为了减少重复代码,提高效率, 方便大家使用,我们把标题栏通过组合的方式定义成一个控件. 例下图: 点击: 如不设置左边,右边图片: 下面说一下具体实现步骤: 步骤一: 导航栏包括:* 返回按钮* 标题* 右侧按钮(功能不确定) 首先是布局文件,如下: </pre><p></p><pre name="code" class="java&q

顶部导航栏

1. 创建res/menu/main.xml文件 <!-- 菜单栏的设置 --> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" > <!-- 默认的 --> <item android:id="@+id/action_settin

[学习]京东首页顶部导航栏hover

这里要说的内容就是这个小尖角: 开始只看到网页截图的时候我一直认为是一个">"做的hover效果. 这样的话就需要先把">"先旋转90度,再做hover的效果. 后来打开了原网页看了源代码,发现他们是这么做的: 首先画了一个15x7的div,overflow:hidden;然后里面写了一个"◇"font-size:15px;定位,上移7px; (这样我们看到的效果就是">"旋转了90度的样子) 再加上hove

滚动页面, 顶部导航栏固定效果

(function(){ $(document).scroll(function(){ var scrTop = $(document).scrollTop(); if(scrTop>70){ $(".m-top-notice").addClass("fixed"); } else if(scrTop <= 70){ $(".m-top-notice").removeClass("fixed"); } })})()