/* * 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.darkrockstudios.apps.tminus.views.slidingtabs; 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; private SlidingTabLayout.TabColorizer mCustomTabColorizer; private final SimpleTabColorizer mDefaultTabColorizer; SlidingTabStrip( Context context ) { this( context, null ); } SlidingTabStrip( Context context, AttributeSet attrs ) { super( context, attrs ); setWillNotDraw( false ); final float density = getResources().getDisplayMetrics().density; 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 ); 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) ); } void setCustomTabColorizer( SlidingTabLayout.TabColorizer customTabColorizer ) { mCustomTabColorizer = customTabColorizer; invalidate(); } void setSelectedIndicatorColors( int... colors ) { // Make sure that the custom colorizer is removed mCustomTabColorizer = null; mDefaultTabColorizer.setIndicatorColors( colors ); invalidate(); } void setDividerColors( int... colors ) { // Make sure that the custom colorizer is removed mCustomTabColorizer = null; mDefaultTabColorizer.setDividerColors( colors ); invalidate(); } 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); 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 ); 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 = (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. */ 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. * * @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 ); } private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { private int[] mIndicatorColors; private int[] mDividerColors; @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; } } }