1
/*
2
* Copyright (C) 2013 The Android Open Source Project
3
*
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
*
8
* http://www.apache.org/licenses/LICENSE-2.0
9
*
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*/
16
17
package com.example.android.common.view;
18
19
import android.R;
20
import android.content.Context;
21
import android.graphics.Canvas;
22
import android.graphics.Color;
23
import android.graphics.Paint;
24
import android.util.AttributeSet;
25
import android.util.TypedValue;
26
import android.view.View;
27
import android.widget.LinearLayout;
28
29
class SlidingTabStrip extends LinearLayout {
30
31
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
32
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
33
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
34
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
35
36
private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
37
private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
38
private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
39
40
private final int mBottomBorderThickness;
41
private final Paint mBottomBorderPaint;
42
43
private final int mSelectedIndicatorThickness;
44
private final Paint mSelectedIndicatorPaint;
45
46
private final int mDefaultBottomBorderColor;
47
48
private final Paint mDividerPaint;
49
private final float mDividerHeight;
50
51
private int mSelectedPosition;
52
private float mSelectionOffset;
53
54
private SlidingTabLayout.TabColorizer mCustomTabColorizer;
55
private final SimpleTabColorizer mDefaultTabColorizer;
56
57
SlidingTabStrip(Context context) {
58
this(context, null);
59
}
60
61
SlidingTabStrip(Context context, AttributeSet attrs) {
62
super(context, attrs);
63
setWillNotDraw(false);
64
65
final float density = getResources().getDisplayMetrics().density;
66
67
TypedValue outValue = new TypedValue();
68
context.getTheme().resolveAttribute(R.attr.colorForeground, outValue, true);
69
final int themeForegroundColor = outValue.data;
70
71
mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
72
DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
73
74
mDefaultTabColorizer = new SimpleTabColorizer();
75
mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
76
mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor,
77
DEFAULT_DIVIDER_COLOR_ALPHA));
78
79
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
80
mBottomBorderPaint = new Paint();
81
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
82
83
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
84
mSelectedIndicatorPaint = new Paint();
85
86
mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
87
mDividerPaint = new Paint();
88
mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
89
}
90
91
void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
92
mCustomTabColorizer = customTabColorizer;
93
invalidate();
94
}
95
96
void setSelectedIndicatorColors(int... colors) {
97
// Make sure that the custom colorizer is removed
98
mCustomTabColorizer = null;
99
mDefaultTabColorizer.setIndicatorColors(colors);
100
invalidate();
101
}
102
103
void setDividerColors(int... colors) {
104
// Make sure that the custom colorizer is removed
105
mCustomTabColorizer = null;
106
mDefaultTabColorizer.setDividerColors(colors);
107
invalidate();
108
}
109
110
void onViewPagerPageChanged(int position, float positionOffset) {
111
mSelectedPosition = position;
112
mSelectionOffset = positionOffset;
113
invalidate();
114
}
115
116
@Override
117
protected void onDraw(Canvas canvas) {
118
final int height = getHeight();
119
final int childCount = getChildCount();
120
final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
121
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
122
? mCustomTabColorizer
123
: mDefaultTabColorizer;
124
125
// Thick colored underline below the current selection
126
if (childCount > 0) {
127
View selectedTitle = getChildAt(mSelectedPosition);
128
int left = selectedTitle.getLeft();
129
int right = selectedTitle.getRight();
130
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
131
132
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
133
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
134
if (color != nextColor) {
135
color = blendColors(nextColor, color, mSelectionOffset);
136
}
137
138
// Draw the selection partway between the tabs
139
View nextTitle = getChildAt(mSelectedPosition + 1);
140
left = (int) (mSelectionOffset * nextTitle.getLeft() +
141
(1.0f - mSelectionOffset) * left);
142
right = (int) (mSelectionOffset * nextTitle.getRight() +
143
(1.0f - mSelectionOffset) * right);
144
}
145
146
mSelectedIndicatorPaint.setColor(color);
147
148
canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
149
height, mSelectedIndicatorPaint);
150
}
151
152
// Thin underline along the entire bottom edge
153
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
154
155
// Vertical separators between the titles
156
int separatorTop = (height - dividerHeightPx) / 2;
157
for (int i = 0; i < childCount - 1; i++) {
158
View child = getChildAt(i);
159
mDividerPaint.setColor(tabColorizer.getDividerColor(i));
160
canvas.drawLine(child.getRight(), separatorTop, child.getRight(),
161
separatorTop + dividerHeightPx, mDividerPaint);
162
}
163
}
164
165
/**
166
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
167
*/
168
private static int setColorAlpha(int color, byte alpha) {
169
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
170
}
171
172
/**
173
* Blend {@code color1} and {@code color2} using the given ratio.
174
*
175
* @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
176
* 0.0 will return {@code color2}.
177
*/
178
private static int blendColors(int color1, int color2, float ratio) {
179
final float inverseRation = 1f - ratio;
180
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
181
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
182
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
183
return Color.rgb((int) r, (int) g, (int) b);
184
}
185
186
private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
187
private int[] mIndicatorColors;
188
private int[] mDividerColors;
189
190
@Override
191
public final int getIndicatorColor(int position) {
192
return mIndicatorColors[position % mIndicatorColors.length];
193
}
194
195
@Override
196
public final int getDividerColor(int position) {
197
return mDividerColors[position % mDividerColors.length];
198
}
199
200
void setIndicatorColors(int... colors) {
201
mIndicatorColors = colors;
202
}
203
204
void setDividerColors(int... colors) {
205
mDividerColors = colors;
206
}
207
}
208
}