/* * Copyright 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.slidingtabscolors; import com.example.android.common.view.SlidingTabLayout; import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; /** * A basic sample which shows how to use {@link com.example.android.common.view.SlidingTabLayout} * to display a custom {@link ViewPager} title strip which gives continuous feedback to the user * when scrolling. */ public class SlidingTabsColorsFragment extends Fragment { /** * This class represents a tab to be displayed by {@link ViewPager} and it‘s associated * {@link SlidingTabLayout}. * 这个类代表了一个tab通过ViewPager去显示,它和SlidingTabLayout相关联 */ static class SamplePagerItem { private final CharSequence mTitle;//pager title private final int mIndicatorColor;//pager indicator color 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} * 返回一个新的Fragment通过ViewPager去显示 */ 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)} */ CharSequence getTitle() { return mTitle; } /** * @return the color to be used for indicator on the {@link SlidingTabLayout} */ int getIndicatorColor() { return mIndicatorColor; } /** * @return the color to be used for right divider on the {@link SlidingTabLayout} */ int getDividerColor() { return mDividerColor; } } static final String LOG_TAG = "SlidingTabsColorsFragment"; /** * A custom {@link ViewPager} title strip which looks much like Tabs present in Android v4.0 and * above, but is designed to give continuous feedback to the user when scrolling. */ private SlidingTabLayout mSlidingTabLayout; /** * A {@link ViewPager} which will be used in conjunction with the {@link SlidingTabLayout} above. */ private ViewPager mViewPager; /** * List of {@link SamplePagerItem} which represent this sample‘s tabs. */ private List<SamplePagerItem> mTabs = new ArrayList<SamplePagerItem>(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // BEGIN_INCLUDE (populate_tabs) /** * Populate our tab list with tabs. Each item contains a title, indicator color and divider * color, which are used by {@link SlidingTabLayout}. * 使用我们的tab填充tabs列表,每个条目包含一个标题,指示器颜色,和分割线颜色 */ mTabs.add(new SamplePagerItem( getString(R.string.tab_stream), // Title Color.BLUE, // Indicator color Color.GRAY // Divider color )); mTabs.add(new SamplePagerItem( getString(R.string.tab_messages), // Title Color.RED, // Indicator color Color.GRAY // Divider color )); mTabs.add(new SamplePagerItem( getString(R.string.tab_photos), // Title Color.YELLOW, // Indicator color Color.GRAY // Divider color )); mTabs.add(new SamplePagerItem( getString(R.string.tab_notifications), // Title Color.GREEN, // Indicator color Color.GRAY // Divider color )); // END_INCLUDE (populate_tabs) } /** * Inflates the {@link View} which will be displayed by this {@link Fragment}, from the app‘s * resources. */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_sample, container, false); } // BEGIN_INCLUDE (fragment_onviewcreated) /** * This is called after the {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} has finished. * Here we can pick out the {@link View}s we need to configure from the content view. * * We set the {@link ViewPager}‘s adapter to be an instance of * {@link SampleFragmentPagerAdapter}. The {@link SlidingTabLayout} is then given the * {@link ViewPager} so that it can populate itself. * * @param view View created in {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} */ @Override public void onViewCreated(View view, Bundle savedInstanceState) { // BEGIN_INCLUDE (setup_viewpager) // Get the ViewPager and set it‘s PagerAdapter so that it can display items mViewPager = (ViewPager) view.findViewById(R.id.viewpager); mViewPager.setAdapter(new SampleFragmentPagerAdapter(getChildFragmentManager())); // END_INCLUDE (setup_viewpager) // BEGIN_INCLUDE (setup_slidingtablayout) // Give the SlidingTabLayout the ViewPager, this must be done AFTER the ViewPager has had // it‘s PagerAdapter set.这必须在Viewpager的adapter被设置之后 mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs); mSlidingTabLayout.setViewPager(mViewPager); mSlidingTabLayout.setCustomTextViewColor(Color.RED,Color.BLACK); // BEGIN_INCLUDE (tab_colorizer) // Set a TabColorizer to customize the indicator and divider colors. Here we just retrieve // the tab at the position, and return it‘s set color mSlidingTabLayout.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() { @Override public int getIndicatorColor(int position) { return mTabs.get(position).getIndicatorColor(); } @Override public int getDividerColor(int position) { return mTabs.get(position).getDividerColor(); } }); // END_INCLUDE (tab_colorizer) // END_INCLUDE (setup_slidingtablayout) } // END_INCLUDE (fragment_onviewcreated) /** * The {@link FragmentPagerAdapter} used to display pages in this sample. The individual pages * are instances of {@link ContentFragment} which just display three lines of text. Each page is * created by the relevant {@link SamplePagerItem} for the requested position. * <p> * The important section of this class is the {@link #getPageTitle(int)} method which controls * what is displayed in the {@link SlidingTabLayout}. */ class SampleFragmentPagerAdapter extends FragmentPagerAdapter { SampleFragmentPagerAdapter(FragmentManager fm) { super(fm); } /** * Return the {@link android.support.v4.app.Fragment} to be displayed at {@code position}. * <p> * Here we return the value returned from {@link SamplePagerItem#createFragment()}. */ @Override public Fragment getItem(int i) { return mTabs.get(i).createFragment(); } @Override public int getCount() { return mTabs.size(); } // BEGIN_INCLUDE (pageradapter_getpagetitle) /** * Return the title of the item at {@code position}. This is important as what this method * returns is what is displayed in the {@link SlidingTabLayout}. * <p> * Here we return the value returned from {@link SamplePagerItem#getTitle()}. */ @Override public CharSequence getPageTitle(int position) { return mTabs.get(position).getTitle(); } // END_INCLUDE (pageradapter_getptle) } }/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.common.view; import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; import android.os.Build; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.widget.HorizontalScrollView; import android.widget.TextView; import com.example.android.common.logger.Log; /** * To be used with ViewPager to provide a tab indicator component which give constant feedback as to * the user‘s scroll progress. * <p> * To use the component, simply add it to your view hierarchy. Then in your * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. * <p> * The colors can be customized in two ways. The first and simplest is to provide an array of colors * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The * alternative is via the {@link TabColorizer} interface which provides you complete control over * which color is used for any individual position. * <p> * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, * providing the layout ID of your custom layout. * * just like list * * 本类作用: * 将SlidingTabStrip作为子类添加到本类 * 在本类中根据PagerAdapter的大小生成TextView添加到SlidingTabStrip中,并设置TextView的点击监听和设置ViewPager.OnPageChangeListener监听器 * 在PageChange监听器中控制TextView的移动和调用SlidingTabStrip中的相应方法 */ public class SlidingTabLayout extends HorizontalScrollView { /** * Allows complete control over the colors drawn in the tab layout. Set with * {@link #setCustomTabColorizer(TabColorizer)}. */ public interface TabColorizer { /** * @return return the color of the indicator used when {@code position} is selected. */ int getIndicatorColor(int position); /** * @return return the color of the divider drawn to the right of {@code position}. */ int getDividerColor(int position); } private static final int TITLE_OFFSET_DIPS = 24; // view padding private static final int TAB_VIEW_PADDING_DIPS = 16; // text size private static final int TAB_VIEW_TEXT_SIZE_SP = 12; private static final int TAB_VIEW_DEFAULT_TEXT_COLOR = Color.BLACK; private int mTitleOffset; private int mTabViewLayoutId; private int mTabViewTextViewId; private int mTabTextViewFocusColor; private int mTabTextViewUnFocusColor; private ViewPager mViewPager; private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; private final SlidingTabStrip mTabStrip; public SlidingTabLayout(Context context) { this(context, null); } public SlidingTabLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // Disable the Scroll Bar // Define whether the horizontal scrollbar should be drawn or not. // The scrollbar is not drawn by default. // 定义水平滚动栏是否应该被绘制 setHorizontalScrollBarEnabled(false); // Make sure that the Tab Strips fills this View // 表明这个HorizontalScrollView是否应该拉伸它的内容宽度去填充视图 setFillViewport(true); mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); mTabStrip = new SlidingTabStrip(context); //将mTabStrip添加到本容器中 addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } /** * Set the custom {@link TabColorizer} to be used. * * If you only require simple custmisation then you can use * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve * similar effects. * * set TabColorizer into SlidingTabStrip container */ public void setCustomTabColorizer(TabColorizer tabColorizer) { mTabStrip.setCustomTabColorizer(tabColorizer); } /** * Sets the colors to be used for indicating the selected tab. These colors are treated as a * circular array. Providing one color will mean that all tabs are indicated with the same color. */ public void setSelectedIndicatorColors(int... colors) { mTabStrip.setSelectedIndicatorColors(colors); } /** * Sets the colors to be used for tab dividers. These colors are treated as a circular array. * Providing one color will mean that all tabs are indicated with the same color. */ public void setDividerColors(int... colors) { mTabStrip.setDividerColors(colors); } /** * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so * that the layout can update it‘s scroll position correctly. * * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) * * 为本类中的监听器赋值 */ public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { mViewPagerPageChangeListener = listener; } /** * Set the custom layout to be inflated for the tab views. * * @param layoutResId Layout id to be inflated * @param textViewId id of the {@link TextView} in the inflated view */ public void setCustomTabView(int layoutResId, int textViewId) { mTabViewLayoutId = layoutResId; mTabViewTextViewId = textViewId; } /** * * @param focusColor 焦点的颜色 * @param unfocusCilor 非焦点的颜色 */ public void setCustomTextViewColor(int focusColor,int unfocusCilor){ mTabTextViewFocusColor = focusColor; mTabTextViewUnFocusColor = unfocusCilor; } /** * Sets the associated view pager. Note that the assumption here is that the pager content * (number of tabs and tab titles) does not change after this call has been made. * 注意调用这个方法后pager的内容没有改变 * * remove all view in container and set listener for viewpager */ public void setViewPager(ViewPager viewPager) { mTabStrip.removeAllViews(); mViewPager = viewPager; if (viewPager != null) { viewPager.setOnPageChangeListener(new InternalViewPagerListener()); populateTabStrip(); } } /** * Create a default view to be used for tabs. This is called if a custom tab view is not set via * {@link #setCustomTabView(int, int)}. * 创建tabs使用的一个默认的TextView,如果没有通过setCustomTabView(int, int)设置一个自定义的tab view这将被调用 */ protected TextView createDefaultTabView(Context context) { TextView textView = new TextView(context); textView.setGravity(Gravity.CENTER); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); textView.setTypeface(Typeface.DEFAULT_BOLD); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // If we‘re running on Honeycomb or newer, then we can use the Theme‘s // selectableItemBackground to ensure that the View has a pressed state // 如果我们运行在3.0或者以上的版本,那么我们可以使用主题的selectableItemBackground去确保View有一个按下的状态 TypedValue outValue = new TypedValue(); getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true); textView.setBackgroundResource(outValue.resourceId); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { // If we‘re running on ICS or newer, enable all-caps to match the Action Bar tab style //如果我们运行在4.0或者4.0版本以上,Action Bar的tab的所有字母大写样式可用 textView.setAllCaps(true); } int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); textView.setPadding(padding, padding, padding, padding); return textView; } /** * 根据PagerAdapter的数量,得到tabview并设置它的标题,设置监听器,添加到SlidingTabStrip中 */ 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; //如果自定义的mTabViewLayoutId有值,则使用自定义的布局 if (mTabViewLayoutId != 0) { // If there is a custom tab view layout id set, try and inflate it // 如果这里有自定义的tab的视图布局id,尝试去填充它 tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, false); tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); } //如果tabView没有赋值,则创建默认的TabView if (tabView == null) { tabView = createDefaultTabView(getContext()); } if (tabTitleView == null && TextView.class.isInstance(tabView)) { tabTitleView = (TextView) tabView; } //设置标题和监听器 tabTitleView.setText(adapter.getPageTitle(i)); tabView.setOnClickListener(tabClickListener); //添加到SlidingTabStrip中 mTabStrip.addView(tabView); } } /** * This is called when the view is attached to a window. * At this point it has a Surface and will start drawing. * Note that this function is guaranteed to be called before onDraw(android.graphics.Canvas), * however it may be called any time before the first onDraw -- * including before or after onMeasure(int, int) * * 当一个view被附着到window上的时候被调用。 * * 注意这个功能被保证在onDraw(android.graphics.Canvas)之前调用,但是它可能在第一次onDraw方法之前的任何时候被调用 * 包括onMeasure(int, int)之前或者之后 */ @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (mViewPager != null) { scrollToTab(mViewPager.getCurrentItem(), 0); } } /** * 控制TextView的滚动 * @param tabIndex * @param positionOffset */ private void scrollToTab(int tabIndex, int positionOffset) { final int tabStripChildCount = mTabStrip.getChildCount(); //不可以小于0 大于子类的数量 if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { return; } //得到当前的View View selectedChild = mTabStrip.getChildAt(tabIndex); if (selectedChild != null) { 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 // 如果我们不在第一个子类或者中间滚动,确定服从位移.mTitleOffset为24dp targetScrollX -= mTitleOffset; } //为什么向左滚动滚动完成后才设置颜色,向右滚动开始滚动就已经开始设置颜色 //因为向左滚动的时候滚动完成position才变成position+1,向右滚动开始滚动position就变成position-1了 if(positionOffset == 0) setTabTextColor((TextView) selectedChild,tabIndex); //滚动到指定的坐标位置 scrollTo(targetScrollX, 0); } } /** * 设置文本的颜色 */ public void setTabTextColor(TextView selectedChild,int tabIndex){ if(mTabTextViewFocusColor == 0){ mTabTextViewFocusColor = TAB_VIEW_DEFAULT_TEXT_COLOR; } if(mTabTextViewUnFocusColor == 0){ mTabTextViewUnFocusColor = TAB_VIEW_DEFAULT_TEXT_COLOR; } selectedChild.setTextColor(mTabTextViewFocusColor); for (int i = 0; i < mTabStrip.getChildCount(); i++) { if(i != tabIndex){ TextView unfocusText = (TextView) mTabStrip.getChildAt(i); unfocusText.setTextColor(mTabTextViewUnFocusColor); } } } /** * 实现了OnPageChangeListener监听器 */ private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { private int mScrollState; @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { int tabStripChildCount = mTabStrip.getChildCount(); if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { return; } //ViewPager滚动的时候positionOffset值为0~1之间 mTabStrip.onViewPagerPageChanged(position, positionOffset); View selectedTitle = mTabStrip.getChildAt(position); //positionOffset如果从0~1表示向左滑,1~0表示向右滑 int extraOffset = (selectedTitle != null) ? (int) (positionOffset * selectedTitle.getWidth()) : 0; scrollToTab(position, extraOffset); if (mViewPagerPageChangeListener != null) { mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } } @Override public void onPageScrollStateChanged(int state) { mScrollState = state; if (mViewPagerPageChangeListener != null) { mViewPagerPageChangeListener.onPageScrollStateChanged(state); } } @Override public void onPageSelected(int position) { /** * Indicates that the pager is in an idle, settled state. * The current page is fully in view and no animation is in progress. * 表明页面为空闲状态,停止状态,当前页面已经完全在view中,没有动画正在进行中 */ if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { mTabStrip.onViewPagerPageChanged(position, 0f); scrollToTab(position, 0); } if (mViewPagerPageChangeListener != null) { mViewPagerPageChangeListener.onPageSelected(position); } } } /** * tab click listener */ private class TabClickListener implements View.OnClickListener { @Override public void onClick(View v) { for (int i = 0; i < mTabStrip.getChildCount(); i++) { //judge which view is clicked if (v == mTabStrip.getChildAt(i)) { mViewPager.setCurrentItem(i); return; } } } } }/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.common.view; import android.R; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.widget.LinearLayout; /** * 本类作用: * 绘制分割线,绘制底部指示器,绘制底部边界 */ class SlidingTabStrip extends LinearLayout { /** * 默认底部边界的厚度(宽度) */ private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2; /** * 默认底部边界颜色的透明度 */ private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; /** * 已选指示器的厚度 */ private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8; /** * 默认已选指示器的颜色 */ private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; /** * 默认分割线的厚度 */ private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1; /** * 默认分割线颜色的透明度 */ private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20; /** * 默认分割线的高度 */ private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f; /** * 底部边界的高度 */ private final int mBottomBorderThickness; /** * 默认底部边界的画笔 */ private final Paint mBottomBorderPaint; /** * 已选指示器的高度 */ private final int mSelectedIndicatorThickness; /** * 已选指示器的画笔 */ private final Paint mSelectedIndicatorPaint; /** * 默认底部边界的颜色 */ private final int mDefaultBottomBorderColor; /** * 分割线的画笔 */ private final Paint mDividerPaint; /** * 分割线的高度 */ private final float mDividerHeight; /** * 选择的位置 */ private int mSelectedPosition; /** * 选择的位移 */ private float mSelectionOffset; /** * 自定义的Tab样式 */ private SlidingTabLayout.TabColorizer mCustomTabColorizer; /** * 默认的Tab样式 */ private final SimpleTabColorizer mDefaultTabColorizer; SlidingTabStrip(Context context) { this(context, null); } SlidingTabStrip(Context context, AttributeSet attrs) { super(context, attrs); //如果这个view不需要绘制任何东西,那么设置这个标志去允许进一步的优化 setWillNotDraw(false); final float density = getResources().getDisplayMetrics().density; //动态类型数据值的容器,主要被用于Resources去持有资源值 TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true); final int themeForegroundColor = outValue.data; //混合主题的前景颜色和默认的底部边界颜色的透明度,默认的底部边界颜色 mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); //默认的SimpleTabColorizer,指示器颜色为0xFF33B5E5,默认的分割线的颜色为主题的前景色和0x20混合的颜色 mDefaultTabColorizer = new SimpleTabColorizer(); mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, DEFAULT_DIVIDER_COLOR_ALPHA)); //默认的底部高度 mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); mBottomBorderPaint = new Paint(); mBottomBorderPaint.setColor(mDefaultBottomBorderColor); mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); mSelectedIndicatorPaint = new Paint(); //分割先的高度和画笔 mDividerHeight = DEFAULT_DIVIDER_HEIGHT; mDividerPaint = new Paint(); mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density)); } /** * 设置自定义的颜色样式并重绘 * @param customTabColorizer */ void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { mCustomTabColorizer = customTabColorizer; invalidate(); } /** * 设置已选的指示器颜色并重绘 * @param colors */ void setSelectedIndicatorColors(int... colors) { // Make sure that the custom colorizer is removed mCustomTabColorizer = null; mDefaultTabColorizer.setIndicatorColors(colors); invalidate(); } /** * 设置分割线的颜色 * @param colors */ void setDividerColors(int... colors) { // Make sure that the custom colorizer is removed mCustomTabColorizer = null; mDefaultTabColorizer.setDividerColors(colors); invalidate(); } /** * ViewPager页面改变的时候,传递的位置和位置偏移量 * @param position 向左滚动的时候滚动完成position才变成position+1,向右滚动开始滚动position就变成position-1了 * @param positionOffset ViewPager页面改变的时候的positionOffset,如果从0~1表示向左滑,1~0表示向右滑 */ 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(); final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height); // 如果自定义的TabColorizer为null,则选择默认的TabColorizer final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null ? mCustomTabColorizer : mDefaultTabColorizer; // Thick colored underline below the current selection if (childCount > 0) { View selectedTitle = getChildAt(mSelectedPosition); int left = selectedTitle.getLeft(); int right = selectedTitle.getRight(); int color = tabColorizer.getIndicatorColor(mSelectedPosition); //mSelectionOffset > 0f用于判断是否在移动 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); // 计算指示器底部移动条的宽度,比如mSelectionOffset为0.7,就是0.7乘以后一个View的宽度加上0.3乘以前一个View的宽度 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,底部一条长的分割线 canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); // Vertical separators between the titles int separatorTop = (height - dividerHeightPx) / 2; // 绘制分割线 for (int i = 0; i < childCount - 1; i++) { View child = getChildAt(i); mDividerPaint.setColor(tabColorizer.getDividerColor(i)); canvas.drawLine(child.getRight(), separatorTop, child.getRight(), separatorTop + dividerHeightPx, mDividerPaint); } } /** * Set the alpha value of the {@code color} to be the given {@code alpha} value. * 使用argb (int alpha, int red, int green, int blue)方法生成一个新的颜色 */ private static int setColorAlpha(int color, byte alpha) { return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); } /** * Blend {@code color1} and {@code color2} using the given ratio. * 使用给定的比率混合color1和color2 * * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, * 0.0 will return {@code color2}. */ 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); } /** * 实现了SlidingTabLayout.TabColorizer,保存指示器的颜色数组,分割线的颜色数组 */ private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { private int[] mIndicatorColors; private int[] mDividerColors; /** * 设计相当精妙,使用位置取余mIndicatorColors数组的长度,不用担心数组越界的情况 * @param position * @return */ @Override public final int getIndicatorColor(int position) { return mIndicatorColors[position % mIndicatorColors.length]; } @Override public final int getDividerColor(int position) { return mDividerColors[position % mDividerColors.length]; } void setIndicatorColors(int... colors) { mIndicatorColors = colors; } void setDividerColors(int... colors) { mDividerColors = colors; } } }
时间: 2024-10-21 09:46:57