blob: d5bbbae59f68ef6b98d17c004ed26d8096e8e7ac [file] [log] [blame]
Alexander Lucas97842ff2014-03-07 14:56:55 -08001/*
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
17package com.example.android.common.view;
18
19import android.R;
20import android.content.Context;
21import android.graphics.Canvas;
22import android.graphics.Color;
23import android.graphics.Paint;
24import android.util.AttributeSet;
25import android.util.TypedValue;
26import android.view.View;
27import android.widget.LinearLayout;
28
29class 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}