blob: 8fb94bc8fefb526c897a9215cbf43b3fa6d5c870 [file] [log] [blame]
Alexandre Lisione5b66022013-10-30 11:34:15 -04001package org.sflphone.views;
2
3/*
4 * Copyright (C) 2013 Andreas Stuetz <andreas.stuetz@gmail.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19import java.util.Locale;
20
21import org.sflphone.R;
22
23import android.annotation.SuppressLint;
24import android.content.Context;
25import android.content.res.TypedArray;
26import android.graphics.Canvas;
27import android.graphics.Paint;
28import android.graphics.Paint.Style;
29import android.graphics.Typeface;
30import android.os.Build;
31import android.os.Parcel;
32import android.os.Parcelable;
33import android.support.v4.view.ViewPager;
34import android.support.v4.view.ViewPager.OnPageChangeListener;
35import android.util.AttributeSet;
36import android.util.DisplayMetrics;
37import android.util.TypedValue;
38import android.view.Gravity;
39import android.view.View;
40import android.view.ViewTreeObserver.OnGlobalLayoutListener;
41import android.widget.HorizontalScrollView;
42import android.widget.ImageButton;
43import android.widget.LinearLayout;
44import android.widget.TextView;
45
46public class PagerSlidingTabStrip extends HorizontalScrollView {
47
48 public interface IconTabProvider {
49 public int getPageIconResId(int position);
50 }
51
52 // @formatter:off
53 private static final int[] ATTRS = new int[] { android.R.attr.textSize, android.R.attr.textColor };
54 // @formatter:on
55
56 private LinearLayout.LayoutParams defaultTabLayoutParams;
57 private LinearLayout.LayoutParams expandedTabLayoutParams;
58
59 private final PageListener pageListener = new PageListener();
60 public OnPageChangeListener delegatePageListener;
61
62 private LinearLayout tabsContainer;
63 private ViewPager pager;
64
65 private int tabCount;
66
67 private int currentPosition = 0;
68 private float currentPositionOffset = 0f;
69
70 private Paint rectPaint;
71 private Paint dividerPaint;
72
73 private boolean checkedTabWidths = false;
74
75 private int indicatorColor = 0xFF666666;
76 private int underlineColor = 0x1A000000;
77 private int dividerColor = 0x1A000000;
78
79 private boolean shouldExpand = false;
80 private boolean textAllCaps = true;
81
82 private int scrollOffset = 52;
83 private int indicatorHeight = 8;
84 private int underlineHeight = 2;
85 private int dividerPadding = 12;
86 private int tabPadding = 24;
87 private int dividerWidth = 1;
88
89 private int tabTextSize = 12;
90 private int tabTextColor = 0xFF666666;
91 private Typeface tabTypeface = null;
92 private int tabTypefaceStyle = Typeface.BOLD;
93
94 private int lastScrollX = 0;
95
96 private int tabBackgroundResId = R.drawable.background_tabs;
97
98 private Locale locale;
99
100 public PagerSlidingTabStrip(Context context) {
101 this(context, null);
102 }
103
104 public PagerSlidingTabStrip(Context context, AttributeSet attrs) {
105 this(context, attrs, 0);
106 }
107
108 public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) {
109 super(context, attrs, defStyle);
110
111 setFillViewport(true);
112 setWillNotDraw(false);
113
114 tabsContainer = new LinearLayout(context);
115 tabsContainer.setOrientation(LinearLayout.HORIZONTAL);
116 tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
117 addView(tabsContainer);
118
119 DisplayMetrics dm = getResources().getDisplayMetrics();
120
121 scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm);
122 indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm);
123 underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm);
124 dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm);
125 tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm);
126 dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm);
127 tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm);
128
129 // get system attrs (android:textSize and android:textColor)
130
131 TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
132
133 tabTextSize = a.getDimensionPixelSize(0, tabTextSize);
134 tabTextColor = a.getColor(1, tabTextColor);
135
136 a.recycle();
137
138 // get custom attrs
139
140 a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip);
141
142 indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_indicatorColor, indicatorColor);
143 underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_underlineColor, underlineColor);
144 dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_dividerColor, dividerColor);
145 indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_indicatorHeight, indicatorHeight);
146 underlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_underlineHeight, underlineHeight);
147 dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_dividerPadding, dividerPadding);
148 tabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_tabPaddingLeftRight, tabPadding);
149 tabBackgroundResId = a.getResourceId(R.styleable.PagerSlidingTabStrip_tabBackground, tabBackgroundResId);
150 shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_shouldExpand, shouldExpand);
151 scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_scrollOffset, scrollOffset);
152 textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_textAllCaps, textAllCaps);
153
154 a.recycle();
155
156 rectPaint = new Paint();
157 rectPaint.setAntiAlias(true);
158 rectPaint.setStyle(Style.FILL);
159
160 dividerPaint = new Paint();
161 dividerPaint.setAntiAlias(true);
162 dividerPaint.setStrokeWidth(dividerWidth);
163
164 defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
165 expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);
166
167 if (locale == null) {
168 locale = getResources().getConfiguration().locale;
169 }
170 }
171
172 public void setViewPager(ViewPager pager) {
173 this.pager = pager;
174
175 if (pager.getAdapter() == null) {
176 throw new IllegalStateException("ViewPager does not have adapter instance.");
177 }
178
179 pager.setOnPageChangeListener(pageListener);
180
181 notifyDataSetChanged();
182 }
183
184 public void setOnPageChangeListener(OnPageChangeListener listener) {
185 this.delegatePageListener = listener;
186 }
187
188 public void notifyDataSetChanged() {
189
190 tabsContainer.removeAllViews();
191
192 tabCount = pager.getAdapter().getCount();
193
194 for (int i = 0; i < tabCount; i++) {
195
196 if (pager.getAdapter() instanceof IconTabProvider) {
197 addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i));
198 } else {
199 addTextTab(i, pager.getAdapter().getPageTitle(i).toString());
200 }
201
202 }
203
204 updateTabStyles();
205
206 checkedTabWidths = false;
207
208 getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
209
210 @SuppressWarnings("deprecation")
211 @SuppressLint("NewApi")
212 @Override
213 public void onGlobalLayout() {
214
215 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
216 getViewTreeObserver().removeGlobalOnLayoutListener(this);
217 } else {
218 getViewTreeObserver().removeOnGlobalLayoutListener(this);
219 }
220
221 currentPosition = pager.getCurrentItem();
222 scrollToChild(currentPosition, 0);
223 }
224 });
225
226 }
227
228 private void addTextTab(final int position, String title) {
229
230 TextView tab = new TextView(getContext());
231 tab.setText(title);
232 tab.setFocusable(true);
233 tab.setGravity(Gravity.CENTER);
234 tab.setSingleLine();
235
236 tab.setOnClickListener(new OnClickListener() {
237 @Override
238 public void onClick(View v) {
239 pager.setCurrentItem(position);
240 }
241 });
242
243 tabsContainer.addView(tab);
244
245 }
246
247 private void addIconTab(final int position, int resId) {
248
249 ImageButton tab = new ImageButton(getContext());
250 tab.setFocusable(true);
251 tab.setImageResource(resId);
252
253 tab.setOnClickListener(new OnClickListener() {
254 @Override
255 public void onClick(View v) {
256 pager.setCurrentItem(position);
257 }
258 });
259
260 tabsContainer.addView(tab);
261
262 }
263
264 private void updateTabStyles() {
265
266 for (int i = 0; i < tabCount; i++) {
267
268 View v = tabsContainer.getChildAt(i);
269
270 v.setLayoutParams(defaultTabLayoutParams);
271 v.setBackgroundResource(tabBackgroundResId);
272 if (shouldExpand) {
273 v.setPadding(0, 0, 0, 0);
274 } else {
275 v.setPadding(tabPadding, 0, tabPadding, 0);
276 }
277
278 if (v instanceof TextView) {
279
280 TextView tab = (TextView) v;
281 tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize);
282 tab.setTypeface(tabTypeface, tabTypefaceStyle);
283 tab.setTextColor(tabTextColor);
284
285 // setAllCaps() is only available from API 14, so the upper case is made manually if we are on a
286 // pre-ICS-build
287 if (textAllCaps) {
288 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
289 tab.setAllCaps(true);
290 } else {
291 tab.setText(tab.getText().toString().toUpperCase(locale));
292 }
293 }
294 }
295 }
296
297 }
298
299 @Override
300 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
301 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
302
303 if (!shouldExpand || MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.UNSPECIFIED) {
304 return;
305 }
306
307 int myWidth = getMeasuredWidth();
308 int childWidth = 0;
309 for (int i = 0; i < tabCount; i++) {
310 childWidth += tabsContainer.getChildAt(i).getMeasuredWidth();
311 }
312
313 if (!checkedTabWidths && childWidth > 0 && myWidth > 0) {
314
315 if (childWidth <= myWidth) {
316 for (int i = 0; i < tabCount; i++) {
317 tabsContainer.getChildAt(i).setLayoutParams(expandedTabLayoutParams);
318 }
319 }
320
321 checkedTabWidths = true;
322 }
323 }
324
325 private void scrollToChild(int position, int offset) {
326
327 if (tabCount == 0) {
328 return;
329 }
330
331 int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset;
332
333 if (position > 0 || offset > 0) {
334 newScrollX -= scrollOffset;
335 }
336
337 if (newScrollX != lastScrollX) {
338 lastScrollX = newScrollX;
339 scrollTo(newScrollX, 0);
340 }
341
342 }
343
344 @Override
345 protected void onDraw(Canvas canvas) {
346 super.onDraw(canvas);
347
348 if (isInEditMode() || tabCount == 0) {
349 return;
350 }
351
352 final int height = getHeight();
353
354 // draw indicator line
355
356 rectPaint.setColor(indicatorColor);
357
358 // default: line below current tab
359 View currentTab = tabsContainer.getChildAt(currentPosition);
360 float lineLeft = currentTab.getLeft();
361 float lineRight = currentTab.getRight();
362
363 // if there is an offset, start interpolating left and right coordinates between current and next tab
364 if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {
365
366 View nextTab = tabsContainer.getChildAt(currentPosition + 1);
367 final float nextTabLeft = nextTab.getLeft();
368 final float nextTabRight = nextTab.getRight();
369
370 lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft);
371 lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight);
372 }
373
374 canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint);
375
376 // draw underline
377
378 rectPaint.setColor(underlineColor);
379 canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);
380
381 // draw divider
382
383 dividerPaint.setColor(dividerColor);
384 for (int i = 0; i < tabCount - 1; i++) {
385 View tab = tabsContainer.getChildAt(i);
386 canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint);
387 }
388 }
389
390 private class PageListener implements OnPageChangeListener {
391
392 @Override
393 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
394
395 currentPosition = position;
396 currentPositionOffset = positionOffset;
397
398 scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));
399
400 invalidate();
401
402 if (delegatePageListener != null) {
403 delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
404 }
405 }
406
407 @Override
408 public void onPageScrollStateChanged(int state) {
409 if (state == ViewPager.SCROLL_STATE_IDLE) {
410 scrollToChild(pager.getCurrentItem(), 0);
411 }
412
413 if (delegatePageListener != null) {
414 delegatePageListener.onPageScrollStateChanged(state);
415 }
416 }
417
418 @Override
419 public void onPageSelected(int position) {
420 if (delegatePageListener != null) {
421 delegatePageListener.onPageSelected(position);
422 }
423 }
424
425 }
426
427 public void setIndicatorColor(int indicatorColor) {
428 this.indicatorColor = indicatorColor;
429 invalidate();
430 }
431
432 public void setIndicatorColorResource(int resId) {
433 this.indicatorColor = getResources().getColor(resId);
434 invalidate();
435 }
436
437 public int getIndicatorColor() {
438 return this.indicatorColor;
439 }
440
441 public void setIndicatorHeight(int indicatorLineHeightPx) {
442 this.indicatorHeight = indicatorLineHeightPx;
443 invalidate();
444 }
445
446 public int getIndicatorHeight() {
447 return indicatorHeight;
448 }
449
450 public void setUnderlineColor(int underlineColor) {
451 this.underlineColor = underlineColor;
452 invalidate();
453 }
454
455 public void setUnderlineColorResource(int resId) {
456 this.underlineColor = getResources().getColor(resId);
457 invalidate();
458 }
459
460 public int getUnderlineColor() {
461 return underlineColor;
462 }
463
464 public void setDividerColor(int dividerColor) {
465 this.dividerColor = dividerColor;
466 invalidate();
467 }
468
469 public void setDividerColorResource(int resId) {
470 this.dividerColor = getResources().getColor(resId);
471 invalidate();
472 }
473
474 public int getDividerColor() {
475 return dividerColor;
476 }
477
478 public void setUnderlineHeight(int underlineHeightPx) {
479 this.underlineHeight = underlineHeightPx;
480 invalidate();
481 }
482
483 public int getUnderlineHeight() {
484 return underlineHeight;
485 }
486
487 public void setDividerPadding(int dividerPaddingPx) {
488 this.dividerPadding = dividerPaddingPx;
489 invalidate();
490 }
491
492 public int getDividerPadding() {
493 return dividerPadding;
494 }
495
496 public void setScrollOffset(int scrollOffsetPx) {
497 this.scrollOffset = scrollOffsetPx;
498 invalidate();
499 }
500
501 public int getScrollOffset() {
502 return scrollOffset;
503 }
504
505 public void setShouldExpand(boolean shouldExpand) {
506 this.shouldExpand = shouldExpand;
507 requestLayout();
508 }
509
510 public boolean getShouldExpand() {
511 return shouldExpand;
512 }
513
514 public boolean isTextAllCaps() {
515 return textAllCaps;
516 }
517
518 public void setAllCaps(boolean textAllCaps) {
519 this.textAllCaps = textAllCaps;
520 }
521
522 public void setTextSize(int textSizePx) {
523 this.tabTextSize = textSizePx;
524 updateTabStyles();
525 }
526
527 public int getTextSize() {
528 return tabTextSize;
529 }
530
531 public void setTextColor(int textColor) {
532 this.tabTextColor = textColor;
533 updateTabStyles();
534 }
535
536 public void setTextColorResource(int resId) {
537 this.tabTextColor = getResources().getColor(resId);
538 updateTabStyles();
539 }
540
541 public int getTextColor() {
542 return tabTextColor;
543 }
544
545 public void setTypeface(Typeface typeface, int style) {
546 this.tabTypeface = typeface;
547 this.tabTypefaceStyle = style;
548 updateTabStyles();
549 }
550
551 public void setTabBackground(int resId) {
552 this.tabBackgroundResId = resId;
553 }
554
555 public int getTabBackground() {
556 return tabBackgroundResId;
557 }
558
559 public void setTabPaddingLeftRight(int paddingPx) {
560 this.tabPadding = paddingPx;
561 updateTabStyles();
562 }
563
564 public int getTabPaddingLeftRight() {
565 return tabPadding;
566 }
567
568 @Override
569 public void onRestoreInstanceState(Parcelable state) {
570 SavedState savedState = (SavedState) state;
571 super.onRestoreInstanceState(savedState.getSuperState());
572 currentPosition = savedState.currentPosition;
573 requestLayout();
574 }
575
576 @Override
577 public Parcelable onSaveInstanceState() {
578 Parcelable superState = super.onSaveInstanceState();
579 SavedState savedState = new SavedState(superState);
580 savedState.currentPosition = currentPosition;
581 return savedState;
582 }
583
584 static class SavedState extends BaseSavedState {
585 int currentPosition;
586
587 public SavedState(Parcelable superState) {
588 super(superState);
589 }
590
591 private SavedState(Parcel in) {
592 super(in);
593 currentPosition = in.readInt();
594 }
595
596 @Override
597 public void writeToParcel(Parcel dest, int flags) {
598 super.writeToParcel(dest, flags);
599 dest.writeInt(currentPosition);
600 }
601
602 public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
603 @Override
604 public SavedState createFromParcel(Parcel in) {
605 return new SavedState(in);
606 }
607
608 @Override
609 public SavedState[] newArray(int size) {
610 return new SavedState[size];
611 }
612 };
613 }
614
615}