SlidingTabColors——viewpager和自定义导航栏

SlidingTabColors

之前阅读过Android Samples中UI的部分,过后发现很多知识点都忘了。今天开始把看过的东西都整理记录一下,从小事做起吧。与君共勉。

源代码在/android-23/ui/SlidingTabsColors下。

  • 使用ViewPager滑动界面
  • Canvas绘图
  • Fragment的使用
  • 动态添加View
  • ScrollView的使用

目录


数据的交互

MainActivity

源码中主界面包含两个Fragment,上面的主要功能是打Log,下面的Fragment是核心功能的实现。

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
SlidingTabsColorsFragment fragment = new SlidingTabsColorsFragment();
transaction.replace(R.id.sample_content_fragment, fragment);
transaction.commit();

ContentFragment

此类为ViewPager中Adapter的Item。

public class ContentFragment extends Fragment {
    //使用Bundle传递数据。
    public static ContentFragment newInstance(CharSequence title, int indicatorColor,
            int dividerColor) {
        Bundle bundle = new Bundle();
        bundle.putCharSequence(KEY_TITLE, title);
        bundle.putInt(KEY_INDICATOR_COLOR, indicatorColor);
        bundle.putInt(KEY_DIVIDER_COLOR, dividerColor);

        ContentFragment fragment = new ContentFragment();
        fragment.setArguments(bundle);

        return fragment;
    }

    //该Fragment中包含三个TextView,分别显示文字,指示器颜色,分割线颜色。
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        Bundle args = getArguments();

        if (args != null) {
            TextView title = (TextView) view.findViewById(R.id.item_title);
            title.setText("Title: " + args.getCharSequence(KEY_TITLE));

            int indicatorColor = args.getInt(KEY_INDICATOR_COLOR);
            TextView indicatorColorView = (TextView) view.findViewById(R.id.item_indicator_color);
            indicatorColorView.setText("Indicator: #" + Integer.toHexString(indicatorColor));
            indicatorColorView.setTextColor(indicatorColor);

            int dividerColor = args.getInt(KEY_DIVIDER_COLOR);
            TextView dividerColorView = (TextView) view.findViewById(R.id.item_divider_color);
            dividerColorView.setText("Divider: #" + Integer.toHexString(dividerColor));
            dividerColorView.setTextColor(dividerColor);
        }
    }
}

SlidingTabsColorsFragment

在SlidingTabsColorsFragment中添加ViewPager,而ViewPager的中的内容是ContentFragment,由下面这个类的createFragment()方法提供提供。

static class SamplePagerItem {
        private final CharSequence mTitle;
        private final int mIndicatorColor;
        private final int mDividerColor;

        SamplePagerItem(CharSequence title, int indicatorColor, int dividerColor) {
            mTitle = title;
            mIndicatorColor = indicatorColor;
            mDividerColor = dividerColor;
        }

        /**
         * @return A new {@link Fragment} to be displayed by a {@link ViewPager}
         * 获取ContentFragment()的方法
         */
        Fragment createFragment() {
            return ContentFragment.newInstance(mTitle, mIndicatorColor, mDividerColor);
        }

        /**
         * @return the title which represents this tab. In this sample this is used directly by
         * {@link android.support.v4.view.PagerAdapter#getPageTitle(int)}
         * ViewPager中的文本内容
         */
        CharSequence getTitle() {
            return mTitle;
        }

        /**
         * @return the color to be used for indicator on the {@link SlidingTabLayout}
         * ScrollView中下面指示器的颜色
         */
        int getIndicatorColor() {
            return mIndicatorColor;
        }

        /**
         * @return the color to be used for right divider on the {@link SlidingTabLayout}
         * ScrollView中分割线的颜色
         */
        int getDividerColor() {
            return mDividerColor;
        }
    }
//使用ArrayList创建几个ContentFragment
private List<SamplePagerItem> mTabs = new ArrayList<SamplePagerItem>();
//选择FragmentPagerAdapter作为ViewPager的Adapter。
class SampleFragmentPagerAdapter extends FragmentPagerAdapter {

        SampleFragmentPagerAdapter(FragmentManager fm) {
            super(fm);
        }

//获取item,类型为ContentFragment
        @Override
        public Fragment getItem(int i) {
            return mTabs.get(i).createFragment();
        }

        @Override
        public int getCount() {
            return mTabs.size();
        }

//显示内容文字
        @Override
        public CharSequence getPageTitle(int position) {
            return mTabs.get(position).getTitle();
        }

    }
    mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
    mViewPager.setAdapter(new SampleFragmentPagerAdapter(getChildFragmentManager()));

自定义的View

滑动条需要手动实现,原理是在HorizontalScrollView中添加一个LinearLayout,再在LinearLayout中添加几个TextView,当然也可以是其他的ui控件。

SlidingTabLayout

SlidingTabLayout继承自HorizontalScrollView

public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // Disable the Scroll Bar
        setHorizontalScrollBarEnabled(false);
        // Make sure that the Tab Strips fills this View
        setFillViewport(true);

    //首先添加一个LinearLayout
        mTabStrip = new SlidingTabStrip(context);
        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    }

前面ViewPager的Item有几个,那么在LinearLayout中也添加几个TextView。

private void populateTabStrip() {
        final PagerAdapter adapter = mViewPager.getAdapter();
        final View.OnClickListener tabClickListener = new TabClickListener();

        for (int i = 0; i < adapter.getCount(); i++) {
            View tabView = null;
            TextView tabTitleView = null;

            if (tabView == null) {
                tabView = createDefaultTabView(getContext());
            }

            tabTitleView.setText(adapter.getPageTitle(i));
            //给TextView添加点击事件,点击textview,ViewPager调转到对应id的Item
            tabView.setOnClickListener(tabClickListener);

            mTabStrip.addView(tabView);
        }
    }

//刚初始化时,ScrollView显示在第一个item的位置
@Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        if (mViewPager != null) {
            scrollToTab(mViewPager.getCurrentItem(), 0);
        }
    }

/*需要注意scrollToTab()方法中的scrollTo(x,y)方法,该方法在ScrollView中,其中的两个参数指的是scrollView的偏移量。
scrollView第一次在界面上显示时,它的左边应该是贴着手机的左边的(不一定在手机的左上角),View的左上角的坐标为(0,0),假如我们调用
scrollTo(x,y)方法,那么整个ScrollView会移动(-x,-y)的距离,即
scrollView上的(x,y)点会移动到刚刚(0,0)的位置。--我的理解是这样的。
*/
    private void scrollToTab(int tabIndex, int positionOffset) {
        final int tabStripChildCount = mTabStrip.getChildCount();
        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
            return;
        }

        View selectedChild = mTabStrip.getChildAt(tabIndex);
        if (selectedChild != null) {
/*targetScrollX就是ScrollView将要将要水平移动的位置,此处设置的是比positionOffset要小一些。比如Viewpager移动到
了下一个,则positionOffset为一个TextView的宽度,但是我们让他移动positionOffset-mTitleOffset个宽度,那么就可以显示上一个
TextView的一部分了。
*/
            int targetScrollX = selectedChild.getLeft() + positionOffset;

            if (tabIndex > 0 || positionOffset > 0) {
                // If we're not at the first child and are mid-scroll, make sure we obey the offset
                targetScrollX -= mTitleOffset;
            }

            scrollTo(targetScrollX, 0);
        }
    }
//添加ViewPagerListener,为了实现滑动ViewPager,指示器也跟着运动。
    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
        private int mScrollState;

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            int tabStripChildCount = mTabStrip.getChildCount();
           //position不能等于tabStripChildCount,后面要获取
           //nextView,若等于,nextView为空。
            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
                return;
            }
//设置extraOffset,只是为了界面好看一些。
            mTabStrip.onViewPagerPageChanged(position, positionOffset);

            View selectedTitle = mTabStrip.getChildAt(position);
            int extraOffset = (selectedTitle != null)
                    ? (int) (positionOffset * selectedTitle.getWidth())
                    : 0;
            scrollToTab(position, extraOffset);

        }
//点击了TextView,ScrollView也要跟着改变,如果position>0,ScrollView会跟着滚动。
        @Override
        public void onPageSelected(int position) {
            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
                mTabStrip.onViewPagerPageChanged(position, 0f);
                scrollToTab(position, 0);
            }
        }

    }
    //LinearLayout中textView的点击事件
private class TabClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
            //获取点击的TextView的index,选择Viewpager相应的
            //item
                if (v == mTabStrip.getChildAt(i)) {
                    mViewPager.setCurrentItem(i);
                    return;
                }
            }
        }
    }

SlidingTabStrip

SlidingTabStrip继承自LinearLayout,即上面提到的LinearLayout,他是SlidingTabLayout的子View。我们在其中添加它的子View,也就是TextView,还需要在几个TextView的中间画分割线,最重要的是该View下方的指示器,他会跟随ViewPager的翻页移动到相应的位置。

//滑动Viewpager时调用了此方法,传入当前ViewPager的item 
//position和滑动百分比,调用invalidate()来刷新view。
void onViewPagerPageChanged(int position, float positionOffset) {
        mSelectedPosition = position;
        mSelectionOffset = positionOffset;
        invalidate();
    }
@Override
    protected void onDraw(Canvas canvas) {
        final int height = getHeight();
        final int childCount = getChildCount();
//        设置分割线为半个TextView高度(随便一个值,好看就行)
        final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);

        // Thick colored underline below the current selection
        if (childCount > 0) {
        //获取子View和它的宽高
            View selectedTitle = getChildAt(mSelectedPosition);
            int left = selectedTitle.getLeft();
            int right = selectedTitle.getRight();
            //通过一个interface获取当前positon对应SamplePagerItem的颜色。
            int color = tabColorizer.getIndicatorColor(mSelectedPosition);

            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
                if (color != nextColor) {
                    color = blendColors(nextColor, color, mSelectionOffset);
                }

                // Draw the selection partway between the tabs
                View nextTitle = getChildAt(mSelectedPosition + 1);
                //left = 当前View的左侧+(nextView左侧-当前View的左侧)×offset,右侧同理。
                left = (int) (mSelectionOffset * nextTitle.getLeft() +
                        (1.0f - mSelectionOffset) * left);
                right = (int) (mSelectionOffset * nextTitle.getRight() +
                        (1.0f - mSelectionOffset) * right);
            }
            mSelectedIndicatorPaint.setColor(color);

            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
                    height, mSelectedIndicatorPaint);
        }
        // Thin underline along the entire bottom edge
        //在此View下方画一条线
        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);

        // Vertical separators between the titles
        for (int i = 0; i < childCount - 1; i++) {
            View child = getChildAt(i);
            mDividerPaint.setColor(tabColorizer.getDividerColor(i));
            //共有childCount个TextView,中间有childCount-1个间隔,在其中画分割线。
            canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
                    separatorTop + dividerHeightPx, mDividerPaint);
        }
    }

要实现滑动过程中指示器颜色渐变,只需要在onDraw()中更新painter的Color就可以。
颜色渐变的方法如下:

//获取两个颜色进行中和,实现颜色过度
private static int blendColors(int color1, int color2, float ratio) {
        final float inverseRation = 1f - ratio;
        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
        return Color.rgb((int) r, (int) g, (int) b);
    }

实现效果

这里写图片描述 这里写图片描述 这里写图片描述

源码可百度android samples到ui目录下找。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值