/*
* Copyright (C) 2015 Peter Cai
*
* This file is part of BlackLight
*
* BlackLight is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* BlackLight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with BlackLight. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jasonchen.microlang.view;
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.view.ViewParent;
import android.widget.LinearLayout;
import android.widget.TextView;
public class SlidingTabStrip extends LinearLayout {
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 2;
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
private final int mBottomBorderThickness;
private final Paint mBottomBorderPaint;
private final int mSelectedIndicatorThickness;
private final Paint mSelectedIndicatorPaint;
private final int mDefaultBottomBorderColor;
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(android.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);
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
mBottomBorderPaint = new Paint();
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
mSelectedIndicatorPaint = new Paint();
}
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 onViewPagerPageChanged(int position, float positionOffset) {
mSelectedPosition = position;
mSelectionOffset = positionOffset;
// Title colors changes when page scrolled
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
? mCustomTabColorizer
: mDefaultTabColorizer;
int id = getSlidingTabLayout().getTextViewId();
View selected = getChildAt(mSelectedPosition);
View selectedTitle = id == 0 ? selected : selected.findViewById(id);
int selectedColor = tabColorizer.getSelectedTitleColor(mSelectedPosition);
int normalColor = tabColorizer.getNormalTitleColor(mSelectedPosition);
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
View next = getChildAt(mSelectedPosition + 1);
View nextTitle = id == 0 ? next : next.findViewById(id);
// Set the gradient title colors
int nextSelectedColor = tabColorizer.getSelectedTitleColor(mSelectedPosition + 1);
int nextNormalColor = tabColorizer.getNormalTitleColor(mSelectedPosition + 1);
int selectedBlend = blendColors(selectedColor, normalColor, 1.0f - mSelectionOffset);
int nextBlend = blendColors(nextSelectedColor, nextNormalColor, mSelectionOffset);
if (selectedTitle instanceof TextView)
((TextView) selectedTitle).setTextColor(selectedBlend);
else if (selectedTitle instanceof TintImageView)
((TintImageView) selectedTitle).setColor(selectedBlend);
if (nextTitle instanceof TextView)
((TextView) nextTitle).setTextColor(nextBlend);
else if (nextTitle instanceof TintImageView)
((TintImageView) nextTitle).setColor(nextBlend);
} else if (mSelectionOffset == 0f) {
if (selectedTitle instanceof TextView)
((TextView) selectedTitle).setTextColor(selectedColor);
else if (selectedTitle instanceof TintImageView)
((TintImageView) selectedTitle).setColor(selectedColor);
}
invalidate();
}
void updateTitleViews() {
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
? mCustomTabColorizer
: mDefaultTabColorizer;
int id = getSlidingTabLayout().getTextViewId();
for (int i = 0; i < getChildCount(); i++) {
View v = getChildAt(i);
View child = id == 0 ? v : v.findViewById(id);
int color;
if (mSelectedPosition != i)
color = tabColorizer.getNormalTitleColor(i);
else
color = tabColorizer.getSelectedTitleColor(i);
if (child instanceof TextView) {
((TextView) v).setTextColor(color);
} else if (child instanceof TintImageView) {
((TintImageView) v).setColor(color);
}
}
invalidate();
}
SlidingTabLayout getSlidingTabLayout() {
ViewParent parent = getParent();
if (parent instanceof SlidingTabLayout) {
return (SlidingTabLayout) parent;
} else if (parent == null) {
return null;
} else {
throw new RuntimeException("The parent of a SlidingTabStrip must be a SlidingTabLayout");
}
}
@Override
protected void onDraw(Canvas canvas) {
final int height = getHeight();
final int childCount = getChildCount();
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);
}
/**
* 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);
}
public static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
private int[] mIndicatorColors;
@Override
public int getIndicatorColor(int position) {
return mIndicatorColors[position % mIndicatorColors.length];
}
@Override
public int getSelectedTitleColor(int position) {
return 0;
}
@Override
public int getNormalTitleColor(int position) {
return blendColors(getSelectedTitleColor(position), Color.GRAY, 0.6f);
}
void setIndicatorColors(int... colors) {
mIndicatorColors = colors;
}
}
}