模仿PagerSlidingTabStrip - ViewPag

作者: qingwenLi | 来源:发表于2015-12-31 14:38 被阅读685次

版权声明:本文为博主原创文章,未经博主允许不得转载。

自从上班后一直以来项目用到比较有难度的控件都上github搜,从来没想过自己写。
用多了别人的东西也觉得没什么意思能力也得不到提升,到了今年觉得一定要有自己的一套东西才行想法有啦就马上行动....
这次的准备使用最常用的控件 "ViewPager 滑动视图Tab " 而之前一直都使用 “PagerSlidingTabStrip开源控件 ” 这个比较有名效果也比较好,
今天呢就把它作为参考自己写一个 Sliding。

这里的先给出完整的控件代码
建议先看看 下面在单个方法讲解:

public class SlidingTab extends HorizontalScrollView {
    
    private int textColor = 0x00808080;
    private int textDefColor = 0x00808080;
    private float textPadding = 15;
    private float textSize = 10;
    private float lineHeight = 10;
    private int lineColor = 0x00808080;
    private LinearLayout mLayout;
    private ViewPager mPager;
    private final PageListener mPageListener = new PageListener();
    private Paint mPaint; 
    private int count ;
    private float offset = 52;
    private int lastScrollX;
    private int currentPosition;
    private float  currentPositionOffset;
    private OnTabPageChangeListener onTabPageChangeListener;
    
    
    public SlidingTab(Context context) {
        super(context);
    }
    public SlidingTab(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public SlidingTab(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }
    
    

    private  void init(AttributeSet attrs){
        DisplayMetrics dm = getResources().getDisplayMetrics();
        offset = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, offset,dm);
        lineHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, lineHeight,dm);
        textPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, textPadding,dm);
        textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, textSize,dm);
        
        
        
        TypedArray mTypedValue =  getContext().obtainStyledAttributes(attrs,R.styleable.SlingTab);
        textColor = mTypedValue.getColor(R.styleable.SlingTab_textColor, textColor);
        textDefColor  = mTypedValue.getColor(R.styleable.SlingTab_textDefColor, textDefColor);
        textPadding = mTypedValue.getDimension(R.styleable.SlingTab_textPadding, textPadding);
        textSize = mTypedValue.getDimension(R.styleable.SlingTab_textSize, textSize);
        lineHeight = mTypedValue.getDimension(R.styleable.SlingTab_lineHeight, lineHeight);
        lineColor  = mTypedValue.getColor(R.styleable.SlingTab_lineColor, lineColor);
        mTypedValue.recycle();
        mPaint = new Paint();
        mPaint.setStyle(Style.FILL);
        mPaint.setAntiAlias(true);
        setHorizontalScrollBarEnabled(false);
        mLayout = new LinearLayout(getContext());
        mLayout.setOrientation(LinearLayout.HORIZONTAL);
        mLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));    
        addView(mLayout);
    }
    public void setViewPager(ViewPager mPager){
        if(mPager == null){
            throw new NullPointerException("current ViewPager for empty");
        }
        this.mPager = mPager;
        mPager.addOnPageChangeListener(mPageListener);
        notifyDataSetChanged();     
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);   
        if(count < 0){
            return;
        }
        

        
         mPaint.setColor(lineColor);
         int height = getMeasuredHeight();
         float lineLeft = mLayout.getChildAt(currentPosition).getLeft() + textPadding;
         float lineRight = mLayout.getChildAt(currentPosition).getRight()- textPadding;
        if(currentPositionOffset > 0F && currentPosition < count){
             float Left = mLayout.getChildAt(currentPosition + 1).getLeft()  +  textPadding;
             float Right = mLayout.getChildAt(currentPosition + 1).getRight()-  textPadding;
             lineLeft = (currentPositionOffset * Left + (1F - currentPositionOffset) * lineLeft);
             lineRight = (currentPositionOffset * Right + (1F - currentPositionOffset) * lineRight);
        }
        canvas.drawRect(lineLeft , height - lineHeight, lineRight, height, mPaint);
    }
    
    
    
    private void notifyDataSetChanged() {
        count =  mPager.getAdapter().getCount();
        for (int i = 0; i < count; i++) {
            mLayout.addView(addTab(i, mPager.getAdapter().getPageTitle(i).toString()));
        }
        scrollToChild(0, 0);
    }
    
    
    
    private TextView addTab(final int position, String title) {
        TextView mTextView = new TextView(getContext());
        mTextView.setId(position);
        mTextView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
        mTextView.setGravity(Gravity.CENTER);
        mTextView.setText(title);
        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,textSize);
        mTextView.setTextColor(textDefColor);
        mTextView.setPadding((int)textPadding, 0, (int)textPadding, 0);
        mTextView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(position);
            }
        });
        return mTextView;
    }
    
    
    
    
    
    void scrollToChild(int position , int positionOffset){
        if(count < 0 ){
            return;
        }
        int newPositionOffset  = mLayout.getChildAt(position).getLeft() + positionOffset;
        if(position > 0 || positionOffset > 0 ){
            newPositionOffset -= offset;
        }
        if(newPositionOffset != lastScrollX){
            lastScrollX = newPositionOffset;
            scrollTo(newPositionOffset, 0);
        }           
    }
    
    
    
    public  interface OnTabPageChangeListener{
        void onPageScrollStateChanged(int state);
        void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
        void onPageSelected(int position);
    } 

    
    class PageListener implements OnPageChangeListener{
    
        @Override
        public void onPageScrollStateChanged(int state) {
            if(onTabPageChangeListener != null){
                onTabPageChangeListener.onPageScrollStateChanged(state);
            }   
        }
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            SlidingTab.this.currentPosition = position;
            SlidingTab.this.currentPositionOffset = positionOffset;
            scrollToChild(position, (int) (positionOffset * mLayout.getChildAt(position).getWidth()) );
            invalidate();
            if(onTabPageChangeListener != null){
                onTabPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
            }
        }
         
        @Override
        public void onPageSelected(int position) {

            if(onTabPageChangeListener != null){
                onTabPageChangeListener.onPageSelected(position );
            }
        
        }
        
    }
    public int getTextColor() {
        return textColor;
    }
    public void setTextColor(int textColor) {
        this.textColor = textColor;
        invalidate();
    }
    public int getTextDefColor() {
        return textDefColor;
    }
    public void setTextDefColor(int textDefColor) {
        this.textDefColor = textDefColor;
        invalidate();
    }
    public float getTextPadding() {
        return textPadding;
    }
    public void setTextPadding(float textPadding) {
        this.textPadding = textPadding;
        invalidate();
    }
    public float getTextSize() {
        return textSize;
    }
    public void setTextSize(float textSize) {
        this.textSize = textSize;
        invalidate();
    }
    public float getLineHeight() {
        return lineHeight;
    }
    public void setLineHeight(float lineHeight) {
        this.lineHeight = lineHeight;
        invalidate();
    }
    public int getLineColor() {
        return lineColor;
    }
    public void setLineColor(int lineColor) {
        this.lineColor = lineColor;
        invalidate();
    }
    public OnTabPageChangeListener getOnTabPageChangeListener() {
        return onTabPageChangeListener;
    }
    public void setOnTabPageChangeListener(
            OnTabPageChangeListener onTabPageChangeListener) {
        this.onTabPageChangeListener = onTabPageChangeListener;
    }
}

要写控件首先要想的是这控件具备什么属性。
而我们这个控件的属性是:
ViewPager 每页面对应一个标题,标题就是 Text ,Text有颜色大小
ViewPager 页面对应的标识以线为基础。

      //标题颜色  : 这里是为了后期的扩展 
    private int textColor = 0x00808080;
     //默认标题颜色
    private int textDefColor = 0x00808080;
     // 每个标题的边距
    private float textPadding = 15;
     // 标题字体大小
    private float textSize = 10;
    // 页面标识 线的高度
    private float lineHeight = 10;

当我们把控件具备的属性都了解清楚啦,并且都写出来啦,这个时候为了方便就需要自定义当前控件的标签属性,在res底下的values文件夹中创建一个attr.xml内容如下

<?xml version="1.0" encoding="utf-8"?>
<resources>
        <declare-styleable name="SlingTab">
               <attr name="textColor" format="color" />
               <attr name="textDefColor" format="color" />  
               <attr name="textPadding" format="dimension" />   
               <attr name="textSize" format="dimension" />  
               <attr name="lineHeight" format="dimension" />    
               <attr name="lineColor" format="color" /> 
        </declare-styleable>
</resources>

xml创建好啦,这个时候我们就要去引用这些属性啦让它可以成为标签的属性

        
    public SlidingTab(Context context) {
        super(context);
    }
    public SlidingTab(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public SlidingTab(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }
    
        private  void init(AttributeSet attrs){
        TypedArray mTypedValue =   getContext().obtainStyledAttributes(attrs,R.styleable.SlingTab);
        textColor =    mTypedValue.getColor(R.styleable.SlingTab_textColor, textColor);
        textDefColor  = mTypedValue.getColor(R.styleable.SlingTab_textDefColor, textDefColor);
        textPadding = mTypedValue.getDimension(R.styleable.SlingTab_textPadding, textPadding);
        textSize = mTypedValue.getDimension(R.styleable.SlingTab_textSize, textSize);
        lineHeight = mTypedValue.getDimension(R.styleable.SlingTab_lineHeight, lineHeight);
        lineColor  = mTypedValue.getColor(R.styleable.SlingTab_lineColor, lineColor);
        mTypedValue.recycle();
    }

接下来就创建线的画笔,而当前控件是继承至 HorizontalScrollView只能有一个子View那么就将Linearlayout作为子布局。


mPaint = new Paint(); 
mPaint.setStyle(Style.FILL); 
mPaint.setAntiAlias(true); 
setHorizontalScrollBarEnabled(false); 
mLayout = new LinearLayout(getContext()); 
mLayout.setOrientation(LinearLayout.HORIZONTAL); 
mLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 
addView(mLayout);

接着传入进来的ViewPager通过ViewPager获取标题名称以及标题个数然后创建TextView,设置ViewPager 滑动监听为了实现后期滑动的标示偏移值以及TextView实现点击事件。


    
    public void setViewPager(ViewPager mPager){
        if(mPager == null){
            throw new NullPointerException("current ViewPager for empty");
        }
        this.mPager = mPager;
        mPager.addOnPageChangeListener(mPageListener);
        notifyDataSetChanged();     
    }
    
    
    private void notifyDataSetChanged() {
        count =  mPager.getAdapter().getCount();
        for (int i = 0; i < count; i++) {
            mLayout.addView(addTab(i, mPager.getAdapter().getPageTitle(i).toString()));
        }
        scrollToChild(0, 0);
    }
    
    
    
    private TextView addTab(final int position, String title) {
        TextView mTextView = new TextView(getContext());
        mTextView.setId(position);
        mTextView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
        mTextView.setGravity(Gravity.CENTER);
        mTextView.setText(title);
        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,textSize);
        mTextView.setTextColor(textDefColor);
        mTextView.setPadding((int)textPadding, 0, (int)textPadding, 0);
        mTextView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mPager.setCurrentItem(position);
            }
        });
        return mTextView;
    }

到这里基本的添加都做好啦,但是标示的移动以及标题超出页面后需要滑动对应每个标题还没做,先来看看滑动怎么做,这里就需要使用ViewPager滑动监听接口中的onPagerScrolled方法,方法中的参数为:
position:滑动时,屏幕左侧显示的第一个page
positionOffset:滑动比例,值的范围为[0, 1),手指往左滑动,该值递增,反之递减
positionOffsetPixels:滑动距离,和屏幕有关,手指往左滑动,该值递增,反之递减

我们将当前position 以及 position 乘以每个标题的宽度传入scrollToChild方法中,再拿到当前position标题的左边X轴位置 + Positionoffset ,加上if语句如果当前的 position大于0或者 Positionoffset 大于0
就 -= 我们设置的偏移量 再加上if语句 如果当前 newPositionOffset 不等于lastScrollX 就将 lastScrollX = newPositionOffset;
scrollTo方法进行移动。

“注:该方法scrollTo(x,y)正数往左滑动,负数往右滑动与我们所认识的滑动是发向的 详情请看API”

    
    class PageListener implements OnPageChangeListener{
    
        @Override
        public void onPageScrollStateChanged(int state) {
            if(onTabPageChangeListener != null){
                onTabPageChangeListener.onPageScrollStateChanged(state);
            }   
        }
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            SlidingTab.this.currentPosition = position;
            SlidingTab.this.currentPositionOffset = positionOffset;
            scrollToChild(position, (int) (positionOffset * mLayout.getChildAt(position).getWidth()) );
            invalidate();
            if(onTabPageChangeListener != null){
                onTabPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
            }
        }
         
        @Override
        public void onPageSelected(int position) {

            if(onTabPageChangeListener != null){
                onTabPageChangeListener.onPageSelected(position );
            }
        
        }


    
    void scrollToChild(int position , int positionOffset){
        if(count < 0 ){
            return;
        }
        int newPositionOffset  = mLayout.getChildAt(position).getLeft() + positionOffset;
        if(position > 0 || positionOffset > 0 ){
            newPositionOffset -= offset;
        }
        if(newPositionOffset != lastScrollX){
            lastScrollX = newPositionOffset;
            scrollTo(newPositionOffset, 0);
        }
                
    }

还有最后一个方法画标示线,获取测量后的View的高度,根据currentPosition 获取标题左左边以及右边左边。
判断当前currentPositionOffset 是否大于0并且当前currentPosition 小于标题个数,通过条件 根据currentPosition+1 获取标题左左边以及右边左边做下计算:


偏移值  * 第二个标题左边X坐标  + (1.0 - 偏移值)  *   第一个标题的左边X坐标;
ViewPager滑动过程会不停调用
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
 方法 偏移值就会不停的变 通过invalidate();
 去更新图层 

,将数据画在距形上就搞定啦。

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);   
        if(count < 0){
            return;
        }
         mPaint.setColor(lineColor);
         int height = getMeasuredHeight();
         float lineLeft = mLayout.getChildAt(currentPosition).getLeft() + textPadding;
         float lineRight = mLayout.getChildAt(currentPosition).getRight()- textPadding;
        if(currentPositionOffset > 0F && currentPosition < count){
             float Left = mLayout.getChildAt(currentPosition + 1).getLeft()  +  textPadding;
             float Right = mLayout.getChildAt(currentPosition + 1).getRight()-  textPadding;
             lineLeft = (currentPositionOffset * Left + (1F - currentPositionOffset) * lineLeft);
             lineRight = (currentPositionOffset * Right + (1F - currentPositionOffset) * lineRight);
        }
        canvas.drawRect(lineLeft , height - lineHeight, lineRight, height, mPaint);
    }
截图.jpg 最终效果图.gif YHPKD~TKEX26L7CA1%8Z12E.jpg

如果看不太明白请原谅,第二次写博客不是表达的很好,
需要源码 留下邮箱~

相关文章

网友评论

  • 74646969fbbf:求源码,邮箱401829206@qq.com 多谢
  • 0f421b74ad9f:求源码,邮箱422742446@qq.com
  • 228e76bb7bda:求源码,邮箱2224344971@qq.com
  • 朝花夕拾不起来:这个官方文档不是有例子么?
    qingwenLi:@朝花夕拾不起来 不一样啦,这个我自己写的
  • 洋Coder:onDraw方法中的if(count<0)应该为if(count<=0)吧?
    qingwenLi:@洋Coder 最近又开始忙,没时间写啦。
    洋Coder:@A其实我很傻 刚好项目要用,又懒得写,直接拿来用了
    qingwenLi:@洋Coder 哈哈,<=0比较严谨些
  • Believe_you:默默的关注你

本文标题:模仿PagerSlidingTabStrip - ViewPag

本文链接:https://www.haomeiwen.com/subject/skbihttx.html