Skip to content

Most visited

Recently visited

navigation
SlidingTabsBasic / src / com.example.android.common / view /

SlidingTabLayout.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.content.Context;
20
import android.graphics.Typeface;
21
import android.os.Build;
22
import android.support.v4.view.PagerAdapter;
23
import android.support.v4.view.ViewPager;
24
import android.util.AttributeSet;
25
import android.util.TypedValue;
26
import android.view.Gravity;
27
import android.view.LayoutInflater;
28
import android.view.View;
29
import android.widget.HorizontalScrollView;
30
import android.widget.TextView;
31
 
32
/**
33
 * To be used with ViewPager to provide a tab indicator component which give constant feedback as to
34
 * the user's scroll progress.
35
 * <p>
36
 * To use the component, simply add it to your view hierarchy. Then in your
37
 * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
38
 * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
39
 * <p>
40
 * The colors can be customized in two ways. The first and simplest is to provide an array of colors
41
 * via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
42
 * alternative is via the {@link TabColorizer} interface which provides you complete control over
43
 * which color is used for any individual position.
44
 * <p>
45
 * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
46
 * providing the layout ID of your custom layout.
47
 */
48
public class SlidingTabLayout extends HorizontalScrollView {
49
 
50
    /**
51
     * Allows complete control over the colors drawn in the tab layout. Set with
52
     * {@link #setCustomTabColorizer(TabColorizer)}.
53
     */
54
    public interface TabColorizer {
55
 
56
        /**
57
         * @return return the color of the indicator used when {@code position} is selected.
58
         */
59
        int getIndicatorColor(int position);
60
 
61
        /**
62
         * @return return the color of the divider drawn to the right of {@code position}.
63
         */
64
        int getDividerColor(int position);
65
 
66
    }
67
 
68
    private static final int TITLE_OFFSET_DIPS = 24;
69
    private static final int TAB_VIEW_PADDING_DIPS = 16;
70
    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
71
 
72
    private int mTitleOffset;
73
 
74
    private int mTabViewLayoutId;
75
    private int mTabViewTextViewId;
76
 
77
    private ViewPager mViewPager;
78
    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
79
 
80
    private final SlidingTabStrip mTabStrip;
81
 
82
    public SlidingTabLayout(Context context) {
83
        this(context, null);
84
    }
85
 
86
    public SlidingTabLayout(Context context, AttributeSet attrs) {
87
        this(context, attrs, 0);
88
    }
89
 
90
    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
91
        super(context, attrs, defStyle);
92
 
93
        // Disable the Scroll Bar
94
        setHorizontalScrollBarEnabled(false);
95
        // Make sure that the Tab Strips fills this View
96
        setFillViewport(true);
97
 
98
        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
99
 
100
        mTabStrip = new SlidingTabStrip(context);
101
        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
102
    }
103
 
104
    /**
105
     * Set the custom {@link TabColorizer} to be used.
106
     *
107
     * If you only require simple custmisation then you can use
108
     * {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
109
     * similar effects.
110
     */
111
    public void setCustomTabColorizer(TabColorizer tabColorizer) {
112
        mTabStrip.setCustomTabColorizer(tabColorizer);
113
    }
114
 
115
    /**
116
     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
117
     * circular array. Providing one color will mean that all tabs are indicated with the same color.
118
     */
119
    public void setSelectedIndicatorColors(int... colors) {
120
        mTabStrip.setSelectedIndicatorColors(colors);
121
    }
122
 
123
    /**
124
     * Sets the colors to be used for tab dividers. These colors are treated as a circular array.
125
     * Providing one color will mean that all tabs are indicated with the same color.
126
     */
127
    public void setDividerColors(int... colors) {
128
        mTabStrip.setDividerColors(colors);
129
    }
130
 
131
    /**
132
     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
133
     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
134
     * that the layout can update it's scroll position correctly.
135
     *
136
     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
137
     */
138
    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
139
        mViewPagerPageChangeListener = listener;
140
    }
141
 
142
    /**
143
     * Set the custom layout to be inflated for the tab views.
144
     *
145
     * @param layoutResId Layout id to be inflated
146
     * @param textViewId id of the {@link TextView} in the inflated view
147
     */
148
    public void setCustomTabView(int layoutResId, int textViewId) {
149
        mTabViewLayoutId = layoutResId;
150
        mTabViewTextViewId = textViewId;
151
    }
152
 
153
    /**
154
     * Sets the associated view pager. Note that the assumption here is that the pager content
155
     * (number of tabs and tab titles) does not change after this call has been made.
156
     */
157
    public void setViewPager(ViewPager viewPager) {
158
        mTabStrip.removeAllViews();
159
 
160
        mViewPager = viewPager;
161
        if (viewPager != null) {
162
            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
163
            populateTabStrip();
164
        }
165
    }
166
 
167
    /**
168
     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
169
     * {@link #setCustomTabView(int, int)}.
170
     */
171
    protected TextView createDefaultTabView(Context context) {
172
        TextView textView = new TextView(context);
173
        textView.setGravity(Gravity.CENTER);
174
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
175
        textView.setTypeface(Typeface.DEFAULT_BOLD);
176
 
177
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
178
            // If we're running on Honeycomb or newer, then we can use the Theme's
179
            // selectableItemBackground to ensure that the View has a pressed state
180
            TypedValue outValue = new TypedValue();
181
            getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
182
                    outValue, true);
183
            textView.setBackgroundResource(outValue.resourceId);
184
        }
185
 
186
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
187
            // If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
188
            textView.setAllCaps(true);
189
        }
190
 
191
        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
192
        textView.setPadding(padding, padding, padding, padding);
193
 
194
        return textView;
195
    }
196
 
197
    private void populateTabStrip() {
198
        final PagerAdapter adapter = mViewPager.getAdapter();
199
        final View.OnClickListener tabClickListener = new TabClickListener();
200
 
201
        for (int i = 0; i < adapter.getCount(); i++) {
202
            View tabView = null;
203
            TextView tabTitleView = null;
204
 
205
            if (mTabViewLayoutId != 0) {
206
                // If there is a custom tab view layout id set, try and inflate it
207
                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
208
                        false);
209
                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
210
            }
211
 
212
            if (tabView == null) {
213
                tabView = createDefaultTabView(getContext());
214
            }
215
 
216
            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
217
                tabTitleView = (TextView) tabView;
218
            }
219
 
220
            tabTitleView.setText(adapter.getPageTitle(i));
221
            tabView.setOnClickListener(tabClickListener);
222
 
223
            mTabStrip.addView(tabView);
224
        }
225
    }
226
 
227
    @Override
228
    protected void onAttachedToWindow() {
229
        super.onAttachedToWindow();
230
 
231
        if (mViewPager != null) {
232
            scrollToTab(mViewPager.getCurrentItem(), 0);
233
        }
234
    }
235
 
236
    private void scrollToTab(int tabIndex, int positionOffset) {
237
        final int tabStripChildCount = mTabStrip.getChildCount();
238
        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
239
            return;
240
        }
241
 
242
        View selectedChild = mTabStrip.getChildAt(tabIndex);
243
        if (selectedChild != null) {
244
            int targetScrollX = selectedChild.getLeft() + positionOffset;
245
 
246
            if (tabIndex > 0 || positionOffset > 0) {
247
                // If we're not at the first child and are mid-scroll, make sure we obey the offset
248
                targetScrollX -= mTitleOffset;
249
            }
250
 
251
            scrollTo(targetScrollX, 0);
252
        }
253
    }
254
 
255
    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
256
        private int mScrollState;
257
 
258
        @Override
259
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
260
            int tabStripChildCount = mTabStrip.getChildCount();
261
            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
262
                return;
263
            }
264
 
265
            mTabStrip.onViewPagerPageChanged(position, positionOffset);
266
 
267
            View selectedTitle = mTabStrip.getChildAt(position);
268
            int extraOffset = (selectedTitle != null)
269
                    ? (int) (positionOffset * selectedTitle.getWidth())
270
                    : 0;
271
            scrollToTab(position, extraOffset);
272
 
273
            if (mViewPagerPageChangeListener != null) {
274
                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
275
                        positionOffsetPixels);
276
            }
277
        }
278
 
279
        @Override
280
        public void onPageScrollStateChanged(int state) {
281
            mScrollState = state;
282
 
283
            if (mViewPagerPageChangeListener != null) {
284
                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
285
            }
286
        }
287
 
288
        @Override
289
        public void onPageSelected(int position) {
290
            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
291
                mTabStrip.onViewPagerPageChanged(position, 0f);
292
                scrollToTab(position, 0);
293
            }
294
 
295
            if (mViewPagerPageChangeListener != null) {
296
                mViewPagerPageChangeListener.onPageSelected(position);
297
            }
298
        }
299
 
300
    }
301
 
302
    private class TabClickListener implements View.OnClickListener {
303
        @Override
304
        public void onClick(View v) {
305
            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
306
                if (v == mTabStrip.getChildAt(i)) {
307
                    mViewPager.setCurrentItem(i);
308
                    return;
309
                }
310
            }
311
        }
312
    }
313
 
314
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.