谷歌官方Android最新滑动选项导航卡SlidingTabLayout和SlidingTabStrip

分手后的思念是犯贱 2023-10-16 22:16 110阅读 0赞



谷歌官方Android最新滑动选项导航卡SlidingTabLayout和SlidingTabStrip

在最新版的谷歌官方Android SDK(Android 5.0+以上的SDK)中,已经废弃过往过选项导航卡的支持,取而代之以最新的SlidingTabLayout和SlidingTabStrip结合ViewPager实现选项导航卡滑动切换的样式。
SlidingTabLayout代表可以盛放选项导航卡的布局“容器”具体,SlidingTabLayout中存放的每一个子元素(导航卡中的每一个‘卡片’)由SlidingTabStrip实现。
现在给出一个完整的例子加以说明,随后将谷歌官方SlidingTabLayout和SlidingTabStrip实现的源代码全部贴出,具体的使用方法:可以直接把SlidingTabLayout和SlidingTabStrip这两个类文件放到自己项目的源代码中,作为普通的类文件使用。在我给出的演示代码中,代码存放结构如图所示:

Center

测试用的主Activity MainActivity.java文件:

  1. package zhangphil.slidingtabs;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.support.v4.view.PagerAdapter;
  5. import android.support.v4.view.ViewPager;
  6. import android.view.Gravity;
  7. import android.view.View;
  8. import android.view.ViewGroup;
  9. import android.widget.TextView;
  10. import zhangphil.android.common.view.SlidingTabLayout;
  11. import zhangphil.slidingtabs.R;
  12. public class MainActivity extends Activity {
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_main);
  17. ViewPager mViewPager = (ViewPager) findViewById(R.id.viewpager);
  18. mViewPager.setAdapter(new MyPagerAdapter(this));
  19. SlidingTabLayout mSlidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
  20. mSlidingTabLayout.setViewPager(mViewPager);
  21. }
  22. private class MyPagerAdapter extends PagerAdapter {
  23. private Activity mActivity;
  24. public MyPagerAdapter(Activity mActivity){
  25. this.mActivity=mActivity;
  26. }
  27. @Override
  28. public boolean isViewFromObject(View view, Object o) {
  29. return o == view;
  30. }
  31. @Override
  32. public CharSequence getPageTitle(int pos) {
  33. return "选项卡" +pos;
  34. }
  35. @Override
  36. public Object instantiateItem(ViewGroup container, int pos) {
  37. TextView tv=new TextView(mActivity);
  38. tv.setText(pos+"");
  39. tv.setTextSize(50.0f);
  40. tv.setGravity(Gravity.CENTER);
  41. container.addView(tv);
  42. return tv;
  43. }
  44. @Override
  45. public void destroyItem(ViewGroup container, int position, Object object) {
  46. container.removeView((View) object);
  47. }
  48. @Override
  49. public int getCount() {
  50. return 10;
  51. }
  52. }
  53. }

MainActivity.java需要的布局文件 activity_main.xml :

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical" >
  6. <zhangphil.android.common.view.SlidingTabLayout
  7. android:id="@+id/sliding_tabs"
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content" />
  10. <android.support.v4.view.ViewPager
  11. android:id="@+id/viewpager"
  12. android:layout_width="match_parent"
  13. android:layout_height="0px"
  14. android:layout_weight="1" />
  15. </LinearLayout>

代码运行结果如图:

Center 1

以下是谷歌官方Android实现的SlidingTabLayout.java全部源代码:

  1. package zhangphil.android.common.view;
  2. import android.content.Context;
  3. import android.graphics.Typeface;
  4. import android.os.Build;
  5. import android.support.v4.view.PagerAdapter;
  6. import android.support.v4.view.ViewPager;
  7. import android.util.AttributeSet;
  8. import android.util.TypedValue;
  9. import android.view.Gravity;
  10. import android.view.LayoutInflater;
  11. import android.view.View;
  12. import android.widget.HorizontalScrollView;
  13. import android.widget.TextView;
  14. /**
  15. * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
  16. * the user's scroll progress.
  17. * <p>
  18. * To use the component, simply add it to your view hierarchy. Then in your
  19. * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
  20. * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
  21. * <p>
  22. * The colors can be customized in two ways. The first and simplest is to provide an array of colors
  23. * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
  24. * alternative is via the {@link TabColorizer} interface which provides you complete control over
  25. * which color is used for any individual position.
  26. * <p>
  27. * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
  28. * providing the layout ID of your custom layout.
  29. */
  30. public class SlidingTabLayout extends HorizontalScrollView {
  31. /**
  32. * Allows complete control over the colors drawn in the tab layout. Set with
  33. * {@link #setCustomTabColorizer(TabColorizer)}.
  34. */
  35. public interface TabColorizer {
  36. /**
  37. * @return return the color of the indicator used when {@code position} is selected.
  38. */
  39. int getIndicatorColor(int position);
  40. /**
  41. * @return return the color of the divider drawn to the right of {@code position}.
  42. */
  43. int getDividerColor(int position);
  44. }
  45. private static final int TITLE_OFFSET_DIPS = 24;
  46. private static final int TAB_VIEW_PADDING_DIPS = 16;
  47. private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
  48. private int mTitleOffset;
  49. private int mTabViewLayoutId;
  50. private int mTabViewTextViewId;
  51. private ViewPager mViewPager;
  52. private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
  53. private final SlidingTabStrip mTabStrip;
  54. public SlidingTabLayout(Context context) {
  55. this(context, null);
  56. }
  57. public SlidingTabLayout(Context context, AttributeSet attrs) {
  58. this(context, attrs, 0);
  59. }
  60. public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
  61. super(context, attrs, defStyle);
  62. // Disable the Scroll Bar
  63. setHorizontalScrollBarEnabled(false);
  64. // Make sure that the Tab Strips fills this View
  65. setFillViewport(true);
  66. mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
  67. mTabStrip = new SlidingTabStrip(context);
  68. addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
  69. }
  70. /**
  71. * Set the custom {@link TabColorizer} to be used.
  72. *
  73. * If you only require simple custmisation then you can use
  74. * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
  75. * similar effects.
  76. */
  77. public void setCustomTabColorizer(TabColorizer tabColorizer) {
  78. mTabStrip.setCustomTabColorizer(tabColorizer);
  79. }
  80. /**
  81. * Sets the colors to be used for indicating the selected tab. These colors are treated as a
  82. * circular array. Providing one color will mean that all tabs are indicated with the same color.
  83. */
  84. public void setSelectedIndicatorColors(int... colors) {
  85. mTabStrip.setSelectedIndicatorColors(colors);
  86. }
  87. /**
  88. * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
  89. * Providing one color will mean that all tabs are indicated with the same color.
  90. */
  91. public void setDividerColors(int... colors) {
  92. mTabStrip.setDividerColors(colors);
  93. }
  94. /**
  95. * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
  96. * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
  97. * that the layout can update it's scroll position correctly.
  98. *
  99. * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
  100. */
  101. public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
  102. mViewPagerPageChangeListener = listener;
  103. }
  104. /**
  105. * Set the custom layout to be inflated for the tab views.
  106. *
  107. * @param layoutResId Layout id to be inflated
  108. * @param textViewId id of the {@link TextView} in the inflated view
  109. */
  110. public void setCustomTabView(int layoutResId, int textViewId) {
  111. mTabViewLayoutId = layoutResId;
  112. mTabViewTextViewId = textViewId;
  113. }
  114. /**
  115. * Sets the associated view pager. Note that the assumption here is that the pager content
  116. * (number of tabs and tab titles) does not change after this call has been made.
  117. */
  118. public void setViewPager(ViewPager viewPager) {
  119. mTabStrip.removeAllViews();
  120. mViewPager = viewPager;
  121. if (viewPager != null) {
  122. viewPager.setOnPageChangeListener(new InternalViewPagerListener());
  123. populateTabStrip();
  124. }
  125. }
  126. /**
  127. * Create a default view to be used for tabs. This is called if a custom tab view is not set via
  128. * {@link #setCustomTabView(int, int)}.
  129. */
  130. protected TextView createDefaultTabView(Context context) {
  131. TextView textView = new TextView(context);
  132. textView.setGravity(Gravity.CENTER);
  133. textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
  134. textView.setTypeface(Typeface.DEFAULT_BOLD);
  135. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
  136. // If we're running on Honeycomb or newer, then we can use the Theme's
  137. // selectableItemBackground to ensure that the View has a pressed state
  138. TypedValue outValue = new TypedValue();
  139. getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
  140. outValue, true);
  141. textView.setBackgroundResource(outValue.resourceId);
  142. }
  143. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
  144. // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
  145. textView.setAllCaps(true);
  146. }
  147. int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
  148. textView.setPadding(padding, padding, padding, padding);
  149. return textView;
  150. }
  151. private void populateTabStrip() {
  152. final PagerAdapter adapter = mViewPager.getAdapter();
  153. final View.OnClickListener tabClickListener = new TabClickListener();
  154. for (int i = 0; i < adapter.getCount(); i++) {
  155. View tabView = null;
  156. TextView tabTitleView = null;
  157. if (mTabViewLayoutId != 0) {
  158. // If there is a custom tab view layout id set, try and inflate it
  159. tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
  160. false);
  161. tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
  162. }
  163. if (tabView == null) {
  164. tabView = createDefaultTabView(getContext());
  165. }
  166. if (tabTitleView == null && TextView.class.isInstance(tabView)) {
  167. tabTitleView = (TextView) tabView;
  168. }
  169. tabTitleView.setText(adapter.getPageTitle(i));
  170. tabView.setOnClickListener(tabClickListener);
  171. mTabStrip.addView(tabView);
  172. }
  173. }
  174. @Override
  175. protected void onAttachedToWindow() {
  176. super.onAttachedToWindow();
  177. if (mViewPager != null) {
  178. scrollToTab(mViewPager.getCurrentItem(), 0);
  179. }
  180. }
  181. private void scrollToTab(int tabIndex, int positionOffset) {
  182. final int tabStripChildCount = mTabStrip.getChildCount();
  183. if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
  184. return;
  185. }
  186. View selectedChild = mTabStrip.getChildAt(tabIndex);
  187. if (selectedChild != null) {
  188. int targetScrollX = selectedChild.getLeft() + positionOffset;
  189. if (tabIndex > 0 || positionOffset > 0) {
  190. // If we're not at the first child and are mid-scroll, make sure we obey the offset
  191. targetScrollX -= mTitleOffset;
  192. }
  193. scrollTo(targetScrollX, 0);
  194. }
  195. }
  196. private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
  197. private int mScrollState;
  198. @Override
  199. public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
  200. int tabStripChildCount = mTabStrip.getChildCount();
  201. if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
  202. return;
  203. }
  204. mTabStrip.onViewPagerPageChanged(position, positionOffset);
  205. View selectedTitle = mTabStrip.getChildAt(position);
  206. int extraOffset = (selectedTitle != null)
  207. ? (int) (positionOffset * selectedTitle.getWidth())
  208. : 0;
  209. scrollToTab(position, extraOffset);
  210. if (mViewPagerPageChangeListener != null) {
  211. mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
  212. positionOffsetPixels);
  213. }
  214. }
  215. @Override
  216. public void onPageScrollStateChanged(int state) {
  217. mScrollState = state;
  218. if (mViewPagerPageChangeListener != null) {
  219. mViewPagerPageChangeListener.onPageScrollStateChanged(state);
  220. }
  221. }
  222. @Override
  223. public void onPageSelected(int position) {
  224. if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
  225. mTabStrip.onViewPagerPageChanged(position, 0f);
  226. scrollToTab(position, 0);
  227. }
  228. if (mViewPagerPageChangeListener != null) {
  229. mViewPagerPageChangeListener.onPageSelected(position);
  230. }
  231. }
  232. }
  233. private class TabClickListener implements View.OnClickListener {
  234. @Override
  235. public void onClick(View v) {
  236. for (int i = 0; i < mTabStrip.getChildCount(); i++) {
  237. if (v == mTabStrip.getChildAt(i)) {
  238. mViewPager.setCurrentItem(i);
  239. return;
  240. }
  241. }
  242. }
  243. }
  244. }

谷歌官方Android实现的SlidingTabStrip.java全部源代码:

  1. package zhangphil.android.common.view;
  2. import android.R;
  3. import android.content.Context;
  4. import android.graphics.Canvas;
  5. import android.graphics.Color;
  6. import android.graphics.Paint;
  7. import android.util.AttributeSet;
  8. import android.util.TypedValue;
  9. import android.view.View;
  10. import android.widget.LinearLayout;
  11. class SlidingTabStrip extends LinearLayout {
  12. private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
  13. private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
  14. private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
  15. private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
  16. private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
  17. private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
  18. private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
  19. private final int mBottomBorderThickness;
  20. private final Paint mBottomBorderPaint;
  21. private final int mSelectedIndicatorThickness;
  22. private final Paint mSelectedIndicatorPaint;
  23. private final int mDefaultBottomBorderColor;
  24. private final Paint mDividerPaint;
  25. private final float mDividerHeight;
  26. private int mSelectedPosition;
  27. private float mSelectionOffset;
  28. private SlidingTabLayout.TabColorizer mCustomTabColorizer;
  29. private final SimpleTabColorizer mDefaultTabColorizer;
  30. SlidingTabStrip(Context context) {
  31. this(context, null);
  32. }
  33. SlidingTabStrip(Context context, AttributeSet attrs) {
  34. super(context, attrs);
  35. setWillNotDraw(false);
  36. final float density = getResources().getDisplayMetrics().density;
  37. TypedValue outValue = new TypedValue();
  38. context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
  39. final int themeForegroundColor = outValue.data;
  40. mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
  41. DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
  42. mDefaultTabColorizer = new SimpleTabColorizer();
  43. mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
  44. mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
  45. DEFAULT_DIVIDER_COLOR_ALPHA));
  46. mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
  47. mBottomBorderPaint = new Paint();
  48. mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
  49. mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
  50. mSelectedIndicatorPaint = new Paint();
  51. mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
  52. mDividerPaint = new Paint();
  53. mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
  54. }
  55. void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
  56. mCustomTabColorizer = customTabColorizer;
  57. invalidate();
  58. }
  59. void setSelectedIndicatorColors(int... colors) {
  60. // Make sure that the custom colorizer is removed
  61. mCustomTabColorizer = null;
  62. mDefaultTabColorizer.setIndicatorColors(colors);
  63. invalidate();
  64. }
  65. void setDividerColors(int... colors) {
  66. // Make sure that the custom colorizer is removed
  67. mCustomTabColorizer = null;
  68. mDefaultTabColorizer.setDividerColors(colors);
  69. invalidate();
  70. }
  71. void onViewPagerPageChanged(int position, float positionOffset) {
  72. mSelectedPosition = position;
  73. mSelectionOffset = positionOffset;
  74. invalidate();
  75. }
  76. @Override
  77. protected void onDraw(Canvas canvas) {
  78. final int height = getHeight();
  79. final int childCount = getChildCount();
  80. final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
  81. final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
  82. ? mCustomTabColorizer
  83. : mDefaultTabColorizer;
  84. // Thick colored underline below the current selection
  85. if (childCount > 0) {
  86. View selectedTitle = getChildAt(mSelectedPosition);
  87. int left = selectedTitle.getLeft();
  88. int right = selectedTitle.getRight();
  89. int color = tabColorizer.getIndicatorColor(mSelectedPosition);
  90. if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
  91. int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
  92. if (color != nextColor) {
  93. color = blendColors(nextColor, color, mSelectionOffset);
  94. }
  95. // Draw the selection partway between the tabs
  96. View nextTitle = getChildAt(mSelectedPosition + 1);
  97. left = (int) (mSelectionOffset * nextTitle.getLeft() +
  98. (1.0f - mSelectionOffset) * left);
  99. right = (int) (mSelectionOffset * nextTitle.getRight() +
  100. (1.0f - mSelectionOffset) * right);
  101. }
  102. mSelectedIndicatorPaint.setColor(color);
  103. canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
  104. height, mSelectedIndicatorPaint);
  105. }
  106. // Thin underline along the entire bottom edge
  107. canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
  108. // Vertical separators between the titles
  109. int separatorTop = (height - dividerHeightPx) / 2;
  110. for (int i = 0; i < childCount - 1; i++) {
  111. View child = getChildAt(i);
  112. mDividerPaint.setColor(tabColorizer.getDividerColor(i));
  113. canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
  114. separatorTop + dividerHeightPx, mDividerPaint);
  115. }
  116. }
  117. /**
  118. * Set the alpha value of the {@code color} to be the given {@code alpha} value.
  119. */
  120. private static int setColorAlpha(int color, byte alpha) {
  121. return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
  122. }
  123. /**
  124. * Blend {@code color1} and {@code color2} using the given ratio.
  125. *
  126. * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
  127. * 0.0 will return {@code color2}.
  128. */
  129. private static int blendColors(int color1, int color2, float ratio) {
  130. final float inverseRation = 1f - ratio;
  131. float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
  132. float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
  133. float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
  134. return Color.rgb((int) r, (int) g, (int) b);
  135. }
  136. private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
  137. private int[] mIndicatorColors;
  138. private int[] mDividerColors;
  139. @Override
  140. public final int getIndicatorColor(int position) {
  141. return mIndicatorColors[position % mIndicatorColors.length];
  142. }
  143. @Override
  144. public final int getDividerColor(int position) {
  145. return mDividerColors[position % mDividerColors.length];
  146. }
  147. void setIndicatorColors(int... colors) {
  148. mIndicatorColors = colors;
  149. }
  150. void setDividerColors(int... colors) {
  151. mDividerColors = colors;
  152. }
  153. }
  154. }

发表评论

表情:
评论列表 (有 0 条评论,110人围观)

还没有评论,来说两句吧...

相关阅读