Android 自定义view-Indicator样式,TabIndicatorLaout

先上个效果图:

白色三角指示器

废话一下

目前很多界面都是这种TabLayout+ViewPager的组合界面,TabLayout自带的指示器样式,似乎有点不够用。我个人并不喜欢写很多style的xml设置给TabLayout。我想的是指示器要白色三角形,就直接写个ImageView,给一张白色三角形的图片就行了。指示器需要什么样子就在布局文件中写成什么样子,这样会让我比较爽。所以就写了个这玩意。把原来的TabLayout+ViewPger变成了TabLayout+TabIndicatorLayout+ViewPager.

 

布局代码

在布局中找到你需要放Indicator指示器的位置,用TabIndicatorLayout这个控件,包裹你的指示器就行了,比如我这里的指示器是一个白色三角形,TriangleView就是白色三角形,这个自己写就行了。如果你的指示器是其他的什么,一样放这里就行了,比如ImageView,设置为一张白色三角形的图片。。或者你的指示器是一个复杂的布局(基本不存在),你可以在这里直接写你的布局,比如一个LinearLayout,包裹你要显示的Indicator的内容。

    <!--TabLayout,不解释-->
    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="55dp"
        android:background="@color/colorPrimary"
        app:tabIndicatorHeight="0dp"
        app:tabMode="scrollable"
        app:tabTextColor="#ffffff">

    </android.support.design.widget.TabLayout>

    <!--TabIndicatorLayout,指示器的容器,将你需要的指示器布局,写在这个容器中就行了-->
    <com.ts_xiaoa.tsxiaoa_lib.widget.TabIndicatorLayout
        android:id="@+id/tab_indicator_layout"
        android:layout_width="match_parent"
        android:layout_height="10dp"
        android:background="@color/colorPrimary">

        <!--白色三角形,这里可以放任意其他布局,比如ImageView...-->
        <com.ts_xiaoa.tsxiaoa_lib.widget.TriangleView
            android:layout_width="20dp"
            android:layout_height="10dp" />

    </com.ts_xiaoa.tsxiaoa_lib.widget.TabIndicatorLayout>

    <!--ViewPager,不解释-->
    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </android.support.v4.view.ViewPager>

java代码

在代码中需要为TabIndicator绑定tabLayout和ViewPager。看下面代码中的最后一句,最后一句,最后一句。

        //获取控件
        TabLayout tabLayout = findViewById(R.id.tab_layout);
        ViewPager viewPager = findViewById(R.id.view_pager);
        TabIndicatorLayout tabIndicatorLayout = findViewById(R.id.tab_indicator_layout);
        //设置viewpager的适配器
        viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int i) {
                return new MyFragment();
            }

            @Override
            public int getCount() {
                return 0;
            }

            @Nullable
            @Override
            public CharSequence getPageTitle(int position) {
                return position % 3 == 0 ? "我的标题" : "我的比较长的标题";
            }
        });
        //绑定TabLayout,ViewPager
        tabLayout.setupWithViewPager(viewPager);
        //绑定TabIndicatorLayout,TabLayout,ViewPager
        tabIndicatorLayout.setupWithViewPager(tabLayout, viewPager);

 

实现步骤

这其实没什么可说的,就是监听viewpager的滑动,根据当前TabItem的位置,移动Indicator的位置。逻辑也不复杂。具体的逻辑都在代码中了,改写的注释都写了。没有自定义属性什么的,所以就这一个类。代码不多,就几行,全部都贴出来了。关键代码是71-75行代码和86行-125行,也就是两个监听滑动的方法。

/**
 * create by ts_xiaoa
 * create Time 2018/9/14
 * description:
 */
public class TabIndicatorLayout extends FrameLayout implements ViewTreeObserver.OnScrollChangedListener, ViewPager.OnPageChangeListener {

    //需要显示的指示器
    private View indicatorView;
    //绑定的viewpager和tabLayout
    private TabLayout tabLayout;
    private ViewPager viewPager;
    //tabItem的父容器
    private ViewGroup tabParentLayout;
    //记录Indicator的宽度是否为MATCH_PARENT
    private boolean isMatchParent;
    private boolean isPagerScroll;

    public TabIndicatorLayout(@NonNull Context context) {
        super(context);
    }

    public TabIndicatorLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public TabIndicatorLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (getChildCount() > 1) {
            throw new IllegalArgumentException("只能有一个indicator布局");
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        //得到指示器indicator的视图
        indicatorView = getChildAt(0);
        //判断是否需要Indicator宽度全充满
        switch (indicatorView.getLayoutParams().width) {
            case LayoutParams.MATCH_PARENT:
                isMatchParent = true;//记录是否需要全充满
                break;
            default:
                isMatchParent = false;//记录是否需要全充满
                break;
        }
    }

    public void setupWithViewPager(TabLayout tabLayout, ViewPager viewPager) {
        this.tabLayout = tabLayout;
        this.viewPager = viewPager;
        this.tabLayout.getViewTreeObserver().addOnScrollChangedListener(this);
        this.viewPager.addOnPageChangeListener(this);
        //得到TabItem的父布局
        this.tabParentLayout = (ViewGroup) tabLayout.getChildAt(0);
    }


    /**
     * 监听tabLayout的滑动
     */
    @Override
    public void onScrollChanged() {
        //如果用户只拖动TabLayout,让Indicator的位置跟着TabItem一起动
        if (!isPagerScroll) {
            int tabRelationX = (int) tabParentLayout.getChildAt(viewPager.getCurrentItem()).getX() - tabLayout.getScrollX();
            int tabWidth = tabParentLayout.getChildAt(viewPager.getCurrentItem()).getMeasuredWidth();
            indicatorView.setX(tabRelationX + tabWidth / 2 - indicatorView.getMeasuredWidth() / 2);
        }
    }

    /**
     * 监听viewPager的滑动
     *
     * @param position
     * @param positionOffset
     * @param positionOffsetPixels
     */
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        isPagerScroll = true;
        //判断当切换完成到最后一个pager时
        if (position == tabParentLayout.getChildCount() - 1) {
            //return,下面107行位置tabParentLayout.getChildAt(position + 1).getMeasuredWidth()会报NullPoint
            return;
        }

        /* *******************************indicator移动部分*********************************** */
        //得到当前TabItem的X坐标
        int currTabItemX = (int) tabParentLayout.getChildAt(position).getX();
        //得到当前TabItem的宽度
        int currTabItemWidth = tabParentLayout.getChildAt(position).getMeasuredWidth();
        //计算出当前Indicator的X 注:没有滑动时indicator的位置X
        int indicatorX = currTabItemX + currTabItemWidth / 2 - indicatorView.getMeasuredWidth() / 2;
        //得到indicator移动到下一个pager的总距离:(当前TabItem的宽度+下一个TabItem的宽度)/2
        int distance = (tabParentLayout.getChildAt(position + 1).getMeasuredWidth() + currTabItemWidth) / 2;
        //计算滑动viewpager时的偏移量:偏移比例 * 滑动到下一个pager的总距离
        int offset = (int) (positionOffset * (distance));
        //得到TabLayout滑动的X
        int tabScrollX = tabLayout.getScrollX();
        //计算得出滑动时indicator的X:原来的X+滑动的偏移量 - TabLayout滑动的X
        indicatorX += offset - tabScrollX;
        //重新设置当前indicator的X
        indicatorView.setX(indicatorX);
        /* *******************************indicator移动部分*********************************** */


        /* *******************************indicator宽度变化部分*********************************** */
        //如果indicator的宽度设置为MATCH_PARENT,每个TabItem的宽度不一样
        if (isMatchParent) {
            int leftWidth = tabParentLayout.getChildAt(position).getMeasuredWidth();
            int rightWidth = tabParentLayout.getChildAt(position + 1).getMeasuredWidth();
            int indicatorWidth = (int) (leftWidth + (rightWidth - leftWidth) * positionOffset);
            ViewGroup.LayoutParams layoutParams = indicatorView.getLayoutParams();
            layoutParams.width = indicatorWidth;
            indicatorView.setLayoutParams(layoutParams);
        }
        /* *******************************indicator宽度变化部分*********************************** */
    }

    @Override
    public void onPageSelected(int i) {
        isPagerScroll = false;
    }

    @Override
    public void onPageScrollStateChanged(int i) {
        /**
         * ViewPager.SCROLL_STATE_IDLE;值为0 表示空闲
         * ViewPager.SCROLL_STATE_DRAGGING;值为1 表示拖动viewPager
         * ViewPager.SCROLL_STATE_SETTLING;值为2 表示停止
         */

        //记录viewpager是否正在滑动
        if (i != ViewPager.SCROLL_STATE_DRAGGING) {
            isPagerScroll = false;
        }
    }
}

 

最后的最后

今天刚好是九月十八号,提醒大家:

勿忘国耻,犯我华夏者,虽远必诛。

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值