PagerSlidingTabStrip是Gihub上的开源项目:https://github.com/astuetz/PagerSlidingTabStrip
主要的就一个类PagerSlidingTabStrip.java
如图:
使用:
main_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" > <com.astuetz.PagerSlidingTabStrip android:id="@+id/tabs" android:layout_width="match_parent" android:layout_height="48dip" android:background="@drawable/background_tabs" /> <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/colors" android:layout_below="@+id/tabs" tools:context=".MainActivity" /> <LinearLayout //这里以下是那几个颜色选择的view,先不要管 android:id="@+id/colors" android:layout_width="match_parent" android:layout_height="48dip" android:layout_alignParentBottom="true" android:layout_marginBottom="8dip" android:layout_marginLeft="4dip" android:layout_marginRight="4dip" android:orientation="horizontal" > <ImageView android:layout_width="0dip" android:layout_height="match_parent" android:layout_margin="4dip" android:layout_weight="1" android:background="#FF666666" android:onClick="onColorClicked" android:tag="#FF666666" /> <ImageView android:layout_width="0dip" android:layout_height="match_parent" android:layout_margin="4dip" android:layout_weight="1" android:background="#FF96AA39" android:onClick="onColorClicked" android:tag="#FF96AA39" /> <ImageView android:layout_width="0dip" android:layout_height="match_parent" android:layout_margin="4dip" android:layout_weight="1" android:background="#FFC74B46" android:onClick="onColorClicked" android:tag="#FFC74B46" /> <ImageView android:layout_width="0dip" android:layout_height="match_parent" android:layout_margin="4dip" android:layout_weight="1" android:background="#FFF4842D" android:onClick="onColorClicked" android:tag="#FFF4842D" /> <ImageView android:layout_width="0dip" android:layout_height="match_parent" android:layout_margin="4dip" android:layout_weight="1" android:background="#FF3F9FE0" android:onClick="onColorClicked" android:tag="#FF3F9FE0" /> <ImageView android:layout_width="0dip" android:layout_height="match_parent" android:layout_margin="4dip" android:layout_weight="1" android:background="#FF5161BC" android:onClick="onColorClicked" android:tag="#FF5161BC" /> </LinearLayout> </RelativeLayout>
public class MainActivity extends FragmentActivity { private final Handler handler = new Handler(); private PagerSlidingTabStrip tabs; private ViewPager pager; private MyPagerAdapter adapter; private Drawable oldBackground = null; private int currentColor = 0xFF666666; @SuppressLint("NewApi") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //getActionBar().hide(); tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs); pager = (ViewPager) findViewById(R.id.pager); adapter = new MyPagerAdapter(getSupportFragmentManager()); pager.setAdapter(adapter); final int pageMargin = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 4, getResources() .getDisplayMetrics()); pager.setPageMargin(pageMargin); tabs.setViewPager(pager); changeColor(currentColor); } }
PagerSlidingTabStrip.java
PagerSlidingTabStrip继承了HorizontalScrollView
原理:
1.实例化PagerSlidingTabStrip时候在构造方法中用addView给HorizontalScrollView添加一个LinearLayout布局,并且初始化自定义属性,定义了两个画笔Paint用来画tab下面的长的细线,和移动的indicator,事实上他们并不是线,而是Rect(矩形)。
2.那么我们到底需要几个tab由谁来决定呢,当然是下面的ViewPager内容页了,有几页内容,就需要几个tab,我们在实例化PagerSlidingTabStrip之后,调用了在PagerSlidingTabStrip.java中的方法setViewPager(ViewPager ); ViewPager我们给定义了一个strin[],这里装的就是tab文本。在setViewPager中调用了
pager.setOnPageChangeListener(pageListener);//监听传递过来的ViewPager,这里的ViewPager在MainActivity中并没有监听,而是在PagerSlidingTabStrip中监听的,目的是为了通过监听ViewPager的滑动来改变tab的状态, notifyDataSetChanged();//这个方法就实在初始化那几个tab标签了,通过一个for循环,创建TextView并逐个添加至LinearLayout布局。
3.紧接着是onDraw方法,得到当前的tab的索引数字,然后得到这个索引下的TextView的left,right和下一个TextView的left,right坐标。而高度是我们自己给定义的值,通过四个坐标画出一个矩形,然后还有一个很细的矩形也就是tab下面的那么,这个不需要怎么计算,宽是屏幕宽,高随便给。TextView之前的小竖线也是画笔话的,这个位置也很容易得到。
4.滑动ViewPager时Tab跟着变化,在PageChangeListener的onPageScrolled中调用scrollToChild和onPageScrollStateChanged和invalidate();通过onPageScrolled的参数计算出滚动的距离,并不断的刷新视图invalidate();
public class PagerSlidingTabStrip extends HorizontalScrollView { public interface IconTabProvider { public int getPageIconResId(int position); } // @formatter:off private static final int[] ATTRS = new int[] { android.R.attr.textSize, android.R.attr.textColor }; // @formatter:on private LinearLayout.LayoutParams defaultTabLayoutParams; private LinearLayout.LayoutParams expandedTabLayoutParams; private final PageListener pageListener = new PageListener(); public OnPageChangeListener delegatePageListener; private LinearLayout tabsContainer; private ViewPager pager; private int tabCount; private int currentPosition = 0; private float currentPositionOffset = 0f; private Paint rectPaint; private Paint dividerPaint; private int indicatorColor = 0xFF666666; private int underlineColor = 0x1A000000; private int dividerColor = 0x1A000000; private boolean shouldExpand = false; private boolean textAllCaps = true; private int scrollOffset = 52; private int indicatorHeight = 8; private int underlineHeight = 2; private int dividerPadding = 12; private int tabPadding = 24; private int dividerWidth = 1; private int tabTextSize = 12; private int tabTextColor = 0xFF666666; private Typeface tabTypeface = null; private int tabTypefaceStyle = Typeface.BOLD; private int lastScrollX = 0; private int tabBackgroundResId = R.drawable.background_tab; private Locale locale; public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setFillViewport(true); setWillNotDraw(false); 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(); // get custom attrs 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); a.recycle(); 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); if (locale == null) { locale = getResources().getConfiguration().locale; } } 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(); } public void setOnPageChangeListener(OnPageChangeListener listener) { this.delegatePageListener = listener; } 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 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); } }); } private void addTextTab(final int position, String title) { TextView tab = new TextView(getContext()); tab.setText(title); tab.setGravity(Gravity.CENTER); tab.setSingleLine(); addTab(position, tab); } private void addIconTab(final int position, int resId) { ImageButton tab = new ImageButton(getContext()); tab.setImageResource(resId); addTab(position, tab); } private void addTab(final int position, View tab) { tab.setFocusable(true); tab.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { pager.setCurrentItem(position); } }); tab.setPadding(tabPadding, 0, tabPadding, 0); tabsContainer .addView(tab, position, shouldExpand ? expandedTabLayoutParams : defaultTabLayoutParams); } private void updateTabStyles() { for (int i = 0; i < tabCount; i++) { View v = tabsContainer.getChildAt(i); v.setBackgroundResource(tabBackgroundResId); if (v instanceof TextView) { TextView tab = (TextView) v; tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize); tab.setTypeface(tabTypeface, tabTypefaceStyle); tab.setTextColor(tabTextColor); // setAllCaps() is only available from API 14, so the upper case // is made manually if we are on a // pre-ICS-build if (textAllCaps) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { tab.setAllCaps(true); } else { tab.setText(tab.getText().toString() .toUpperCase(locale)); } } } } } 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); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isInEditMode() || tabCount == 0) { return; } final int height = getHeight(); // 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); } } private class PageListener implements OnPageChangeListener { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { currentPosition = position; currentPositionOffset = positionOffset; scrollToChild(position, (int) (positionOffset * tabsContainer .getChildAt(position).getWidth())); invalidate(); if (delegatePageListener != null) { delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageScrollStateChanged(int state) { if (state == ViewPager.SCROLL_STATE_IDLE) { scrollToChild(pager.getCurrentItem(), 0); } if (delegatePageListener != null) { delegatePageListener.onPageScrollStateChanged(state); } } @Override public void onPageSelected(int position) { if (delegatePageListener != null) { delegatePageListener.onPageSelected(position); } } } }