Android 自定义控件——PagerSlidingTabStrip

PagerSlidingTabStrip是Gihub上的开源项目:https://github.com/astuetz/PagerSlidingTabStrip

主要的就一个类PagerSlidingTabStrip.Java 

如图:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

使用:

main_layout.xml

 

[html] view plain copy
 
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:app="http://schemas.android.com/apk/res-auto"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent" >  
  6.   
  7.     <com.astuetz.PagerSlidingTabStrip  
  8.         android:id="@+id/tabs"  
  9.         android:layout_width="match_parent"  
  10.         android:layout_height="48dip"  
  11.         android:background="@drawable/background_tabs" />  
  12.   
  13.     <android.support.v4.view.ViewPager  
  14.         android:id="@+id/pager"  
  15.         android:layout_width="match_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:layout_above="@+id/colors"  
  18.         android:layout_below="@+id/tabs"  
  19.         tools:context=".MainActivity" />  
  20.   
  21.     <LinearLayout                  //这里以下是那几个颜色选择的view,先不要管  
  22.         android:id="@+id/colors"  
  23.         android:layout_width="match_parent"  
  24.         android:layout_height="48dip"  
  25.         android:layout_alignParentBottom="true"  
  26.         android:layout_marginBottom="8dip"  
  27.         android:layout_marginLeft="4dip"  
  28.         android:layout_marginRight="4dip"  
  29.         android:orientation="horizontal" >  
  30.   
  31.         <ImageView  
  32.             android:layout_width="0dip"  
  33.             android:layout_height="match_parent"  
  34.             android:layout_margin="4dip"  
  35.             android:layout_weight="1"  
  36.             android:background="#FF666666"  
  37.             android:onClick="onColorClicked"  
  38.             android:tag="#FF666666" />  
  39.   
  40.         <ImageView  
  41.             android:layout_width="0dip"  
  42.             android:layout_height="match_parent"  
  43.             android:layout_margin="4dip"  
  44.             android:layout_weight="1"  
  45.             android:background="#FF96AA39"  
  46.             android:onClick="onColorClicked"  
  47.             android:tag="#FF96AA39" />  
  48.   
  49.         <ImageView  
  50.             android:layout_width="0dip"  
  51.             android:layout_height="match_parent"  
  52.             android:layout_margin="4dip"  
  53.             android:layout_weight="1"  
  54.             android:background="#FFC74B46"  
  55.             android:onClick="onColorClicked"  
  56.             android:tag="#FFC74B46" />  
  57.   
  58.         <ImageView  
  59.             android:layout_width="0dip"  
  60.             android:layout_height="match_parent"  
  61.             android:layout_margin="4dip"  
  62.             android:layout_weight="1"  
  63.             android:background="#FFF4842D"  
  64.             android:onClick="onColorClicked"  
  65.             android:tag="#FFF4842D" />  
  66.   
  67.         <ImageView  
  68.             android:layout_width="0dip"  
  69.             android:layout_height="match_parent"  
  70.             android:layout_margin="4dip"  
  71.             android:layout_weight="1"  
  72.             android:background="#FF3F9FE0"  
  73.             android:onClick="onColorClicked"  
  74.             android:tag="#FF3F9FE0" />  
  75.   
  76.         <ImageView  
  77.             android:layout_width="0dip"  
  78.             android:layout_height="match_parent"  
  79.             android:layout_margin="4dip"  
  80.             android:layout_weight="1"  
  81.             android:background="#FF5161BC"  
  82.             android:onClick="onColorClicked"  
  83.             android:tag="#FF5161BC" />  
  84.     </LinearLayout>  
  85.   
  86. </RelativeLayout>  


MainActivity.java

 

 

[java] view plain copy
 
  1. public class MainActivity extends FragmentActivity {  
  2.   
  3.     private final Handler handler = new Handler();  
  4.   
  5.     private PagerSlidingTabStrip tabs;  
  6.     private ViewPager pager;  
  7.     private MyPagerAdapter adapter;  
  8.   
  9.     private Drawable oldBackground = null;  
  10.     private int currentColor = 0xFF666666;  
  11.   
  12.     @SuppressLint("NewApi")  
  13.     @Override  
  14.     protected void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.activity_main);  
  17.         //getActionBar().hide();  
  18.         tabs = (PagerSlidingTabStrip) findViewById(R.id.tabs);  
  19.         pager = (ViewPager) findViewById(R.id.pager);  
  20.         adapter = new MyPagerAdapter(getSupportFragmentManager());  
  21.   
  22.         pager.setAdapter(adapter);  
  23.   
  24.         final int pageMargin = (int) TypedValue.applyDimension(  
  25.                 TypedValue.COMPLEX_UNIT_DIP, 4, getResources()  
  26.                         .getDisplayMetrics());  
  27.         pager.setPageMargin(pageMargin);  
  28.   
  29.         tabs.setViewPager(pager);  
  30.   
  31.         changeColor(currentColor);  
  32.     }  
  33. }  

 

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中调用了

 

[java] view plain copy
 
  1. pager.setOnPageChangeListener(pageListener);//监听传递过来的ViewPager,这里的ViewPager在MainActivity中并没有监听,而是在PagerSlidingTabStrip中监听的,目的是为了通过监听ViewPager的滑动来改变tab的状态,  
  2.         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();

 

 

 

[java] view plain copy
 
    1. public class PagerSlidingTabStrip extends HorizontalScrollView {  
    2. public interface IconTabProvider {  
    3.         public int getPageIconResId(int position);  
    4.     }  
    5.   
    6.     // @formatter:off  
    7.     private static final int[] ATTRS = new int[] { android.R.attr.textSize,  
    8.             android.R.attr.textColor };  
    9.     // @formatter:on  
    10.   
    11.     private LinearLayout.LayoutParams defaultTabLayoutParams;  
    12.     private LinearLayout.LayoutParams expandedTabLayoutParams;  
    13.   
    14.     private final PageListener pageListener = new PageListener();  
    15.     public OnPageChangeListener delegatePageListener;  
    16.   
    17.     private LinearLayout tabsContainer;  
    18.     private ViewPager pager;  
    19.   
    20.     private int tabCount;  
    21.   
    22.     private int currentPosition = 0;  
    23.     private float currentPositionOffset = 0f;  
    24.   
    25.     private Paint rectPaint;  
    26.     private Paint dividerPaint;  
    27.   
    28.     private int indicatorColor = 0xFF666666;  
    29.     private int underlineColor = 0x1A000000;  
    30.     private int dividerColor = 0x1A000000;  
    31.   
    32.     private boolean shouldExpand = false;  
    33.     private boolean textAllCaps = true;  
    34.   
    35.     private int scrollOffset = 52;  
    36.     private int indicatorHeight = 8;  
    37.     private int underlineHeight = 2;  
    38.     private int dividerPadding = 12;  
    39.     private int tabPadding = 24;  
    40.     private int dividerWidth = 1;  
    41.   
    42.     private int tabTextSize = 12;  
    43.     private int tabTextColor = 0xFF666666;  
    44.     private Typeface tabTypeface = null;  
    45.     private int tabTypefaceStyle = Typeface.BOLD;  
    46.   
    47.     private int lastScrollX = 0;  
    48.   
    49.     private int tabBackgroundResId = R.drawable.background_tab;  
    50.   
    51.     private Locale locale;  
    52.         public PagerSlidingTabStrip(Context context, AttributeSet attrs,  
    53.             int defStyle) {  
    54.         super(context, attrs, defStyle);  
    55.   
    56.         setFillViewport(true);  
    57.         setWillNotDraw(false);  
    58.   
    59.         tabsContainer = new LinearLayout(context);  
    60.         tabsContainer.setOrientation(LinearLayout.HORIZONTAL);  
    61.         tabsContainer.setLayoutParams(new LayoutParams(  
    62.                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));  
    63.         addView(tabsContainer);  
    64.   
    65.         DisplayMetrics dm = getResources().getDisplayMetrics();  
    66.   
    67.         scrollOffset = (int) TypedValue.applyDimension(  
    68.                 TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm);  
    69.         indicatorHeight = (int) TypedValue.applyDimension(  
    70.                 TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm);  
    71.         underlineHeight = (int) TypedValue.applyDimension(  
    72.                 TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm);  
    73.         dividerPadding = (int) TypedValue.applyDimension(  
    74.                 TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm);  
    75.         tabPadding = (int) TypedValue.applyDimension(  
    76.                 TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm);  
    77.         dividerWidth = (int) TypedValue.applyDimension(  
    78.                 TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm);  
    79.         tabTextSize = (int) TypedValue.applyDimension(  
    80.                 TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm);  
    81.   
    82.         // get system attrs (android:textSize and android:textColor)  
    83.   
    84.         TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);  
    85.   
    86.         tabTextSize = a.getDimensionPixelSize(0, tabTextSize);  
    87.         tabTextColor = a.getColor(1, tabTextColor);  
    88.   
    89.         a.recycle();  
    90.   
    91.         // get custom attrs  
    92.   
    93.         a = context.obtainStyledAttributes(attrs,  
    94.                 R.styleable.PagerSlidingTabStrip);  
    95.   
    96.         indicatorColor = a.getColor(  
    97.                 R.styleable.PagerSlidingTabStrip_pstsIndicatorColor,  
    98.                 indicatorColor);  
    99.         underlineColor = a.getColor(  
    100.                 R.styleable.PagerSlidingTabStrip_pstsUnderlineColor,  
    101.                 underlineColor);  
    102.         dividerColor = a  
    103.                 .getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor,  
    104.                         dividerColor);  
    105.         indicatorHeight = a.getDimensionPixelSize(  
    106.                 R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight,  
    107.                 indicatorHeight);  
    108.         underlineHeight = a.getDimensionPixelSize(  
    109.                 R.styleable.PagerSlidingTabStrip_pstsUnderlineHeight,  
    110.                 underlineHeight);  
    111.         dividerPadding = a.getDimensionPixelSize(  
    112.                 R.styleable.PagerSlidingTabStrip_pstsDividerPadding,  
    113.                 dividerPadding);  
    114.         tabPadding = a.getDimensionPixelSize(  
    115.                 R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeftRight,  
    116.                 tabPadding);  
    117.         tabBackgroundResId = a.getResourceId(  
    118.                 R.styleable.PagerSlidingTabStrip_pstsTabBackground,  
    119.                 tabBackgroundResId);  
    120.         shouldExpand = a  
    121.                 .getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand,  
    122.                         shouldExpand);  
    123.         scrollOffset = a  
    124.                 .getDimensionPixelSize(  
    125.                         R.styleable.PagerSlidingTabStrip_pstsScrollOffset,  
    126.                         scrollOffset);  
    127.         textAllCaps = a.getBoolean(  
    128.                 R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps);  
    129.   
    130.         a.recycle();  
    131.   
    132.         rectPaint = new Paint();  
    133.         rectPaint.setAntiAlias(true);  
    134.         rectPaint.setStyle(Style.FILL);  
    135.   
    136.         dividerPaint = new Paint();  
    137.         dividerPaint.setAntiAlias(true);  
    138.         dividerPaint.setStrokeWidth(dividerWidth);  
    139.   
    140.         defaultTabLayoutParams = new LinearLayout.LayoutParams(  
    141.                 LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);  
    142.         expandedTabLayoutParams = new LinearLayout.LayoutParams(0,  
    143.                 LayoutParams.MATCH_PARENT, 1.0f);  
    144.   
    145.         if (locale == null) {  
    146.             locale = getResources().getConfiguration().locale;  
    147.         }  
    148.     }  
    149.         public void setViewPager(ViewPager pager) {  
    150.         this.pager = pager;  
    151.   
    152.         if (pager.getAdapter() == null) {  
    153.             throw new IllegalStateException(  
    154.                     "ViewPager does not have adapter instance.");  
    155.         }  
    156.   
    157.         pager.setOnPageChangeListener(pageListener);  
    158.         notifyDataSetChanged();  
    159.     }  
    160.         public void setOnPageChangeListener(OnPageChangeListener listener) {  
    161.         this.delegatePageListener = listener;  
    162.     }  
    163.   
    164.     public void notifyDataSetChanged() {  
    165.   
    166.         tabsContainer.removeAllViews();  
    167.   
    168.         tabCount = pager.getAdapter().getCount();  
    169.   
    170.         for (int i = 0; i < tabCount; i++) {  
    171.   
    172.             if (pager.getAdapter() instanceof IconTabProvider) {  
    173.                 addIconTab(i,  
    174.                         ((IconTabProvider) pager.getAdapter())  
    175.                                 .getPageIconResId(i));  
    176.             } else {  
    177.                 addTextTab(i, pager.getAdapter().getPageTitle(i).toString());  
    178.             }  
    179.   
    180.         }  
    181.   
    182.         updateTabStyles();  
    183.   
    184.         getViewTreeObserver().addOnGlobalLayoutListener(  
    185.                 new OnGlobalLayoutListener() {  
    186.   
    187.                     @SuppressWarnings("deprecation")  
    188.                     @SuppressLint("NewApi")  
    189.                     @Override  
    190.                     public void onGlobalLayout() {  
    191.   
    192.                         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {  
    193.                             getViewTreeObserver().removeGlobalOnLayoutListener(  
    194.                                     this);  
    195.                         } else {  
    196.                             getViewTreeObserver().removeOnGlobalLayoutListener(  
    197.                                     this);  
    198.                         }  
    199.   
    200.                         currentPosition = pager.getCurrentItem();  
    201.                         scrollToChild(currentPosition, 0);  
    202.                     }  
    203.                 });  
    204.   
    205.     }  
    206.   
    207.     private void addTextTab(final int position, String title) {  
    208.   
    209.         TextView tab = new TextView(getContext());  
    210.         tab.setText(title);  
    211.         tab.setGravity(Gravity.CENTER);  
    212.         tab.setSingleLine();  
    213.   
    214.         addTab(position, tab);  
    215.     }  
    216.   
    217.     private void addIconTab(final int position, int resId) {  
    218.   
    219.         ImageButton tab = new ImageButton(getContext());  
    220.         tab.setImageResource(resId);  
    221.   
    222.         addTab(position, tab);  
    223.   
    224.     }  
    225.   
    226.     private void addTab(final int position, View tab) {  
    227.         tab.setFocusable(true);  
    228.         tab.setOnClickListener(new OnClickListener() {  
    229.             @Override  
    230.             public void onClick(View v) {  
    231.                 pager.setCurrentItem(position);  
    232.             }  
    233.         });  
    234.   
    235.         tab.setPadding(tabPadding, 0, tabPadding, 0);  
    236.         tabsContainer  
    237.                 .addView(tab, position, shouldExpand ? expandedTabLayoutParams  
    238.                         : defaultTabLayoutParams);  
    239.     }  
    240.   
    241.     private void updateTabStyles() {  
    242.   
    243.         for (int i = 0; i < tabCount; i++) {  
    244.   
    245.             View v = tabsContainer.getChildAt(i);  
    246.   
    247.             v.setBackgroundResource(tabBackgroundResId);  
    248.   
    249.             if (v instanceof TextView) {  
    250.   
    251.                 TextView tab = (TextView) v;  
    252.                 tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize);  
    253.                 tab.setTypeface(tabTypeface, tabTypefaceStyle);  
    254.                 tab.setTextColor(tabTextColor);  
    255.   
    256.                 // setAllCaps() is only available from API 14, so the upper case  
    257.                 // is made manually if we are on a  
    258.                 // pre-ICS-build  
    259.                 if (textAllCaps) {  
    260.                     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {  
    261.                         tab.setAllCaps(true);  
    262.                     } else {  
    263.                         tab.setText(tab.getText().toString()  
    264.                                 .toUpperCase(locale));  
    265.                     }  
    266.                 }  
    267.             }  
    268.         }  
    269.   
    270.     }  
    271.   
    272.     private void scrollToChild(int position, int offset) {  
    273.   
    274.         if (tabCount == 0) {  
    275.             return;  
    276.         }  
    277.   
    278.         int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset;  
    279.   
    280.         if (position > 0 || offset > 0) {  
    281.             newScrollX -= scrollOffset;  
    282.         }  
    283.   
    284.         if (newScrollX != lastScrollX) {  
    285.             lastScrollX = newScrollX;  
    286.             scrollTo(newScrollX, 0);  
    287.         }  
    288.   
    289.     }  
    290.   
    291.     @Override  
    292.     protected void onDraw(Canvas canvas) {  
    293.         super.onDraw(canvas);  
    294.   
    295.         if (isInEditMode() || tabCount == 0) {  
    296.             return;  
    297.         }  
    298.   
    299.         final int height = getHeight();  
    300.   
    301.         // draw indicator line  
    302.   
    303.         rectPaint.setColor(indicatorColor);  
    304.   
    305.         // default: line below current tab  
    306.         View currentTab = tabsContainer.getChildAt(currentPosition);  
    307.         float lineLeft = currentTab.getLeft();  
    308.         float lineRight = currentTab.getRight();  
    309.   
    310.         // if there is an offset, start interpolating left and right coordinates  
    311.         // between current and next tab  
    312.         if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {  
    313.   
    314.             View nextTab = tabsContainer.getChildAt(currentPosition + 1);  
    315.             final float nextTabLeft = nextTab.getLeft();  
    316.             final float nextTabRight = nextTab.getRight();  
    317.   
    318.             lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset)  
    319.                     * lineLeft);  
    320.             lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset)  
    321.                     * lineRight);  
    322.         }  
    323.   
    324.         canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height,  
    325.                 rectPaint);  
    326.   
    327.         // draw underline  
    328.   
    329.         rectPaint.setColor(underlineColor);  
    330.         canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(),  
    331.                 height, rectPaint);  
    332.   
    333.         // draw divider  
    334.   
    335.         dividerPaint.setColor(dividerColor);  
    336.         for (int i = 0; i < tabCount - 1; i++) {  
    337.             View tab = tabsContainer.getChildAt(i);  
    338.             canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(),  
    339.                     height - dividerPadding, dividerPaint);  
    340.         }  
    341.     }  
    342.   
    343.     private class PageListener implements OnPageChangeListener {  
    344.   
    345.         @Override  
    346.         public void onPageScrolled(int position, float positionOffset,  
    347.                 int positionOffsetPixels) {  
    348.   
    349.             currentPosition = position;  
    350.             currentPositionOffset = positionOffset;  
    351.   
    352.             scrollToChild(position, (int) (positionOffset * tabsContainer  
    353.                     .getChildAt(position).getWidth()));  
    354.   
    355.             invalidate();  
    356.   
    357.             if (delegatePageListener != null) {  
    358.                 delegatePageListener.onPageScrolled(position, positionOffset,  
    359.                         positionOffsetPixels);  
    360.             }  
    361.         }  
    362.   
    363.         @Override  
    364.         public void onPageScrollStateChanged(int state) {  
    365.             if (state == ViewPager.SCROLL_STATE_IDLE) {  
    366.                 scrollToChild(pager.getCurrentItem(), 0);  
    367.             }  
    368.   
    369.             if (delegatePageListener != null) {  
    370.                 delegatePageListener.onPageScrollStateChanged(state);  
    371.             }  
    372.         }  
    373.   
    374.         @Override  
    375.         public void onPageSelected(int position) {  
    376.             if (delegatePageListener != null) {  
    377.                 delegatePageListener.onPageSelected(position);  
    378.             }  
    379.         }  
    380.   
    381.     }  
    382. }  
posted @ 2017-04-26 13:59  天涯海角路  阅读(104)  评论(0)    收藏  举报