to top
SlidingTabsBasic / src / com.example.android.common / view /

SlidingTabStrip.java

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
}