Android ViewPager + Fragment 自定义滑动标签

本文介绍了如何使用Fragment、ViewPager和HorizontalScrollView创建一个简单的分类导航页。通过自定义View,实现滑动标签的功能,包括在ScrollView中添加TextView作为TabIndicator,并实现了线的动态移动效果。提供了一个GitHub上的Demo链接供参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


Fragment+ViewPager+HorizontalScrollView

简单的分类导航页实现

效果图:


下面的是自定义的View

public class TabPageIndicator extends HorizontalScrollView implements ViewPager.OnPageChangeListener {

    /**
     * attrs
     */
    private int mTabPaddingLeft=16;
    private int mTabPaddingRight=16;

    private int mTextColor= Color.BLACK;
    private int mSelectTextColor=Color.RED;
    private int mTextSize=10;

    private int mCurrentPosition;
    private int mSelectPosition;


    private int mTabCount;

    private float mOffset;

    private Paint mSelectLinePaint;
    private int mLineColor=Color.RED;
    private int mLineHeight=4;


    /**
     * Horizontal LinearLayout;
     */
    private LinearLayout mLinearLayout;

    private ViewPager mPager;

    private ViewPager.OnPageChangeListener mListener;

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

    public TabPageIndicator(Context context, AttributeSet attrs) {
        this(context,attrs,0);
    }

    public TabPageIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setFillViewport(true);//子控件不满全屏时,设置ScrollView 全屏
        setWillNotDraw(false);//取消重绘的Tag ?
        initView(context);

        mSelectLinePaint=new Paint();
    }

    private void initView(Context context) {
        mLinearLayout=new LinearLayout(context);
        mLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
        mLinearLayout.setLayoutParams(new ViewGroup.LayoutParams
                (ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
        mLinearLayout.setGravity(Gravity.CENTER_VERTICAL);

        addView(mLinearLayout);

    }


    /**
     * Indicator 添加ViewPager 关联
     * @param pager
     */
    public void setViewPager(ViewPager pager){
        this.mPager=pager;

        if (mPager!=null)
            mPager.setOnPageChangeListener(this);

        notifyDataSetChange();

    }

    private void notifyDataSetChange() {
        mLinearLayout.removeAllViews();
        PagerAdapter adapter = mPager.getAdapter();
        mTabCount = adapter.getCount();

        for (int i=0;i<mTabCount;i++){
            CharSequence pageTitle = adapter.getPageTitle(i);
            addTab(pageTitle,i);
        }

        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                    getViewTreeObserver().removeGlobalOnLayoutListener(this);
                } else {
                    getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }

                mCurrentPosition = mPager.getCurrentItem();
                mOffset=0;
                scrollToChild();
            }
        });

    }

    private void addTab(CharSequence pageTitle, final int position) {
        TextView tabView=new TextView(getContext());
        if (position==mSelectPosition){
            tabView.setTextColor(mSelectTextColor);
        }else {
            tabView.setTextColor(mTextColor);
        }

        tabView.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);
        tabView.setText(pageTitle);
        tabView.setPadding(mTabPaddingLeft,0,mTabPaddingRight,0);

        mLinearLayout.addView(tabView,position);

        tabView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mPager.setCurrentItem(position);
                updateTextSelectState(position);
            }
        });

    }


    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (mListener!=null){
            mListener.onPageScrolled(position,positionOffset,positionOffsetPixels);
        }
        mCurrentPosition=position;
        mOffset=positionOffset;

        /**
         * 下方的两个方法注意顺序
         */

        scrollToChild();
        invalidate();


    }

    @Override
    public void onPageSelected(int position) {
        if (mListener!=null){
            mListener.onPageSelected(position);
        }

        updateTextSelectState(position);

    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (mListener!=null){
            mListener.onPageScrollStateChanged(state);
        }
    }

    private void scrollToChild() {
        int measuredWidth = this.getMeasuredWidth();
        View tabView = mLinearLayout.getChildAt(mCurrentPosition);
        int width = tabView.getWidth();
        float offset = width * mOffset;
        int left = tabView.getLeft();
        int scrollWitch= (int) (left-measuredWidth/2 + offset);
        smoothScrollTo(scrollWitch,0);


    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mTabCount==0){
            return;
        }
        mSelectLinePaint.setAntiAlias(true);
        mSelectLinePaint.setColor(mLineColor);


        View currentTab = mLinearLayout.getChildAt(mCurrentPosition);
        float lineLeft=currentTab.getLeft();
        float lineRight=currentTab.getRight();

        if (mOffset>0f && mCurrentPosition<mTabCount-1){
            View nextTab = mLinearLayout.getChildAt(mCurrentPosition + 1);
//            lineLeft=nextTab.getLeft()*mOffset-(1f-mOffset)*currentTab.getLeft();
//            lintRight=nextTab.getRight()*mOffset-(1f-mOffset)*currentTab.getRight();

            final float nextTabLeft = nextTab.getLeft();
            final float nextTabRight = nextTab.getRight();

            lineLeft = (mOffset * nextTabLeft + (1f - mOffset) * lineLeft);
            lineRight = (mOffset * nextTabRight + (1f - mOffset) * lineRight);
        }

        canvas.drawRect(lineLeft,getMeasuredHeight()-mLineHeight,lineRight,getMeasuredHeight(),mSelectLinePaint);

    }

    public void updateTextSelectState(int position){
        if (mTabCount==0){
            return;
        }

        TextView lastSelectView = (TextView) mLinearLayout.getChildAt(mSelectPosition);
        lastSelectView.setTextColor(mTextColor);
        TextView currentSelectView = (TextView) mLinearLayout.getChildAt(position);
        currentSelectView.setTextColor(mSelectTextColor);
        mSelectPosition=position;

    }

    public void setTextSize(int textSize){
        this.mTextSize=textSize;
    }

    public void setTabPadding(int tabPadding){
        this.mTabPaddingLeft=tabPadding;
        this.mTabPaddingRight=tabPadding;
    }


}

首先是添加TabIndicator和ViewPager的关联,新建TextView添加到ScrollView的子View 水平线性布局里面,并设置TextView的样式

 /**
     * Indicator 添加ViewPager 关联
     * @param pager
     */
    public void setViewPager(ViewPager pager){
        this.mPager=pager;

        if (mPager!=null)
            mPager.setOnPageChangeListener(this);

        notifyDataSetChange();

    }

    /**
     * 新建Tab加入线性布局里面
     */
    private void notifyDataSetChange() {
        mLinearLayout.removeAllViews();
        PagerAdapter adapter = mPager.getAdapter();
        mTabCount = adapter.getCount();

        for (int i=0;i<mTabCount;i++){
            CharSequence pageTitle = adapter.getPageTitle(i);
            addTab(pageTitle,i);
        }

        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                    getViewTreeObserver().removeGlobalOnLayoutListener(this);
                } else {
                    getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }

                mCurrentPosition = mPager.getCurrentItem();
                mOffset=0;
                scrollToChild();
            }
        });

    }

    private void addTab(CharSequence pageTitle, final int position) {
        TextView tabView=new TextView(getContext());
        if (position==mSelectPosition){
            tabView.setTextColor(mSelectTextColor);
        }else {
            tabView.setTextColor(mTextColor);
        }

        tabView.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);
        tabView.setText(pageTitle);
        tabView.setPadding(mTabPaddingLeft,0,mTabPaddingRight,0);

        mLinearLayout.addView(tabView,position);

        tabView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                mPager.setCurrentItem(position);
                updateTextSelectState(position);
            }
        });

    }


重写Viewpager的滚动事件,同时tab 超过中间位置时ScrollView也一起滚蛋

invalidate() 不断重绘,画下面的那条线,看着那根线也一起移动

<pre name="code" class="java">@Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (mListener!=null){
            mListener.onPageScrolled(position,positionOffset,positionOffsetPixels);
        }
        mCurrentPosition=position;
        mOffset=positionOffset;

        /**
         * 下方的两个方法注意顺序
         */

        scrollToChild();
        invalidate();


    }

    @Override
    public void onPageSelected(int position) {
        if (mListener!=null){
            mListener.onPageSelected(position);
        }

        updateTextSelectState(position);

    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (mListener!=null){
            mListener.onPageScrollStateChanged(state);
        }
    }

ScrollView 滚动

 private void scrollToChild() {
        int measuredWidth = this.getMeasuredWidth();
        View tabView = mLinearLayout.getChildAt(mCurrentPosition);
        int width = tabView.getWidth();
        float offset = width * mOffset;
        int left = tabView.getLeft();
        int scrollWitch= (int) (left-measuredWidth/2 + offset);
        smoothScrollTo(scrollWitch,0);


    }

画下面那条线

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (mTabCount==0){
            return;
        }
        mSelectLinePaint.setAntiAlias(true);
        mSelectLinePaint.setColor(mLineColor);


        View currentTab = mLinearLayout.getChildAt(mCurrentPosition);
        float lineLeft=currentTab.getLeft();
        float lineRight=currentTab.getRight();

        if (mOffset>0f && mCurrentPosition<mTabCount-1){
            View nextTab = mLinearLayout.getChildAt(mCurrentPosition + 1);
//            lineLeft=nextTab.getLeft()*mOffset-(1f-mOffset)*currentTab.getLeft();
//            lintRight=nextTab.getRight()*mOffset-(1f-mOffset)*currentTab.getRight();

            final float nextTabLeft = nextTab.getLeft();
            final float nextTabRight = nextTab.getRight();

            lineLeft = (mOffset * nextTabLeft + (1f - mOffset) * lineLeft);
            lineRight = (mOffset * nextTabRight + (1f - mOffset) * lineRight);
        }

        canvas.drawRect(lineLeft,getMeasuredHeight()-mLineHeight,lineRight,getMeasuredHeight(),mSelectLinePaint);

    }

Activity 里面的很简单,就不粘贴了,直接看Demo吧!

Demo地址:https://github.com/limushan/TabPageIndicator

https://github.com/astuetz/PagerSlidingTabStrip 这是一个大神写的Demo 借鉴了里面的思路并且精简了一下,一般我们可能用不到他写的这么多功能,





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值