在日常开发中ViewPager加Fragment组合的方式非常常见,而在这种情况下通常会有一个指示器与之组合。这篇博客就来和大家一起实现ViewPager指示器。首先整理一下需求:

1.     首先是指示器整体是横向的,并且当宽度超出屏幕宽度时可以左右滑动,这里我们可以使用横向的ScrollView来实现。

2.     其次是我们的指示器是根据ViewPager的Adapter来确定个数的并且要获取到ViewPager的滑动状态,所以我们的指示器要拿到ViewPager和ViewPager的适配器的实例。

3.     再者就是当ViewPager滑动时我们的指示器和和ViewPager滑动同步的处理了

废话不说了,开始。(ps:表达能力有限( ▼-▼ ))

根据我们自定义View的步骤,第一步是自定义View属性了,在res/value下新建attrs文件并定义属性。

<?xmlversion="1.0"encoding="utf-8"?>

<resources>

<declare-styleablename="TabStrip">

<attrname="indicatorHeight"format="dimension"/>

<attrname="indicatorColor"format="color"/>

<attrname="indicatorMargin"format="dimension"/>

<attrname="indicatorTextColor"format="color"/>

<attrname="indicatorTextSize"format="dimension"/>

<attrname="selectedIndicatorTextSize"format="dimension"/>

</declare-styleable>

</resources>

接下来就是新建类并继承HorizontalScrollView并在构造方法中获取需要的自定义属性了。我们的整个类如下:

/**

 * ViewPager指示器

 *

 * @authorfxx

 *

 */

@SuppressLint("NewApi")

public class TabStrip extends HorizontalScrollView {

/**

     * 指示器容器

     */

    private LinearLayoutcontainer;

/**

     * 指示器个数

     */

    private int tabCount;

/**

     * 当前tab位置,默认为0

     */

    private int currentPosition

/**

     * 选中的tab位置

     */

    private int selectedPosition;

    private float currentPositionOffset

    private int lastScrollX

/**

     * LayoutParams用于添加指示器到指示器容器中时使用,按等权重分配指示器宽度

     */

    private LinearLayout.LayoutParamsexpandedTabLayoutParams;

/**

     * 指示器颜色

     */

    private int indicatorColor;

/**

     * 文字颜色

     */

    private int textColor;

/**

     * 文字大小

     */

    private int textSize;

/**

     * 选中位置的文字大小

     */

    private int selectedTextSize;

/**

     * 指示器高度

     */

    private int indicatorHeight;

/**

     * 指示器左右间距

     */

    private int indicatorMargin;

/**

     * ViewPager

     */

    private ViewPagerviewPager;

/**

     * viewpager的适配器

     */

    private PagerAdapterpagerAdapter;

 

/**

     * page改变监听器

     */

    private final PagerStateChangeListener pagerStateChangeListener =new PagerStateChangeListener();

/**

     * 画笔

     */

    private Paintpaint;

    private Contextcontext;

 

    public TabStrip(Contextcontext, AttributeSet attrs, int defStyleAttr,

           intdefStyleRes) {

       super(context,attrs, defStyleAttr,defStyleRes);

context, attrs, defStyleAttr, defStyleRes);

    }

 

    public TabStrip(Contextcontext, AttributeSet attrs, int defStyleAttr) {

       super(context,attrs, defStyleAttr);

context, attrs, defStyleAttr, 0);

    }

 

    public TabStrip(Contextcontext, AttributeSet attrs) {

       super(context,attrs);

context, attrs, 0, 0);

    }

 

    public TabStrip(Contextcontext) {

       super(context);

context, null, 0, 0);

    }

 

/**

     * 初始化

     *

     * @param context

     * @param attrs

     * @param defStyleAttr

     * @param defStyleRes

     */

    private void init(Context context, AttributeSetattrs, intdefStyleAttr,

           intdefStyleRes) {

       this.context =context;

// 取消横向的滚动条

       setHorizontalScrollBarEnabled(false);

// 指示器容器初始化

container = new LinearLayout(context);

container.setOrientation(LinearLayout.HORIZONTAL);

container.setLayoutParams(new LayoutParams(

              android.widget.LinearLayout.LayoutParams.MATCH_PARENT,

              android.widget.LinearLayout.LayoutParams.MATCH_PARENT));

// 添加指示器容器到scrollview

container);

 

// 获取屏幕相关信息

dm

 

typedArray =context.getTheme().obtainStyledAttributes(attrs,

              R.styleable.TabStrip,defStyleAttr, defStyleRes);

       int n = typedArray.getIndexCount();

       for (inti = 0; i < n; i++) {

           intattr = typedArray.getIndex(i);

           switch (attr) {

// 指示器颜色,默认黄色

           case R.styleable.TabStrip_indicatorColor:

indicatorColor =typedArray.getColor(attr, Color.YELLOW);

              break;

// 指示器高度,默认2

           case R.styleable.TabStrip_indicatorHeight:

indicatorHeight =typedArray.getDimensionPixelSize(attr, 2);

              break;

// 指示器左右间距,默认20

           case R.styleable.TabStrip_indicatorMargin:

indicatorMargin =typedArray.getDimensionPixelSize(attr, 20);

              break;

// 文字颜色,默认黑色

           case R.styleable.TabStrip_indicatorTextColor:

textColor = typedArray.getColor(attr, Color.BLACK);

              break;

// 文字大小,默认15

           case R.styleable.TabStrip_indicatorTextSize:

textSize = typedArray

attr,

                            (int) TypedValue.applyDimension(

                                   TypedValue.COMPLEX_UNIT_SP, 15,dm))

                     / 3;

              break;

// 选中项的文字大小,默认18

           case R.styleable.TabStrip_selectedIndicatorTextSize:

selectedTextSize =typedArray

attr,

                            (int) TypedValue.applyDimension(

                                   TypedValue.COMPLEX_UNIT_SP, 18,dm))

                     / 3;

              break;

           default:

              break;

           }

       }

// typedArray回收

typedArray.recycle();

 

expandedTabLayoutParams =new LinearLayout.LayoutParams(0,

              LayoutParams.MATCH_PARENT, 1.0f);

// 初始化画笔

paint = new Paint();

    }

 

@Override

    protected void onDraw(Canvas canvas) {

       super.onDraw(canvas);

// 如果指示器个数为0,直接结束绘画

       if (tabCount

           return;

       }

// 获取onMeasure后的高

       final int height

/*

        * 画指示器下方的线

        */

// 设置颜色

paint.setColor(indicatorColor);

// 当前指示tab位置

currentTab = container.getChildAt(currentPosition);

// 当前tab的左边相对父容器的左边距

       float leftPadding = currentTab.getLeft();

// 当前tab的右边相对于父容器左边距

       float rightPadding = currentTab.getRight();

// 如果出现位移

       if (currentPositionOffset > 0f &&currentPosition < tabCount

nextTab = container.getChildAt(currentPosition

           final float nextTabLeft = nextTab.getLeft();

           final float nextTabRight = nextTab.getRight();

leftPadding = (currentPositionOffset *nextTabLeft

currentPositionOffset) *leftPadding);

rightPadding = (currentPositionOffset *nextTabRight

currentPositionOffset) *rightPadding);

       }

// 绘制

canvas.drawRect(leftPadding,height - indicatorHeight,rightPadding,

height, paint);

 

    }

 

/**

     * 设置ViewPager

     *

     * @param viewPager

     */

    public void setViewPager(ViewPager viewPager) {

       this.viewPager =viewPager;

       if (viewPager.getAdapter() ==null) {

           throw new IllegalStateException(

"ViewPager does not has aadapter instance");

       } else {

pagerAdapter = viewPager.getAdapter();

       }

viewPager.addOnPageChangeListener(pagerStateChangeListener);

       update();

    }

 

/**

     * 更新界面

     */

    private void update() {

// 指示器容器移除所有子view

container.removeAllViews();

// 获取指示器个数

tabCount = pagerAdapter.getCount();

// 逐个添加指示器

       for (inti = 0; i < tabCount; i++) {

i, pagerAdapter.getPageTitle(i));

       }

// 更新Tab样式

       updateTabStyle();

       getViewTreeObserver()

              .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

 

@Override

                  publicvoid onGlobalLayout() {

                     getViewTreeObserver()

                            .removeOnGlobalLayoutListener(this);

currentPosition =viewPager.getCurrentItem();

currentPosition, 0);

                  }

              });

    }

 

/**

     * 滑动ScrollView

     *

     * @param position

     * @param offset

     */

    private void scrollToChild(intposition, intoffset) {

       if (tabCount

           return;

       }

       int newScrollX = container.getChildAt(position).getLeft() +offset;

       if (newScrollX !=lastScrollX) {

lastScrollX = newScrollX;

newScrollX, 0);

       }

    }

 

/**

     * 添加指示器

     *

     * @param position

     * @param title

     */

    private void addTab(final int position, CharSequence title) {

tvTab = new TextView(context);

tvTab.setText(title);

tvTab.setTextColor(textColor);

tvTab.setTextSize(textSize);

tvTab.setGravity(Gravity.CENTER);

tvTab.setSingleLine();

tvTab.setFocusable(true);

tvTab.setOnClickListener(new OnClickListener() {

 

@Override

           public void onClick(View v) {

viewPager.setCurrentItem(position);

           }

       });

tvTab.setPadding(indicatorMargin, 0,indicatorMargin, 0);

container.addView(tvTab,position, expandedTabLayoutParams);

    }

 

/**

     * 更新指示器样式

     */

    private void updateTabStyle() {

       for (inti = 0; i < tabCount; i++) {

tab = (TextView)container.getChildAt(i);

           if (i ==selectedPosition) {

// 设置选中的指示器文字颜色和大小

tab.setTextColor(indicatorColor);

tab.setTextSize(selectedTextSize);

           } else {

tab.setTextColor(textColor);

tab.setTextSize(textSize);

           }

       }

    }

 

/**

     * viewPager状态改变监听

     *

     * @authorfxx

     *

     */

    private class PagerStateChangeListener implements OnPageChangeListener {

 

@Override

       public void onPageScrollStateChanged(intstate) {

// 滑动状态为停止时

           if (state == ViewPager.SCROLL_STATE_IDLE) {

viewPager.getCurrentItem(), 0);

           }

       }

 

/**

        * 滚动时,只要处理指示器下方横线的滚动

        */

@Override

       public void onPageScrolled(intposition, floatpositionOffset,

              intpositionOffsetPixels) {

currentPosition = position;

currentPositionOffset =positionOffset;

// 处理指示器下方横线的滚动

position, (int) (positionOffset

container.getChildAt(position).getWidth()));

 

           invalidate();

       }

 

/**

        * page滚动结束

        */

@Override

       public void onPageSelected(intposition) {

// 滚动结束后的未知

selectedPosition = position;

// 更新指示器状态

           updateTabStyle();

       }

 

    }

}

至此,自定义ViewPager指示器的工作已经做完了,接下来就来验证一下,这里需要注意的是ViewPager的适配器需要重写getPageTitle()方法用于给指示器显示。在ViewPager完成适配器的设置后需要将ViewPager实例设置给我们的指示器,部分代码如下:

viewPager = (ViewPager) findViewById(R.id.viewpager);

tabStrip = (TabStrip) findViewById(R.id.tabstrip);

pageAdapter=newFragmentPageAdapter(getSupportFragmentManager());

viewPager.setAdapter(pageAdapter);

tabStrip.setViewPager(viewPager);

最后我们来看一下实现的效果如图:写的比较枯燥,感兴趣的童鞋下载源码看看吧

android RecyclerView指示器 viewpager指示器样式_android