自定义View之仿虾米音乐TabLayout

520 阅读3分钟

写在开头

最近有点忙,好久没写文章了。最近公司App有个仿虾米音乐高大上tablayout的需求,网上百度了些没啥好的轮子,于是自己撸了一发。效果已实现,发出来,欢迎大家批评指正。

效果如下:
这里写图片描述

不太清楚,如何您正好有这个需求,欢迎下载提问题。
github.com/loveAndroid…

实现思路

首先这个效果系统的tablayout是无法实现的,至少我没发现合适的方式,所以就想着自定义一个tablayout来实现此方式。我们可以看源码的tablayout实现方式,他其实就是一个自定义HorizontalScrollView,所以我们也来根据我们的业务需求实现一个自定义HorizontalScrollView。

主要就是以下几方面:

初始化自定义属性及一些初始化操作。
设置数据源。
关联viewpager及更新界面,添加方法设置当前显示tab。
重要: PageChangeListener实现各种渐变效果

初始化自定义属性及一些初始化操作

      public XiaMiTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initStyle(context, attrs);
        setFillViewport(true);
        setHorizontalScrollBarEnabled(false);
        mTabContainer = new LinearLayout(context);

        addView(mTabContainer, 0, new LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
        mDataList = new ArrayList<>();
    }

    private void initStyle(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.XiaMiTabLayout, 0, 0);
        mNormalTextSize = typedArray.getDimensionPixelSize(R.styleable.XiaMiTabLayout_tab_normal_textSize, DEFAULT_NORMAL_TEXT_SIZE_SP);
        mSelectTextSize = typedArray.getDimensionPixelSize(R.styleable.XiaMiTabLayout_tab_select_textSize, DEFAULT_SELECT_TEXT_SIZE_SP);
        typedArray.recycle();
    }

设置数据源。

        /**
     * 设置数据源
     */
    public void setDataList(List<String> dataList) {
        this.mDataList.clear();
        this.mDataList.addAll(dataList);
    }

关联viewpager及更新界面,添加方法设置当前显示tab。

       /**
     * 关联viewpager
     */
    public void setupWithViewPager(ViewPager viewPager) {
        this.mViewPager = viewPager;
        if (viewPager == null) {
            throw new IllegalArgumentException("viewpager not is null");
        }

        PagerAdapter pagerAdapter = viewPager.getAdapter();
        if (pagerAdapter == null) {
            throw new IllegalArgumentException("pagerAdapter not is null");
        }
        this.mViewPager.addOnPageChangeListener(new TabPagerChanger());
        mTabCount = pagerAdapter.getCount();
        mCurrentTabPosition = viewPager.getCurrentItem();
        notifyDataSetChanged();
    }

    /**
     * 更新界面
     */
    public void notifyDataSetChanged() {
        mTabContainer.removeAllViews();
        for (int i = 0; i < mTabCount; i++) {
            final int currentPosition = i;
            TextView tabTextView = createTextView();
            tabTextView.setText(mDataList.get(i));
            tabTextView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View view) {
                    mViewPager.setCurrentItem(currentPosition);
                }
            });
            mTabContainer.addView(tabTextView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
        }
        setSelectedTabView(mCurrentTabPosition);
    }

    /**
     * 第一次设置选中tab效果
     *
     * @param position
     */
    protected void setSelectedTabView(int position) {
        this.mCurrentTabPosition = position;
        for (int i = 0; i < mTabCount; i++) {
            View view = mTabContainer.getChildAt(i);
            if (view instanceof TextView) {
                TextView textView = (TextView) view;
                textView.setSelected(position == i);
                textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, position == i ? mSelectTextSize : mNormalTextSize);
                textView.setTypeface(position == i ? Typeface.DEFAULT_BOLD : Typeface.DEFAULT);
                textView.setTextColor(position == i ? DEFAULT_SELECT_TEXT_COLOR : DEFAULT_NORMAL_TEXT_COLOR);
                if (mCurrentTabPosition == i) {
                    textView.setPadding(ScreenUtils.dipToPx(getContext(), 5), 0, ScreenUtils.dipToPx(getContext(), 5), ScreenUtils.dipToPx(getContext(), 6));
                } else {
                    textView.setPadding(ScreenUtils.dipToPx(getContext(), 5), 0, ScreenUtils.dipToPx(getContext(), 5), ScreenUtils.dipToPx(getContext(), 10));
                }
            }
        }
    }

PageChangeListener实现各种渐变效果

  //利用估值器实现渐变
    private ArgbEvaluator argbEvaluator = new ArgbEvaluator();
    private IntEvaluator intEvaluator = new IntEvaluator();
    /**
     * pager监听
     */
    private class TabPagerChanger implements ViewPager.OnPageChangeListener {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            final TextView selectedChild = (TextView) mTabContainer.getChildAt(position);
            final TextView nextChild = position + 1 < mTabContainer.getChildCount()
                    ? (TextView) mTabContainer.getChildAt(position + 1)
                    : null;

            if (selectedChild != null) {
                selectedChild.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSelectTextSize - (mSelectTextSize - mNormalTextSize) * positionOffset);
                //初始颜色值
                int bgColor = DEFAULT_SELECT_TEXT_COLOR;
                if (positionOffset == 0) {
                    //显示初始透明颜色
                    bgColor = DEFAULT_SELECT_TEXT_COLOR;
                } else if (positionOffset > 1) {
                    //滚动到一个定值后,颜色最深,而且不再加深
                    bgColor = DEFAULT_NORMAL_TEXT_COLOR;
                } else {
                    //滚动过程中渐变的颜色
                    bgColor = (int) argbEvaluator.evaluate(positionOffset, DEFAULT_SELECT_TEXT_COLOR, DEFAULT_NORMAL_TEXT_COLOR);
                }
                selectedChild.setTextColor(bgColor);

                int pad = 6;
                if (positionOffset == 0) {
                    //显示初始透明颜色
                    pad = 6;
                } else if (positionOffset > 1) {
                    //滚动到一个定值后,颜色最深,而且不再加深
                    pad = 10;
                } else {
                    //滚动过程中渐变的padding
                    pad = intEvaluator.evaluate(positionOffset, 6, 10);
                }
                selectedChild.setPadding(ScreenUtils.dipToPx(getContext(), 5), 0, ScreenUtils.dipToPx(getContext(), 5), ScreenUtils.dipToPx(getContext(), pad));

                if (positionOffset > 0.5) {
                    selectedChild.setTypeface(Typeface.DEFAULT);
                } else {
                    selectedChild.setTypeface(Typeface.DEFAULT_BOLD);
                }
            }

            if (nextChild != null) {
                nextChild.setTextSize(TypedValue.COMPLEX_UNIT_PX, mNormalTextSize + (mSelectTextSize - mNormalTextSize) * positionOffset);
                //初始颜色值
                int bgColor = DEFAULT_NORMAL_TEXT_COLOR;
                if (positionOffset == 0) {
                    //显示初始透明颜色
                    bgColor = DEFAULT_NORMAL_TEXT_COLOR;
                } else if (positionOffset > 1) {
                    //滚动到一个定值后,颜色最深,而且不再加深
                    bgColor = DEFAULT_SELECT_TEXT_COLOR;
                } else {
                    //滚动过程中渐变的颜色
                    bgColor = (int) argbEvaluator.evaluate(positionOffset, DEFAULT_NORMAL_TEXT_COLOR, DEFAULT_SELECT_TEXT_COLOR);
                }
                nextChild.setTextColor(bgColor);

                int pad = 10;
                if (positionOffset == 0) {
                    pad = 10;
                } else if (positionOffset > 1) {
                    pad = 6;
                } else {
                    //滚动过程中渐变的padding
                    pad = intEvaluator.evaluate(positionOffset, 10, 6);
                }
                nextChild.setPadding(ScreenUtils.dipToPx(getContext(), 5), 0, ScreenUtils.dipToPx(getContext(), 5), ScreenUtils.dipToPx(getContext(), pad));
                if (Math.abs(positionOffset) > 0.5) {
                    nextChild.setTypeface(Typeface.DEFAULT_BOLD);
                } else {
                    nextChild.setTypeface(Typeface.DEFAULT);
                }
            }

        }

        @Override
        public void onPageSelected(int position) {
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }

写在末尾

欢迎下载Demo批评指正,如您对指示器也有要求但无思路,也可私信联系,共同探讨(已实现)。