1. import android.content.Context;
  2. import android.graphics.Canvas;
  3. import android.graphics.Paint;
  4. import android.graphics.Typeface;
  5. import android.support.v4.view.ViewCompat;
  6. import android.support.v4.view.ViewPager;
  7. import android.support.v4.widget.EdgeEffectCompat;
  8. import android.support.v4.widget.ScrollerCompat;
  9. import android.util.AttributeSet;
  10. import android.util.TypedValue;
  11. import android.view.Gravity;
  12. import android.view.MotionEvent;
  13. import android.view.VelocityTracker;
  14. import android.view.View;
  15. import android.view.ViewConfiguration;
  16. import android.view.ViewGroup;
  17. import android.view.ViewParent;
  18. import android.view.ViewTreeObserver;
  19. import android.widget.ImageButton;
  20. import android.widget.TextView;
  21.  
  22. import com.shiwen.oil.R;
  23. import com.shiwen.oil.base.BaseActivity;
  24. import com.shiwen.oil.util.UiUtil;
  25.  
  26. public class PagerTab extends ViewGroup {
  27.  
  28. private ViewPager mViewPager;
  29. private PageListener mPageListener = new PageListener();//用于注册给ViewPager监听状态和滚动
  30. private ViewPager.OnPageChangeListener mDelegatePageListener;//用于通知外界ViewPager的状态和滚动
  31. private BaseActivity mActivity;
  32.  
  33. private int mDividerPadding = 12;// 分割线上下的padding
  34. private int mDividerWidth = 1;// 分割线的宽度
  35. private int mDividerColor = 0x1A000000;//分割线颜色
  36. private Paint mDividerPaint;//分割线的画笔
  37.  
  38. private int mIndicatorHeight = 4;//指示器的高度
  39. private int mIndicatorWidth;//指示器的宽度,是动态的随着tab的宽度变化
  40. private int mIndicatorLeft;//指示器的距离左边的距离
  41. private int mIndicatorColor = 0xFF2484E8;//指示器颜色
  42. private Paint mIndicatorPaint; //指示器的画笔
  43.  
  44. private int mContentWidth;//记录自身内容的宽度
  45. private int mContentHeight;//记录自身内容的高度
  46.  
  47. private int mTabPadding = 24;// tab左右的内边距
  48. private int mTabTextSize = 16; //tab文字大小
  49. private int mTabBackgroundResId = R.drawable.bg_tab_text;// tab背景资源
  50. private int mTabTextColorResId = R.color.tab_text_color; //tab文字颜色
  51. private int mTabCount;//tab的个数
  52.  
  53. private int mCurrentPosition = 0;//当前光标所处的tab,规则是以光标的最左端所在的item的position
  54. private float mCurrentOffsetPixels;//光标左边距离当前光标所处的tab的左边距离
  55. private int mSelectedPosition = 0; //当前被选中的tab,用于记录手指点击tab的position
  56.  
  57. private boolean mIsBeingDragged = false;//是否处于拖动中
  58. private float mLastMotionX;//上一次手指触摸的x坐标
  59. private VelocityTracker mVelocityTracker;//用于记录速度的帮助类
  60. private int mMinimumVelocity;//系统默认的最小满足fling的速度
  61. private int mMaximumVelocity;//系统默认最大的fling速度
  62. private int mTouchSlop;//系统默认满足滑动的最小位移
  63.  
  64. private ScrollerCompat mScroller;//处理滚动的帮助者
  65. private int mLastScrollX;//记录上一次滚动的x位置,这是用于处理overScroll,实际位置可能会受到限制
  66.  
  67. private int mMaxScrollX = 0;// 控件最大可滚动的距离
  68. private int mSplitScrollX = 0;// 根据item的个数,计算出每移动一个item控件需要移动的距离
  69.  
  70. private EdgeEffectCompat mLeftEdge;//处理overScroll的反馈效果
  71. private EdgeEffectCompat mRightEdge;
  72.  
  73. public PagerTab(Context context) {
  74. this(context, null);
  75. }
  76.  
  77. public PagerTab(Context context, AttributeSet attrs) {
  78. this(context, attrs, -1);
  79. }
  80.  
  81. public PagerTab(Context context, AttributeSet attrs, int defStyle) {
  82. super(context, attrs, defStyle);
  83. if (context instanceof BaseActivity) {
  84. mActivity = (BaseActivity) context;
  85. }
  86. init();
  87. initPaint();
  88. }
  89.  
  90. /** 初始化一些常量 */
  91. private void init() {
  92. System.out.println("init");
  93. //把一个值从dip转换成px
  94. mIndicatorHeight = UiUtil.dip2px(mIndicatorHeight);
  95. mDividerPadding = UiUtil.dip2px(mDividerPadding);
  96. mTabPadding = UiUtil.dip2px(mTabPadding);
  97. mDividerWidth = UiUtil.dip2px(mDividerWidth);
  98. mTabTextSize = UiUtil.dip2px(mTabTextSize);
  99. //创建一个scroller
  100. // mScroller =new Scroller(mActivity);
  101. mScroller = ScrollerCompat.create(mActivity);
  102. //获取一个系统关于View的常量配置类
  103. final ViewConfiguration configuration = ViewConfiguration.get(mActivity);
  104. //获取滑动的最小距离
  105. mTouchSlop = configuration.getScaledTouchSlop();
  106. //获取fling的最小速度
  107. mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
  108. //获取fling的最大速度
  109. mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
  110.  
  111. mLeftEdge = new EdgeEffectCompat(mActivity);
  112. mRightEdge = new EdgeEffectCompat(mActivity);
  113. }
  114.  
  115. /** 初始化笔 */
  116. private void initPaint() {
  117. mIndicatorPaint = new Paint();
  118. mIndicatorPaint.setAntiAlias(true);
  119. mIndicatorPaint.setStyle(Paint.Style.FILL);
  120. mIndicatorPaint.setColor(mIndicatorColor);
  121.  
  122. mDividerPaint = new Paint();
  123. mDividerPaint.setAntiAlias(true);
  124. mDividerPaint.setStrokeWidth(mDividerWidth);
  125. mDividerPaint.setColor(mDividerColor);
  126. }
  127.  
  128. /** 设置ViewPager */
  129. public void setViewPager(ViewPager viewPager) {
  130. if (viewPager == null || viewPager.getAdapter() == null) {
  131. throw new IllegalStateException("ViewPager is null or ViewPager does not have adapter instance.");
  132. }
  133. mViewPager = viewPager;
  134. onViewPagerChanged();
  135. }
  136.  
  137. private void onViewPagerChanged() {
  138. mViewPager.addOnPageChangeListener(mPageListener);
  139. // mViewPager.setOnPageChangeListener(mPageListener);//给ViewPager设置监听
  140. mTabCount = mViewPager.getAdapter().getCount();//有多少个tab需要看ViewPager有多少个页面
  141. for (int i = 0; i < mTabCount; i++) {
  142. if (mViewPager.getAdapter() instanceof IconTabProvider) {//如果想要使用icon作为tab,则需要adapter实现IconTabProvider接口
  143. addIconTab(i, ((IconTabProvider) mViewPager.getAdapter()).getPageIconResId(i));
  144. } else {
  145. addTextTab(i, mViewPager.getAdapter().getPageTitle(i).toString());
  146. }
  147. }
  148. ViewTreeObserver viewTreeObserver = getViewTreeObserver();
  149. if (viewTreeObserver != null) {//监听第一个的全局layout事件,来设置当前的mCurrentPosition,显示对应的tab
  150. viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
  151. @Override
  152. public void onGlobalLayout() {
  153. getViewTreeObserver().removeGlobalOnLayoutListener(this);//只需要监听一次,之后通过listener回调即可
  154. mCurrentPosition = mViewPager.getCurrentItem();
  155. if (mDelegatePageListener != null) {
  156. mDelegatePageListener.onPageSelected(mCurrentPosition);
  157. }
  158. }
  159. });
  160. }
  161. }
  162.  
  163. /** 设置监听,因为Tab会监听ViewPager的状态,所以不要给ViewPager设置监听了,设置给Tab,由Tab转发 */
  164. public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
  165. mDelegatePageListener = listener;
  166. }
  167.  
  168. /** 添加文字tab */
  169. private void addTextTab(final int position, String title) {
  170. TextView tab = new TextView(mActivity);
  171. tab.setText(title);
  172. tab.setGravity(Gravity.CENTER);
  173. tab.setSingleLine();
  174. tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTabTextSize);
  175. tab.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
  176. tab.setTextColor(UiUtil.getColorStateList(mTabTextColorResId,mActivity));
  177. // tab.setBackgroundDrawable(UiUtil.getDrawable(mTabBackgroundResId));
  178. tab.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
  179. addTab(position, tab);
  180. }
  181.  
  182. /** 添加图片icon */
  183. private void addIconTab(final int position, int resId) {
  184. ImageButton tab = new ImageButton(mActivity);
  185. tab.setImageResource(resId);
  186. tab.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
  187. addTab(position, tab);
  188. selectTab(0);
  189. }
  190.  
  191. private void addTab(final int position, View tab) {
  192. tab.setFocusable(true);
  193. //设置tab的点击事件,当tab被点击时候切换pager的页面
  194. tab.setOnClickListener(new OnClickListener() {
  195. @Override
  196. public void onClick(View v) {
  197. mViewPager.setCurrentItem(position);
  198. }
  199. });
  200. tab.setPadding(mTabPadding, 0, mTabPadding, 0);
  201. addView(tab, position);
  202. selectTab(0);
  203. }
  204.  
  205. /** 测量时的回调 */
  206. @Override
  207. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  208. // 获取控件自身的宽高,模式
  209. int widthSize = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight();
  210. int heightSize = MeasureSpec.getSize(heightMeasureSpec) - getPaddingBottom() - getPaddingBottom();
  211. int widthMode = MeasureSpec.getMode(widthMeasureSpec);
  212. int heightMode = MeasureSpec.getMode(heightMeasureSpec);
  213.  
  214. int totalWidth = 0;
  215. int highest = 0;
  216. int goneChildCount = 0;
  217. for (int i = 0; i < mTabCount; i++) {
  218. final View child = getChildAt(i);
  219. if (child == null || child.getVisibility() == View.GONE) {
  220. goneChildCount--;
  221. continue;
  222. }
  223. int childWidthMeasureSpec;
  224. int childHeightMeasureSpec;
  225.  
  226. LayoutParams childLayoutParams = child.getLayoutParams();
  227. if (childLayoutParams == null) {
  228. childLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
  229. }
  230.  
  231. if (childLayoutParams.width == LayoutParams.MATCH_PARENT) {
  232. childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
  233. } else if (childLayoutParams.width == LayoutParams.WRAP_CONTENT) {
  234. childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
  235. } else {
  236. childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(childLayoutParams.width, MeasureSpec.EXACTLY);
  237. }
  238.  
  239. if (childLayoutParams.height == LayoutParams.MATCH_PARENT) {
  240. childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
  241. } else if (childLayoutParams.height == LayoutParams.WRAP_CONTENT) {
  242. childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.AT_MOST);
  243. } else {
  244. childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(childLayoutParams.height, MeasureSpec.EXACTLY);
  245. }
  246.  
  247. child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  248.  
  249. int childWidth = child.getMeasuredWidth();
  250. int childHeight = child.getMeasuredHeight();
  251.  
  252. totalWidth += childWidth;
  253. highest = highest < childHeight ? childHeight : highest;
  254. }
  255.  
  256. if (totalWidth <= widthSize) {//如果子Tab的总宽度小于PagerTab,则采用平分模式
  257. int splitWidth = (int) (widthSize / (mTabCount - goneChildCount + 0.0f) + 0.5f);
  258. for (int i = 0; i < mTabCount; i++) {
  259. final View child = getChildAt(i);
  260. if (child == null || child.getVisibility() == View.GONE) {
  261. continue;
  262. }
  263. int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(splitWidth, MeasureSpec.EXACTLY);
  264. int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(), MeasureSpec.EXACTLY);
  265. child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  266. }
  267. mMaxScrollX = 0;
  268. mSplitScrollX = 0;
  269. } else {//如果所有子View大于控件的宽度
  270. mMaxScrollX = totalWidth - widthSize;
  271. mSplitScrollX = (int) (mMaxScrollX / (mTabCount - goneChildCount - 1.0f) + 0.5f);
  272. }
  273.  
  274. if (widthMode == MeasureSpec.EXACTLY) {
  275. mContentWidth = widthSize;
  276. } else {
  277. mContentWidth = totalWidth;
  278. }
  279.  
  280. if (heightMode == MeasureSpec.EXACTLY) {
  281. mContentHeight = heightSize;
  282. } else {
  283. mContentHeight = highest;
  284. }
  285.  
  286. int measureWidth = mContentWidth + getPaddingLeft() + getPaddingRight();
  287. int measureHeight = mContentHeight + getPaddingTop() + getPaddingBottom();
  288. setMeasuredDimension(measureWidth, measureHeight);
  289. }
  290.  
  291. /** 布局时的回调 */
  292. @Override
  293. protected void onLayout(boolean changed, int l, int t, int r, int b) {//这里简化了,没有考虑margin的情况
  294. if (changed) {
  295. int height = b - t;//控件供子View显示的高度
  296. int left = l;
  297. for (int i = 0; i < mTabCount; i++) {
  298. final View child = getChildAt(i);
  299. if (child == null || child.getVisibility() == View.GONE) {
  300. continue;
  301. }
  302. int top = (int) ((height - child.getMeasuredHeight()) / 2.0f + 0.5f);//如果控件比tab要高,则居中显示
  303. int right = left + child.getMeasuredWidth();
  304. child.layout(left, top, right, top + child.getMeasuredHeight());//摆放tab
  305. left = right;//因为是水平摆放的,所以为下一个准备left值
  306. }
  307. }
  308. }
  309.  
  310. /** 绘制时的回调 */
  311. @Override
  312. protected void onDraw(Canvas canvas) {
  313. super.onDraw(canvas);
  314. final int height = getHeight();
  315. //画指示器
  316. canvas.drawRect(mIndicatorLeft, height - mIndicatorHeight, mIndicatorLeft + mIndicatorWidth, height, mIndicatorPaint);
  317.  
  318. // 画分割线
  319. for (int i = 0; i < mTabCount - 1; i++) {//分割线的个数比tab的个数少一个
  320. final View child = getChildAt(i);
  321.  
  322. if (child == null || child.getVisibility() == View.GONE) {
  323.  
  324. continue;
  325. }
  326. if (child != null) {
  327.  
  328. canvas.drawLine(child.getRight(), mDividerPadding, child.getRight(), mContentHeight - mDividerPadding, mDividerPaint);
  329. }
  330. }
  331. // 因为overScroll效果是一个持续效果,所以需要持续画
  332. boolean needsInvalidate = false;
  333. if (!mLeftEdge.isFinished()) {//如果效果没停止
  334. final int restoreCount = canvas.save();//先保存当前画布
  335. final int heightEdge = getHeight() - getPaddingTop() - getPaddingBottom();
  336. final int widthEdge = getWidth();
  337. canvas.rotate(270);
  338. canvas.translate(-heightEdge + getPaddingTop(), 0);
  339. mLeftEdge.setSize(heightEdge, widthEdge);
  340. needsInvalidate |= mLeftEdge.draw(canvas);
  341. canvas.restoreToCount(restoreCount);
  342. }
  343. if (!mRightEdge.isFinished()) {
  344. final int restoreCount = canvas.save();
  345. final int widthEdge = getWidth();
  346. final int heightEdge = getHeight() - getPaddingTop() - getPaddingBottom();
  347. canvas.rotate(90);
  348. canvas.translate(-getPaddingTop(), -(widthEdge + mMaxScrollX));
  349. mRightEdge.setSize(heightEdge, widthEdge);
  350. needsInvalidate |= mRightEdge.draw(canvas);
  351. canvas.restoreToCount(restoreCount);
  352. }
  353. if (needsInvalidate) {
  354. postInvalidate();
  355. }
  356. }
  357.  
  358. /** 触摸事件是否拦截的方法 */
  359. @Override
  360. public boolean onInterceptTouchEvent(MotionEvent ev) {
  361. final int action = ev.getAction();
  362. if (mIsBeingDragged && action == MotionEvent.ACTION_MOVE) {//当已经处于拖动,并且当前事件是MOVE,直接消费掉
  363. return true;
  364. }
  365. switch (action) {
  366. case MotionEvent.ACTION_DOWN: {
  367. final float x = ev.getX();
  368. mLastMotionX = x; //记录住当前的x坐标
  369. mIsBeingDragged = !mScroller.isFinished();//如果按下的时候还在滚动,则把状态处于拖动状态
  370. break;
  371. }
  372. case MotionEvent.ACTION_MOVE: {
  373. final float x = ev.getX();
  374. final int xDiff = (int) Math.abs(x - mLastMotionX);//计算两次的差值
  375. if (xDiff > mTouchSlop) {//如果大于最小移动的距离,则把状态改变为拖动状态
  376. mIsBeingDragged = true;
  377. mLastMotionX = x;
  378. ViewParent parent = getParent();//并请求父View不要再拦截自己触摸事件,交给自己处理
  379. if (parent != null) {
  380. parent.requestDisallowInterceptTouchEvent(true);
  381. }
  382. }
  383. break;
  384. }
  385. case MotionEvent.ACTION_CANCEL://当手指离开或者触摸事件取消的时候,把拖动状态取消掉
  386. case MotionEvent.ACTION_UP:
  387. mIsBeingDragged = false;
  388. break;
  389. }
  390. return mIsBeingDragged;//如果是拖动状态,则拦截事件,交给自己的onTouch处理
  391. }
  392.  
  393. /** 触摸事件的处理方法 */
  394. public boolean onTouchEvent(MotionEvent ev) {
  395. if (mVelocityTracker == null) {
  396. mVelocityTracker = VelocityTracker.obtain();
  397. }
  398. mVelocityTracker.addMovement(ev);
  399. final int action = ev.getAction();
  400. switch (action) {
  401. case MotionEvent.ACTION_DOWN: {//如果是down事件,记录住当前的x坐标
  402. final float x = ev.getX();
  403. if (!mScroller.isFinished()) {
  404. mScroller.abortAnimation();
  405. }
  406. mLastMotionX = x;
  407. break;
  408. }
  409. case MotionEvent.ACTION_MOVE: {
  410. final float x = ev.getX();
  411. final float deltaX = x - mLastMotionX;
  412. if (!mIsBeingDragged) {//如果还没有处于拖动,则判断两次的差值是否大于最小拖动的距离
  413. if (Math.abs(deltaX) > mTouchSlop) {
  414. mIsBeingDragged = true;
  415. }
  416. }
  417. if (mIsBeingDragged) {//如果处于拖动状态,记录住x坐标
  418. mLastMotionX = x;
  419. onMove(deltaX);
  420. }
  421. break;
  422. }
  423. case MotionEvent.ACTION_UP: {
  424. if (mIsBeingDragged) {
  425. final VelocityTracker velocityTracker = mVelocityTracker;
  426. //先对速度进行一个调整,第一个参数是时间单位,1000毫秒,第二个参数是最大速度。
  427. velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  428. float velocity = velocityTracker.getXVelocity();//获取水平方向上的速度
  429. onUp(velocity);
  430. }
  431. }
  432. case MotionEvent.ACTION_CANCEL: {
  433. mIsBeingDragged = false;
  434. if (mVelocityTracker != null) {
  435. mVelocityTracker.recycle();
  436. mVelocityTracker = null;
  437. }
  438. break;
  439. }
  440. }
  441. return true;
  442. }
  443.  
  444. private void onMove(float x) {
  445. if (mMaxScrollX <= 0) {
  446. if (mViewPager.isFakeDragging() || mViewPager.beginFakeDrag()) {
  447. mViewPager.fakeDragBy(x);
  448. }
  449. } else {
  450. int scrollByX = -(int) (x + 0.5);
  451. if (getScrollX() + scrollByX < 0) {
  452. scrollByX = 0 - getScrollX();
  453. mLeftEdge.onPull(Math.abs(x) / getWidth());
  454. }
  455. if (getScrollX() + scrollByX > mMaxScrollX) {
  456. scrollByX = mMaxScrollX - getScrollX();
  457. mRightEdge.onPull(Math.abs(x) / getWidth());
  458. }
  459. scrollBy(scrollByX, 0);
  460. ViewCompat.postInvalidateOnAnimation(this);
  461. }
  462. }
  463.  
  464. private void onUp(float velocity) {
  465. if (mMaxScrollX <= 0) {
  466. if (mViewPager.isFakeDragging()) mViewPager.endFakeDrag();
  467. } else {
  468. if (Math.abs(velocity) <= mMinimumVelocity) {
  469. return;
  470. }
  471. mScroller.fling(getScrollX(), 0, -(int) (velocity + 0.5), 0, 0, mMaxScrollX, 0, 0, 270, 0);
  472. ViewCompat.postInvalidateOnAnimation(this);
  473. }
  474. }
  475.  
  476. @Override
  477. public void computeScroll() {
  478. if (mScroller.computeScrollOffset()) {
  479. int oldX = mLastScrollX;
  480. mLastScrollX = mScroller.getCurrX();
  481. if (mLastScrollX < 0 && oldX >= 0) {
  482. mLeftEdge.onAbsorb((int) mScroller.getCurrVelocity());
  483. } else if (mLastScrollX > mMaxScrollX && oldX <= mMaxScrollX) {
  484. mRightEdge.onAbsorb((int) mScroller.getCurrVelocity());
  485. }
  486. int x = mLastScrollX;
  487. if (mLastScrollX < 0) {
  488. x = 0;
  489. } else if (mLastScrollX > mMaxScrollX) {
  490. x = mMaxScrollX;
  491. }
  492. scrollTo(x, 0);
  493. }
  494. ViewCompat.postInvalidateOnAnimation(this);
  495. }
  496.  
  497. /** 检测mIndicatorOffset的合法性,并计算出其他有关tab的属性值 */
  498. private void checkAndcalculate() {
  499. // 如果指示器起始位置比第一个tab的起始位置还要小,纠正为第一个tab的起始位置,指示器宽度就是第一个tab的宽度
  500. final View firstTab = getChildAt(0);
  501. if (mIndicatorLeft < firstTab.getLeft()) {
  502. mIndicatorLeft = firstTab.getLeft();
  503. mIndicatorWidth = firstTab.getWidth();
  504. }
  505. // 如果指示器起始位置比最后一个tab的起始位置还要大,纠正为最后一个tab的起始位置,指示器宽度就是最后一个tab的宽度
  506. View lastTab = getChildAt(mTabCount - 1);
  507. if (mIndicatorLeft > lastTab.getLeft()) {
  508. mIndicatorLeft = lastTab.getLeft();
  509. mIndicatorWidth = lastTab.getWidth();
  510. }
  511. // 通过指示器的起始位置计算出当前处于第几个position,并且计算出已经偏移了多少,偏移量是以当前所处的tab的宽度的百分比
  512. for (int i = 0; i < mTabCount; i++) {
  513. View tab = getChildAt(i);
  514. if (mIndicatorLeft < tab.getLeft()) {
  515. mCurrentPosition = i - 1;
  516. View currentTab = getChildAt(mCurrentPosition);
  517. mCurrentOffsetPixels = (mIndicatorLeft - currentTab.getLeft()) / (currentTab.getWidth() + 0.0f);
  518. break;
  519. }
  520. }
  521. }
  522.  
  523. /** 滚动到指定的child */
  524. public void scrollSelf(int position, float offset) {
  525. if (position >= mTabCount) {
  526. return;
  527. }
  528. final View tab = getChildAt(position);
  529. mIndicatorLeft = (int) (tab.getLeft() + tab.getWidth() * offset + 0.5);
  530. int rightPosition = position + 1;
  531. if (offset > 0 && rightPosition < mTabCount) {
  532. View rightTab = getChildAt(rightPosition);
  533. mIndicatorWidth = (int) (tab.getWidth() * (1 - offset) + rightTab.getWidth() * offset + 0.5);
  534. } else {
  535. mIndicatorWidth = tab.getWidth();
  536. }
  537. checkAndcalculate();
  538.  
  539. int newScrollX = position * mSplitScrollX + (int) (offset * mSplitScrollX + 0.5);
  540. if (newScrollX < 0) {
  541. newScrollX = 0;
  542. }
  543. if (newScrollX > mMaxScrollX) {
  544. newScrollX = mMaxScrollX;
  545. }
  546. //scrollTo(newScrollX, 0);//滑动
  547. int duration = 100;
  548. if (mSelectedPosition != -1) {
  549. duration = (Math.abs(mSelectedPosition - position)) * 100;
  550. }
  551. mScroller.startScroll(getScrollX(), 0, (newScrollX - getScrollX()), 0, duration);
  552. ViewCompat.postInvalidateOnAnimation(this);
  553. }
  554.  
  555. /** 选中指定位置的Tab */
  556. private void selectTab(int position) {
  557. System.out.println("selectTab");
  558. for (int i = 0; i < mTabCount; i++) {
  559. View tab = getChildAt(i);
  560. if (tab != null) {
  561. tab.setSelected(position == i);
  562. }
  563. }
  564. }
  565.  
  566. /** ViewPager的OnPageChangeListener实现类,因为我们需要在PagerTab中获取PagerView的监听,以便可以调整tab */
  567. private class PageListener implements ViewPager.OnPageChangeListener {
  568. @Override
  569. public void onPageScrolled(int position, float positionOffset, final int positionOffsetPixels) {
  570. //根据VierPager的偏移值来滚动tab
  571. scrollSelf(position, positionOffset);
  572. if (mDelegatePageListener != null) {//这个是提供给外部的
  573. mDelegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
  574. }
  575. }
  576.  
  577. @Override
  578. public void onPageScrollStateChanged(int state) {
  579. if (state == ViewPager.SCROLL_STATE_IDLE) {
  580. mSelectedPosition = -1;
  581. }
  582. if (mDelegatePageListener != null) {
  583. mDelegatePageListener.onPageScrollStateChanged(state);
  584. }
  585. }
  586.  
  587. @Override
  588. public void onPageSelected(int position) {
  589. System.out.println("onPageSelected:" + position);
  590. mSelectedPosition = position;
  591. selectTab(position);
  592. if (mDelegatePageListener != null) {
  593. mDelegatePageListener.onPageSelected(position);
  594. }
  595. }
  596. }
  597.  
  598. /** 如果指示器希望是图片,则继承该接口 */
  599. public interface IconTabProvider {
  600. public int getPageIconResId(int position);
  601. public int getPageSelectedIconResId();
  602. }
  603. }

bg_tab_color.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">
  3. <item android:state_selected="true" android:color="@color/text_blue1"/>
  4. <item android:color="@color/text_gray1"/>
  5. </selector>

bg_tab_text.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">
  3. <item android:state_selected="true" android:color="@color/text_blue1"/>
  4. <item android:color="@color/text_gray1"/>
  5. </selector>

使用步骤:

Activity布局如下:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:orientation="vertical"
  6. android:layout_height="match_parent"
  7. >
  8. <com.my.widget.PagerTab
  9. android:id="@+id/pt_tab"
  10. android:background="@color/white"
  11. android:layout_gravity="center"
  12. android:layout_width="match_parent"
  13. android:layout_height="40dp"/>
  14.  
  15. <android.support.v4.view.ViewPager
  16. android:id="@+id/vp_notice"
  17. android:layout_width="match_parent"
  18. android:layout_height="match_parent"/>
  19. </LinearLayout>

代码如下:

  1. MyAdapter noticeAdapter = new MyAdapter (getSupportFragmentManager(),this);
  2. viewPager.setAdapter(noticeAdapter);
  3. pagerTab.setViewPager(viewPager);

viewpager标签栏之PagerTab的更多相关文章

  1. 【练习】ViewPager标签滑动

    效果图: 布局: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:a ...

  2. ViewPager + HorizontalScrollView 实现可滚动的标签栏

    这是一个可滑动的标签栏的自定义控件,参考此文章http://blog.csdn.net/fx_sky/article/details/8990573,我将主要的功能整合成一个类,配上2个特定的布局即可 ...

  3. ViewPager及PagerTabStrip 的使用详解

    ViewPager 就是一个滑屏效果的一个控件,使用比较简单.使用过程思路流程基本如下: 在需要添加的ViewPager的布局文件中添加ViewPager控件--->准备好滑屏所有的View-- ...

  4. ViewPager的使用

    在上培训课的时候,老师一直在将ViewPager是现在的主流,一直想去好好的了解一下,今天去网上学习了一下   ,做一个总结: ViewPager其实就是后来谷歌提供给我们的一个组件,就像TextVi ...

  5. Android ViewPager使用详解

    这是谷歌官方给我们提供的一个兼容低版本安卓设备的软件包,里面包囊了只有在安卓3.0以上可以使用的api.而viewpager就是其中之一利用它,我们可以做很多事情,从最简单的导航,到页面菜单等等.那如 ...

  6. 转:ViewPager+Fragment基本使用方法(附源码)

    ViewPager+Fragment可以做出多页面滑动效果,让我们的应用程序界面操作起来更加灵活 对于ViewPager和Fragment组件还不熟悉的朋友,可以先看看相关的资料 首先在activit ...

  7. 【Android 界面效果21】Android ViewPager使用详解

    这是谷歌官方给我们提供的一个兼容低版本安卓设备的软件包,里面包囊了只有在安卓3.0以上可以使用的api.而viewpager就是其中之一利用它,我们可以做很多事情,从最简单的导航,到页面菜单等等.那如 ...

  8. Android ViewPager使用具体解释

    这是谷歌官方给我们提供的一个兼容低版本号安卓设备的软件包,里面包囊了仅仅有在安卓3.0以上能够使用的api.而viewpager就是当中之中的一个利用它,我们能够做非常多事情,从最简单的导航,到页面菜 ...

  9. Android开发之ViewPager

    什么是ViewPager? ViewPager是安卓3.0之后提供的新特性,继承自ViewGroup,专门用以实现左右滑动切换View的效果. 如果想向下兼容就必须要android-support-v ...

随机推荐

  1. 仿造email后缀搜索功能(2)

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  2. 关于overflow的学习

    我在此记录一下我的学习到的东西,我自己不清楚所以要记录下来. overflow:hidden 但内元素的高度或宽度大于外元素的高度或宽度时,自动隐藏多余的部分,当然外元素设置了固定的高度或宽度. ov ...

  3. python 出现OSError: [Errno 8] Exec format error的原因

    访问 .py文件的网页的时候会出现 Exec format error的问题, 一般情况下是由于基于Unix(Linux,Mac OS)系统下的问题,办法如下 1 .chmod +x  filenam ...

  4. pathlib:处理文件路径的瑞士军刀

    pathlib是python3.4中引入的模块,专门用来处理路径的.个人觉得这是一个非常非常强大的模块,可以说是处理路径的一把瑞士军刀,下面我们就来看看它的功能. 在pathlib中有一个Path这个 ...

  5. Chrome安装Axure插件axure-chrome-extension

    用Chrome打开Axure发布的原型图打不开,提示需要安装axure-chrome-extension插件,如下图提示 下面记录一下安装过程,其实很简单,插件没必要从网上到处找,在你发布的路径下就有 ...

  6. Linux SWAP交换分区维护

    1.查看当前swap分区信息

  7. Summer training round2 #3

    A!:                    GTY系列题 B!:莫队加分块  GTY系列题 C!:线段树模拟拓扑排序(把普通的拓扑排序的栈操作改成线段树区间减一,查询区间最右侧的0的位置即可.注意一 ...

  8. 认识配置文件schema.xml(managed-schema)

    1.schema文件是在SolrConfig中的架构工厂定义,有两种定义模式: 1.1.默认的托管模式: solr默认使用的就是托管模式.也就是当在solrconfig.xml文件中没有显式声明< ...

  9. 安装tidb数据库

    1.下载压缩包 安装tar包路径 命令:wget http://download.pingcap.org/tidb-latest-linux-amd64.tar.gz 命令:wget http://d ...

  10. Socket的通信机制?

    套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元.它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议 ...