自定义ViewPager导航控件

项目中我们经常会使用viewpager+fragment,这时候就需要一个导航控件,本文介绍如何自定义一个导航控件。

先看效果图:

自定义ViewPager导航控件_第1张图片

先看xml布局文件:



    
    
    
    

布局文件就一个自定义的SimpleIndicator和一个ViewPager用来存放fragment。fragment的代码很简单,就不放了。

接下来看自定的的SimpleIndicator:

public class SimpleIndicator extends LinearLayout {

    private String[] titles = new String[]{"番茄","土豆","西红柿"};
    private Paint mPaint;
    private int mTabCount;
    private float mTranslationX = 0;
    private int position = 0;
    private IndicatorListener mListener;
    private SparseArray mList = new SparseArray<>(); //存放标题的TextView

    public SimpleIndicator(Context context) {
        this(context,null);
    }

    public SimpleIndicator(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(8);
    }

    //listener监听标题项的点击事件
    public void setListener(IndicatorListener listener){
        mListener = listener;
    }

    public void setTitles(String[] titles){
        this.titles = titles;
        mTabCount = titles.length;
        generateTitleView();
    }

    //添加标题项
    private void generateTitleView() {
        if(getChildCount()!=0)removeAllViews();

        for(int i=0;i

这里注意:
①View组件的绘制会调用draw方法,draw过程中主要是先画Drawable背景,对drawable调用setBounds然后是draw犯法。注意的是背景drawable的实际大小会影响view组件的大小,当背景比较大时view组件大小等于背景drawable的大小。

②调用invalidate方法时,draw过程会调用onDraw方法,然后就是dispatchDraw方法,dispatchDraw主要是分发给子组件进行绘制,我们通常定制组件的时候重写的是onDraw方法。但是ViewGroup容器组件的绘制,当他没有背景时直接调用的是dispatchDraw方法,而绕过了draw方法,当他有背景的时候就调用draw方法,而draw方法里包含了dispatchDraw方法的调用。因此要在ViewGroup上绘制东西的时候重写的往往是dispatchDraw方法而不是onDraw方法,或者自定制一个Drawable,重写它的draw和getIntrinsicWidth方法。

接下来看MainActivity,MainActivity是用kotlin写的:

class MainActivity : AppCompatActivity() {

    var string_array: Array = arrayOf("番茄","土豆","西红柿")

    var indicator: SimpleIndicator? = null
    var mViewpager: ViewPager? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        indicator = findViewById(R.id.ll_indicator)
        indicator?.setIndicatorListener(object: SimpleIndicator.IndicatorListener{
            override fun onClick(position: Int) {
                mViewpager?.currentItem = position
            }
        })
        indicator?.setTitles(string_array)

        mViewpager = findViewById(R.id.vp_fragment)
        var list: MutableList = mutableListOf()
        for(String in string_array){
            list.add(TestFragment())
        }
        var mAdapter: TabFragmentPagerAdapter = TabFragmentPagerAdapter(supportFragmentManager,list)
        mViewpager?.adapter = mAdapter
        mViewpager?.addOnPageChangeListener(object: ViewPager.OnPageChangeListener{
            override fun onPageScrollStateChanged(p0: Int) {

            }

            /**
             * 从第一项滚动到第二项时,滚动前position为0,滚动时position为0,offset由0增到1,滚动完成position为1
             * 从第二项滚动到第一项是,滚动前position为1,滚动时position为0,offset由1减到0,滚动完成position为0
             */
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                indicator?.vpScroll(position,positionOffset)
            }

            override fun onPageSelected(p0: Int) {

            }

        })
    }
}

这里注意:

ViewPager的OnPageChangeListener中的onPageScrolled方法的三个参数,看名字就知道,第一个参数是当前fragment的索引,第二个参数是滑动过程中的偏移量,第三个参数是偏移的像素值。

举个例子:这里有番茄、土豆、西红柿三个标题,也就是三个fragment,

①假设我们当前在第一项,还没开始滑动的时候,position是0,也就是第一项的索引,当我们从第一项(番茄)滑动到第二项(土豆)时,position不变,positionOffset从0增到1,滑动到第二项的时候,position变为1,positionOffset变为0

②假设我们当前在第二项,还没开始滑动的时候,position是1,也就是第二项的索引,当我们从第二项(土豆)滑动到第一项(番茄)时,position瞬间变为0,positionOffset从1减到0,滑动到第二项的时候,position为0,positionOffset变为0

单我们点击标题项时,回调到MainActivity的listener,这里调用ViewPager的setCurrentItem,setCurrentItem还是通过滑动的方法切换fragment,最终还是会调用onPageScrolled方法。

完整代码地址:https://github.com/wuxiaogui593/SimpleViewPagerIndicator

你可能感兴趣的:(android)