Android 自定义View实现ViewPager指示器

来源:互联网 发布:sql server在哪下载 编辑:程序博客网 时间:2024/05/01 13:51

在日常开发中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 = 0;

    /**

     * 选中的tab位置

     */

    private int selectedPosition;

    private float currentPositionOffset = 0f;

    private int lastScrollX = 0;

    /**

     * 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, AttributeSetattrs, intdefStyleAttr,

           intdefStyleRes) {

       super(context,attrs,defStyleAttr,defStyleRes);

       init(context, attrs, defStyleAttr, defStyleRes);

    }

 

    public TabStrip(Contextcontext, AttributeSetattrs, intdefStyleAttr) {

       super(context,attrs,defStyleAttr);

       init(context, attrs, defStyleAttr, 0);

    }

 

    public TabStrip(Contextcontext, AttributeSetattrs) {

       super(context,attrs);

       init(context, attrs, 0, 0);

    }

 

    public TabStrip(Contextcontext) {

       super(context);

       init(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

       addView(container);

 

       // 获取屏幕相关信息

       DisplayMetrics dm =getResources().getDisplayMetrics();

 

       TypedArray 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

                     .getDimensionPixelSize(attr,

                            (int) TypedValue.applyDimension(

                                   TypedValue.COMPLEX_UNIT_SP, 15,dm))

                     / 3;

              break;

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

           case R.styleable.TabStrip_selectedIndicatorTextSize:

              selectedTextSize =typedArray

                     .getDimensionPixelSize(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 == 0) {

           return;

       }

       // 获取onMeasure后的高

       final int height = getHeight();

       /*

        * 画指示器下方的线

        */

       // 设置颜色

       paint.setColor(indicatorColor);

       // 当前指示tab位置

       View currentTab = container.getChildAt(currentPosition);

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

       float leftPadding = currentTab.getLeft();

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

       float rightPadding = currentTab.getRight();

       // 如果出现位移

       if (currentPositionOffset > 0f &&currentPosition <tabCount - 1) {

           View nextTab = container.getChildAt(currentPosition + 1);

           final float nextTabLeft = nextTab.getLeft();

           final float nextTabRight = nextTab.getRight();

           leftPadding = (currentPositionOffset *nextTabLeft

                  + (1f - currentPositionOffset) *leftPadding);

           rightPadding = (currentPositionOffset *nextTabRight

                  + (1f - 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++) {

           addTab(i, pagerAdapter.getPageTitle(i));

       }

       // 更新Tab样式

       updateTabStyle();

       getViewTreeObserver()

              .addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

 

                  @Override

                  publicvoid onGlobalLayout() {

                     getViewTreeObserver()

                            .removeOnGlobalLayoutListener(this);

                     currentPosition =viewPager.getCurrentItem();

                     scrollToChild(currentPosition, 0);

                  }

              });

    }

 

    /**

     * 滑动ScrollView

     *

     * @param position

     * @param offset

     */

    private void scrollToChild(intposition,intoffset) {

       if (tabCount == 0) {

           return;

       }

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

       if (newScrollX !=lastScrollX) {

           lastScrollX = newScrollX;

           scrollTo(newScrollX, 0);

       }

    }

 

    /**

     * 添加指示器

     *

     * @param position

     * @param title

     */

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

       TextView 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++) {

           TextView 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) {

              scrollToChild(viewPager.getCurrentItem(), 0);

           }

       }

 

       /**

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

        */

       @Override

       public void onPageScrolled(intposition,floatpositionOffset,

              intpositionOffsetPixels) {

           currentPosition = position;

           currentPositionOffset =positionOffset;

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

           scrollToChild(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);

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


源码地址:http://download.csdn.net/detail/u011282703/9582685

1 0
原创粉丝点击