一步步撸出ViewpagerIndicator(精简版)

实现的ViewPagerIndicator的精简版.很多内容参照google的源码

Posted by Mio4kon on 2014-12-30

一步步撸出ViewpagerIndicator(精简版)

写一个类PagerTab extends ViewGroup

写一个方法设置ViewPager,通过ViewPager可以得到Tab的个数

public void setViewPager(ViewPager viewPager){
    mViewPager = viewPager;
    onViewPagerChange();
}

private void onViewPagerChange() {
    mViewPager.setOnPageChangeListener (mPagerListener);
    mTabCount = mViewPager.getAdapter ().getCount ();

    for(int i = 0; i < mTabCount; i++) {
        addTextTab(i,mViewPager.getAdapter ().getPageTitle (i).toString ());
    }
}

创建Tab:TextView,并添加到自身

private void addTextTab(int position, String title) {
    TextView tab =new TextView (getContext ());
    tab.setText (title);
       ...
    //默认第1个tab是选中状态
    if (position == 0) {
        tab.setSelected (true);
        mViewPager.setCurrentItem (0);
    }

    tab.setOnClickListener (new OnClickListener () {
        @Override
        public void onClick(View v) {
            mViewPager.setCurrentItem (position);
        }
    });

    addView (tab,position);
}

计算子View和自身的大小–>onMeasure

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    ... 拿到合成数的Size & Mode
    //测量子View
    for(int i = 0; i < mTabCount; i++) {
        View child = getChildAt (i);
        int childWidthMeasureSpec;
        int childHeightMeasureSpec;
        LayoutParams childLayoutParams = child.getLayoutParams ();

        if(childLayoutParams.width == LayoutParams.MATCH_PARENT){
            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec (widthSize,MeasureSpec.EXACTLY);
        }else if (childLayoutParams.width == LayoutParams.WRAP_CONTENT){
            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec (widthSize, MeasureSpec.AT_MOST);
        } else {
            //具体数值
            childWidthMeasureSpec = MeasureSpec.makeMeasureSpec (childLayoutParams.width, MeasureSpec.EXACTLY);
        }

        //高度同理
        if (childLayoutParams.height == LayoutParams.MATCH_PARENT) {
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec (heightSize, MeasureSpec.EXACTLY);
        } else if (childLayoutParams.height == LayoutParams.WRAP_CONTENT) {
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec (heightSize, MeasureSpec.AT_MOST);
        } else {
            childHeightMeasureSpec = MeasureSpec.makeMeasureSpec (childLayoutParams.height, MeasureSpec.EXACTLY);
        }
        //让系统测量
        child.measure (childWidthMeasureSpec, childHeightMeasureSpec); 

        int childWidth = child.getMeasuredWidth ();
        int childHeight = child.getMeasuredHeight ();
        //得到子View的总宽以及最高的子View高度
        totalWidth += childWidth;
        highest= highest < childHeight ? childHeight : highest;

    }
    //决定self是否可以滑动
    if(totalWidth < widthSize){
        //TODO 没占满,平分Tab
        mMaxScrollX = 0;  //控件最大可滚动的距离
        mSplitScrollX = 0; //根据item的个数,计算出每移动一个item控件需要移动的距离
    }else{
        // 占满甚至可能超出,可以滑动Tab
        mMaxScrollX = totalWidth-widthSize;
        mSplitScrollX = (int) (mMaxScrollX / (mTabCount  - 1.0f) + 0.5f);
    }

    //measure self
    if(widthMode == MeasureSpec.EXACTLY){
        mContentWidth = widthSize;
    }else {
        mContentWidth = totalWidth;
    }

    if(heightMode == MeasureSpec.EXACTLY){
        mContentHeight = heightSize;
    }else {
        mContentHeight = highest;
    }

    int measureWidth = mContentWidth + getPaddingLeft () + getPaddingRight ();
    int measureHeight = mContentHeight + getPaddingTop () + getPaddingBottom ();

    setMeasuredDimension (measureWidth, measureHeight);
}

布局,很简单只需要考虑子View的布局即可,依次排列

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if(changed){
        int height = b - t;//控件供子View显示的高度
        int left = l;
        for(int i = 0; i < mTabCount; i++) {
            View child = getChildAt (i);

            int top = (int) ((height - child.getMeasuredHeight ()) / 2.0f + 0.5f);
            int right = left = child.getMeasuredWidth ();
            child.layout (left,top,right,child.getMeasuredHeight ());
            left += right;
        }
    }
}

画出Indicator和Divider

@Override
protected void onDraw(Canvas canvas) {

    int height = getHeight ();
    //画指示器
    canvas.drawRect (mIndicatorLeft, height - mIndicatorHeight, mIndicatorWidth + mIndicatorLeft, height, mIndicatorPaint);
    //画分割线
    for(int i = 0; i < mTabCount - 1; i++) {
        final View child = getChildAt (i);
        canvas.drawLine (child.getRight (), mDividerPadding, child.getRight (), mContentHeight - mDividerPadding, mDividerPaint);
    }
    super.onDraw (canvas);
}

具体源码见: Gist