Android 自定义控件——PagerSlidingTabStrip
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>
MainActivity.java
- 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);
- }
- }
- }
- }

浙公网安备 33010602011771号