mirror of
https://github.com/MatomoCamp/matomocamp-companion-android.git
synced 2024-09-19 16:13:46 +02:00
Replaced PagerSlidingTabStrip with a slightly improved version of
Android's example SlidingTabLayout.
This commit is contained in:
parent
831539359d
commit
b6e6ecf027
13 changed files with 543 additions and 619 deletions
|
@ -27,7 +27,6 @@ gradle build
|
||||||
## Used libraries
|
## Used libraries
|
||||||
|
|
||||||
* [Android Support Library](http://developer.android.com/tools/support-library/) by The Android Open Source Project
|
* [Android Support Library](http://developer.android.com/tools/support-library/) by The Android Open Source Project
|
||||||
* [PagerSlidingTabStrip](https://github.com/astuetz/PagerSlidingTabStrip) by Andreas Stuetz
|
|
||||||
* [ViewPagerIndicator](http://viewpagerindicator.com/) by Jake Wharton
|
* [ViewPagerIndicator](http://viewpagerindicator.com/) by Jake Wharton
|
||||||
* [PhotoView](https://github.com/chrisbanes/PhotoView) by Chris Banes
|
* [PhotoView](https://github.com/chrisbanes/PhotoView) by Chris Banes
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="@android:integer/config_shortAnimTime">
|
|
||||||
|
|
||||||
<item android:drawable="@color/tab_pressed_fosdem" android:state_pressed="true"/>
|
|
||||||
<item android:drawable="@color/tab_pressed_fosdem" android:state_focused="true"/>
|
|
||||||
<item android:drawable="@android:color/transparent"/>
|
|
||||||
|
|
||||||
</selector>
|
|
|
@ -5,11 +5,10 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical" >
|
android:orientation="vertical" >
|
||||||
|
|
||||||
<com.astuetz.PagerSlidingTabStrip
|
<com.example.android.common.view.SlidingTabLayout
|
||||||
android:id="@+id/indicator"
|
android:id="@+id/sliding_tabs"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
android:layout_height="wrap_content" />
|
||||||
app:pstsIndicatorColor="@color/fosdem_purple" />
|
|
||||||
|
|
||||||
<android.support.v4.view.ViewPager
|
<android.support.v4.view.ViewPager
|
||||||
android:id="@+id/pager"
|
android:id="@+id/pager"
|
||||||
|
|
|
@ -11,11 +11,10 @@
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:visibility="visible" >
|
android:visibility="visible" >
|
||||||
|
|
||||||
<com.astuetz.PagerSlidingTabStrip
|
<com.example.android.common.view.SlidingTabLayout
|
||||||
android:id="@+id/indicator"
|
android:id="@+id/sliding_tabs"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
android:layout_height="wrap_content" />
|
||||||
app:pstsIndicatorColor="@color/fosdem_purple" />
|
|
||||||
|
|
||||||
<android.support.v4.view.ViewPager
|
<android.support.v4.view.ViewPager
|
||||||
android:id="@+id/pager"
|
android:id="@+id/pager"
|
||||||
|
|
|
@ -4,18 +4,4 @@
|
||||||
<!-- Drawable used as a background for activated items. -->
|
<!-- Drawable used as a background for activated items. -->
|
||||||
<attr name="activatedBackgroundIndicator" format="reference" />
|
<attr name="activatedBackgroundIndicator" format="reference" />
|
||||||
|
|
||||||
<declare-styleable name="PagerSlidingTabStrip">
|
|
||||||
<attr name="pstsIndicatorColor" format="color" />
|
|
||||||
<attr name="pstsUnderlineColor" format="color" />
|
|
||||||
<attr name="pstsDividerColor" format="color" />
|
|
||||||
<attr name="pstsIndicatorHeight" format="dimension" />
|
|
||||||
<attr name="pstsUnderlineHeight" format="dimension" />
|
|
||||||
<attr name="pstsDividerPadding" format="dimension" />
|
|
||||||
<attr name="pstsTabPaddingLeftRight" format="dimension" />
|
|
||||||
<attr name="pstsScrollOffset" format="dimension" />
|
|
||||||
<attr name="pstsTabBackground" format="reference" />
|
|
||||||
<attr name="pstsShouldExpand" format="boolean" />
|
|
||||||
<attr name="pstsTextAllCaps" format="boolean" />
|
|
||||||
</declare-styleable>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -2,7 +2,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<color name="fosdem_purple">#a91991</color>
|
<color name="fosdem_purple">#a91991</color>
|
||||||
<color name="tab_pressed_fosdem">#80a91991</color>
|
|
||||||
<color name="main_menu_background">#f0fafafa</color>
|
<color name="main_menu_background">#f0fafafa</color>
|
||||||
<color name="translucent_grey">#29000000</color>
|
<color name="translucent_grey">#29000000</color>
|
||||||
<color name="schedule_time_background">#29000000</color>
|
<color name="schedule_time_background">#29000000</color>
|
||||||
|
|
|
@ -112,6 +112,6 @@
|
||||||
\nThe name FOSDEM and the gear logo are registered trademarks of FOSDEM VZW. Used with permission.
|
\nThe name FOSDEM and the gear logo are registered trademarks of FOSDEM VZW. Used with permission.
|
||||||
\n
|
\n
|
||||||
\n<small>This application makes use of the following libraries:
|
\n<small>This application makes use of the following libraries:
|
||||||
\n- <a href="http://developer.android.com/tools/support-library/">Android Support Library</a><i> by The Android Open Source Project</i>\n- <a href="https://github.com/astuetz/PagerSlidingTabStrip">PagerSlidingTabStrip</a><i> by Andreas Stuetz</i>\n- <a href="http://viewpagerindicator.com/">ViewPagerIndicator</a><i> by Jake Wharton</i>\n- <a href="https://github.com/chrisbanes/PhotoView">PhotoView</a><i> by Chris Banes</i></small></string>
|
\n- <a href="http://developer.android.com/tools/support-library/">Android Support Library</a><i> by The Android Open Source Project</i>\n- <a href="http://viewpagerindicator.com/">ViewPagerIndicator</a><i> by Jake Wharton</i>\n- <a href="https://github.com/chrisbanes/PhotoView">PhotoView</a><i> by Chris Banes</i></small></string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
|
@ -11,6 +11,7 @@
|
||||||
<item name="android:textViewStyle">@style/TextView.Fosdem</item>
|
<item name="android:textViewStyle">@style/TextView.Fosdem</item>
|
||||||
<item name="android:listViewStyle">@style/ListView.Fosdem</item>
|
<item name="android:listViewStyle">@style/ListView.Fosdem</item>
|
||||||
<item name="activatedBackgroundIndicator">@drawable/activated_background</item>
|
<item name="activatedBackgroundIndicator">@drawable/activated_background</item>
|
||||||
|
<item name="android:selectableItemBackground">@drawable/selectable_background</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="DialogTheme" parent="@android:style/Theme.Dialog" />
|
<style name="DialogTheme" parent="@android:style/Theme.Dialog" />
|
||||||
|
|
|
@ -11,7 +11,7 @@ import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import be.digitalia.fosdem.R;
|
import be.digitalia.fosdem.R;
|
||||||
|
|
||||||
import com.astuetz.PagerSlidingTabStrip;
|
import com.example.android.common.view.SlidingTabLayout;
|
||||||
|
|
||||||
public class LiveFragment extends Fragment {
|
public class LiveFragment extends Fragment {
|
||||||
|
|
||||||
|
@ -29,8 +29,9 @@ public class LiveFragment extends Fragment {
|
||||||
|
|
||||||
ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
|
ViewPager pager = (ViewPager) view.findViewById(R.id.pager);
|
||||||
pager.setAdapter(livePagerAdapter);
|
pager.setAdapter(livePagerAdapter);
|
||||||
PagerSlidingTabStrip indicator = (PagerSlidingTabStrip) view.findViewById(R.id.indicator);
|
SlidingTabLayout slidingTabs = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
|
||||||
indicator.setViewPager(pager);
|
slidingTabs.setSelectedIndicatorColors(getResources().getColor(R.color.fosdem_purple));
|
||||||
|
slidingTabs.setViewPager(pager);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import be.digitalia.fosdem.db.DatabaseManager;
|
||||||
import be.digitalia.fosdem.loaders.GlobalCacheLoader;
|
import be.digitalia.fosdem.loaders.GlobalCacheLoader;
|
||||||
import be.digitalia.fosdem.model.Day;
|
import be.digitalia.fosdem.model.Day;
|
||||||
|
|
||||||
import com.astuetz.PagerSlidingTabStrip;
|
import com.example.android.common.view.SlidingTabLayout;
|
||||||
|
|
||||||
public class TracksFragment extends Fragment implements LoaderCallbacks<List<Day>> {
|
public class TracksFragment extends Fragment implements LoaderCallbacks<List<Day>> {
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ public class TracksFragment extends Fragment implements LoaderCallbacks<List<Day
|
||||||
View contentView;
|
View contentView;
|
||||||
View emptyView;
|
View emptyView;
|
||||||
ViewPager pager;
|
ViewPager pager;
|
||||||
PagerSlidingTabStrip indicator;
|
SlidingTabLayout slidingTabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final int DAYS_LOADER_ID = 1;
|
private static final int DAYS_LOADER_ID = 1;
|
||||||
|
@ -60,7 +60,8 @@ public class TracksFragment extends Fragment implements LoaderCallbacks<List<Day
|
||||||
holder.contentView = view.findViewById(R.id.content);
|
holder.contentView = view.findViewById(R.id.content);
|
||||||
holder.emptyView = view.findViewById(android.R.id.empty);
|
holder.emptyView = view.findViewById(android.R.id.empty);
|
||||||
holder.pager = (ViewPager) view.findViewById(R.id.pager);
|
holder.pager = (ViewPager) view.findViewById(R.id.pager);
|
||||||
holder.indicator = (PagerSlidingTabStrip) view.findViewById(R.id.indicator);
|
holder.slidingTabs = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
|
||||||
|
holder.slidingTabs.setSelectedIndicatorColors(getResources().getColor(R.color.fosdem_purple));
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
@ -102,7 +103,8 @@ public class TracksFragment extends Fragment implements LoaderCallbacks<List<Day
|
||||||
public DaysLoader(Context context) {
|
public DaysLoader(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
// Reload days list when the schedule has been refreshed
|
// Reload days list when the schedule has been refreshed
|
||||||
LocalBroadcastManager.getInstance(context).registerReceiver(scheduleRefreshedReceiver, new IntentFilter(DatabaseManager.ACTION_SCHEDULE_REFRESHED));
|
LocalBroadcastManager.getInstance(context).registerReceiver(scheduleRefreshedReceiver,
|
||||||
|
new IntentFilter(DatabaseManager.ACTION_SCHEDULE_REFRESHED));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -142,7 +144,7 @@ public class TracksFragment extends Fragment implements LoaderCallbacks<List<Day
|
||||||
if (holder.pager.getAdapter() == null) {
|
if (holder.pager.getAdapter() == null) {
|
||||||
holder.pager.setAdapter(daysAdapter);
|
holder.pager.setAdapter(daysAdapter);
|
||||||
}
|
}
|
||||||
holder.indicator.setViewPager(holder.pager);
|
holder.slidingTabs.setViewPager(holder.pager);
|
||||||
if (savedCurrentPage != -1) {
|
if (savedCurrentPage != -1) {
|
||||||
holder.pager.setCurrentItem(Math.min(savedCurrentPage, totalPages - 1), false);
|
holder.pager.setCurrentItem(Math.min(savedCurrentPage, totalPages - 1), false);
|
||||||
savedCurrentPage = -1;
|
savedCurrentPage = -1;
|
||||||
|
|
|
@ -1,578 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 Andreas Stuetz <andreas.stuetz@gmail.com>
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.astuetz;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Paint.Style;
|
|
||||||
import android.graphics.Typeface;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.support.v4.view.ViewPager;
|
|
||||||
import android.support.v4.view.ViewPager.OnPageChangeListener;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.DisplayMetrics;
|
|
||||||
import android.util.TypedValue;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
|
||||||
import android.widget.HorizontalScrollView;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import be.digitalia.fosdem.R;
|
|
||||||
|
|
||||||
public class PagerSlidingTabStrip extends HorizontalScrollView {
|
|
||||||
|
|
||||||
public interface IconTabProvider {
|
|
||||||
public int getPageIconResId(int position);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @formatter:off
|
|
||||||
private static final int[] ATTRS = new int[] {
|
|
||||||
android.R.attr.textSize,
|
|
||||||
android.R.attr.textColor
|
|
||||||
};
|
|
||||||
// @formatter:on
|
|
||||||
|
|
||||||
private LinearLayout.LayoutParams defaultTabLayoutParams;
|
|
||||||
private LinearLayout.LayoutParams expandedTabLayoutParams;
|
|
||||||
|
|
||||||
private final PageListener pageListener = new PageListener();
|
|
||||||
public OnPageChangeListener delegatePageListener;
|
|
||||||
|
|
||||||
private LinearLayout tabsContainer;
|
|
||||||
private ViewPager pager;
|
|
||||||
|
|
||||||
private int tabCount;
|
|
||||||
|
|
||||||
private int currentPosition = 0;
|
|
||||||
private float currentPositionOffset = 0f;
|
|
||||||
|
|
||||||
private Paint rectPaint;
|
|
||||||
private Paint dividerPaint;
|
|
||||||
|
|
||||||
private int indicatorColor = 0xFF666666;
|
|
||||||
private int underlineColor = 0x1A000000;
|
|
||||||
private int dividerColor = 0x1A000000;
|
|
||||||
|
|
||||||
private boolean shouldExpand = false;
|
|
||||||
private boolean textAllCaps = true;
|
|
||||||
|
|
||||||
private int scrollOffset = 52;
|
|
||||||
private int indicatorHeight = 8;
|
|
||||||
private int underlineHeight = 2;
|
|
||||||
private int dividerPadding = 12;
|
|
||||||
private int tabPadding = 24;
|
|
||||||
private int dividerWidth = 1;
|
|
||||||
|
|
||||||
private int tabTextSize = 12;
|
|
||||||
private int tabTextColor = 0xFF666666;
|
|
||||||
private Typeface tabTypeface = null;
|
|
||||||
private int tabTypefaceStyle = Typeface.BOLD;
|
|
||||||
|
|
||||||
private int lastScrollX = 0;
|
|
||||||
|
|
||||||
private int tabBackgroundResId = R.drawable.background_tab;
|
|
||||||
|
|
||||||
private Locale locale;
|
|
||||||
|
|
||||||
public PagerSlidingTabStrip(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PagerSlidingTabStrip(Context context, AttributeSet attrs) {
|
|
||||||
this(context, attrs, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PagerSlidingTabStrip(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
|
|
||||||
setFillViewport(true);
|
|
||||||
setWillNotDraw(false);
|
|
||||||
|
|
||||||
tabsContainer = new LinearLayout(context);
|
|
||||||
tabsContainer.setOrientation(LinearLayout.HORIZONTAL);
|
|
||||||
tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
|
||||||
addView(tabsContainer);
|
|
||||||
|
|
||||||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
|
||||||
|
|
||||||
scrollOffset = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, scrollOffset, dm);
|
|
||||||
indicatorHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, indicatorHeight, dm);
|
|
||||||
underlineHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, underlineHeight, dm);
|
|
||||||
dividerPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerPadding, dm);
|
|
||||||
tabPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, tabPadding, dm);
|
|
||||||
dividerWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dividerWidth, dm);
|
|
||||||
tabTextSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, tabTextSize, dm);
|
|
||||||
|
|
||||||
// get system attrs (android:textSize and android:textColor)
|
|
||||||
|
|
||||||
TypedArray a = context.obtainStyledAttributes(attrs, ATTRS);
|
|
||||||
|
|
||||||
tabTextSize = a.getDimensionPixelSize(0, tabTextSize);
|
|
||||||
tabTextColor = a.getColor(1, tabTextColor);
|
|
||||||
|
|
||||||
a.recycle();
|
|
||||||
|
|
||||||
// get custom attrs
|
|
||||||
|
|
||||||
a = context.obtainStyledAttributes(attrs, R.styleable.PagerSlidingTabStrip);
|
|
||||||
|
|
||||||
indicatorColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsIndicatorColor, indicatorColor);
|
|
||||||
underlineColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsUnderlineColor, underlineColor);
|
|
||||||
dividerColor = a.getColor(R.styleable.PagerSlidingTabStrip_pstsDividerColor, dividerColor);
|
|
||||||
indicatorHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsIndicatorHeight, indicatorHeight);
|
|
||||||
underlineHeight = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsUnderlineHeight, underlineHeight);
|
|
||||||
dividerPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsDividerPadding, dividerPadding);
|
|
||||||
tabPadding = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsTabPaddingLeftRight, tabPadding);
|
|
||||||
tabBackgroundResId = a.getResourceId(R.styleable.PagerSlidingTabStrip_pstsTabBackground, tabBackgroundResId);
|
|
||||||
shouldExpand = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsShouldExpand, shouldExpand);
|
|
||||||
scrollOffset = a.getDimensionPixelSize(R.styleable.PagerSlidingTabStrip_pstsScrollOffset, scrollOffset);
|
|
||||||
textAllCaps = a.getBoolean(R.styleable.PagerSlidingTabStrip_pstsTextAllCaps, textAllCaps);
|
|
||||||
|
|
||||||
a.recycle();
|
|
||||||
|
|
||||||
rectPaint = new Paint();
|
|
||||||
rectPaint.setAntiAlias(true);
|
|
||||||
rectPaint.setStyle(Style.FILL);
|
|
||||||
|
|
||||||
dividerPaint = new Paint();
|
|
||||||
dividerPaint.setAntiAlias(true);
|
|
||||||
dividerPaint.setStrokeWidth(dividerWidth);
|
|
||||||
|
|
||||||
defaultTabLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
|
|
||||||
expandedTabLayoutParams = new LinearLayout.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f);
|
|
||||||
|
|
||||||
if (locale == null) {
|
|
||||||
locale = getResources().getConfiguration().locale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setViewPager(ViewPager pager) {
|
|
||||||
this.pager = pager;
|
|
||||||
|
|
||||||
if (pager.getAdapter() == null) {
|
|
||||||
throw new IllegalStateException("ViewPager does not have adapter instance.");
|
|
||||||
}
|
|
||||||
|
|
||||||
pager.setOnPageChangeListener(pageListener);
|
|
||||||
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnPageChangeListener(OnPageChangeListener listener) {
|
|
||||||
this.delegatePageListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void notifyDataSetChanged() {
|
|
||||||
|
|
||||||
tabsContainer.removeAllViews();
|
|
||||||
|
|
||||||
tabCount = pager.getAdapter().getCount();
|
|
||||||
|
|
||||||
for (int i = 0; i < tabCount; i++) {
|
|
||||||
|
|
||||||
if (pager.getAdapter() instanceof IconTabProvider) {
|
|
||||||
addIconTab(i, ((IconTabProvider) pager.getAdapter()).getPageIconResId(i));
|
|
||||||
} else {
|
|
||||||
addTextTab(i, pager.getAdapter().getPageTitle(i).toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTabStyles();
|
|
||||||
|
|
||||||
getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
@Override
|
|
||||||
public void onGlobalLayout() {
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
|
||||||
getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
|
||||||
} else {
|
|
||||||
getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentPosition = pager.getCurrentItem();
|
|
||||||
scrollToChild(currentPosition, 0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTextTab(final int position, String title) {
|
|
||||||
|
|
||||||
TextView tab = new TextView(getContext());
|
|
||||||
tab.setText(title);
|
|
||||||
tab.setGravity(Gravity.CENTER);
|
|
||||||
tab.setSingleLine();
|
|
||||||
|
|
||||||
addTab(position, tab);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIconTab(final int position, int resId) {
|
|
||||||
|
|
||||||
ImageButton tab = new ImageButton(getContext());
|
|
||||||
tab.setImageResource(resId);
|
|
||||||
|
|
||||||
addTab(position, tab);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addTab(final int position, View tab) {
|
|
||||||
tab.setFocusable(true);
|
|
||||||
tab.setOnClickListener(new OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
pager.setCurrentItem(position);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tab.setPadding(tabPadding, 0, tabPadding, 0);
|
|
||||||
tabsContainer.addView(tab, position, shouldExpand ? expandedTabLayoutParams : defaultTabLayoutParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTabStyles() {
|
|
||||||
|
|
||||||
for (int i = 0; i < tabCount; i++) {
|
|
||||||
|
|
||||||
View v = tabsContainer.getChildAt(i);
|
|
||||||
|
|
||||||
v.setBackgroundResource(tabBackgroundResId);
|
|
||||||
|
|
||||||
if (v instanceof TextView) {
|
|
||||||
|
|
||||||
TextView tab = (TextView) v;
|
|
||||||
tab.setTextSize(TypedValue.COMPLEX_UNIT_PX, tabTextSize);
|
|
||||||
tab.setTypeface(tabTypeface, tabTypefaceStyle);
|
|
||||||
tab.setTextColor(tabTextColor);
|
|
||||||
|
|
||||||
// setAllCaps() is only available from API 14, so the upper case is made manually if we are on a
|
|
||||||
// pre-ICS-build
|
|
||||||
if (textAllCaps) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
|
||||||
tab.setAllCaps(true);
|
|
||||||
} else {
|
|
||||||
tab.setText(tab.getText().toString().toUpperCase(locale));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scrollToChild(int position, int offset) {
|
|
||||||
|
|
||||||
if (tabCount == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int newScrollX = tabsContainer.getChildAt(position).getLeft() + offset;
|
|
||||||
|
|
||||||
if (position > 0 || offset > 0) {
|
|
||||||
newScrollX -= scrollOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newScrollX != lastScrollX) {
|
|
||||||
lastScrollX = newScrollX;
|
|
||||||
scrollTo(newScrollX, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
super.onDraw(canvas);
|
|
||||||
|
|
||||||
if (isInEditMode() || tabCount == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int height = getHeight();
|
|
||||||
|
|
||||||
// draw indicator line
|
|
||||||
|
|
||||||
rectPaint.setColor(indicatorColor);
|
|
||||||
|
|
||||||
// default: line below current tab
|
|
||||||
View currentTab = tabsContainer.getChildAt(currentPosition);
|
|
||||||
float lineLeft = currentTab.getLeft();
|
|
||||||
float lineRight = currentTab.getRight();
|
|
||||||
|
|
||||||
// if there is an offset, start interpolating left and right coordinates between current and next tab
|
|
||||||
if (currentPositionOffset > 0f && currentPosition < tabCount - 1) {
|
|
||||||
|
|
||||||
View nextTab = tabsContainer.getChildAt(currentPosition + 1);
|
|
||||||
final float nextTabLeft = nextTab.getLeft();
|
|
||||||
final float nextTabRight = nextTab.getRight();
|
|
||||||
|
|
||||||
lineLeft = (currentPositionOffset * nextTabLeft + (1f - currentPositionOffset) * lineLeft);
|
|
||||||
lineRight = (currentPositionOffset * nextTabRight + (1f - currentPositionOffset) * lineRight);
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.drawRect(lineLeft, height - indicatorHeight, lineRight, height, rectPaint);
|
|
||||||
|
|
||||||
// draw underline
|
|
||||||
|
|
||||||
rectPaint.setColor(underlineColor);
|
|
||||||
canvas.drawRect(0, height - underlineHeight, tabsContainer.getWidth(), height, rectPaint);
|
|
||||||
|
|
||||||
// draw divider
|
|
||||||
|
|
||||||
dividerPaint.setColor(dividerColor);
|
|
||||||
for (int i = 0; i < tabCount - 1; i++) {
|
|
||||||
View tab = tabsContainer.getChildAt(i);
|
|
||||||
canvas.drawLine(tab.getRight(), dividerPadding, tab.getRight(), height - dividerPadding, dividerPaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PageListener implements OnPageChangeListener {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
|
||||||
|
|
||||||
currentPosition = position;
|
|
||||||
currentPositionOffset = positionOffset;
|
|
||||||
|
|
||||||
scrollToChild(position, (int) (positionOffset * tabsContainer.getChildAt(position).getWidth()));
|
|
||||||
|
|
||||||
invalidate();
|
|
||||||
|
|
||||||
if (delegatePageListener != null) {
|
|
||||||
delegatePageListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageScrollStateChanged(int state) {
|
|
||||||
if (state == ViewPager.SCROLL_STATE_IDLE) {
|
|
||||||
scrollToChild(pager.getCurrentItem(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delegatePageListener != null) {
|
|
||||||
delegatePageListener.onPageScrollStateChanged(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPageSelected(int position) {
|
|
||||||
if (delegatePageListener != null) {
|
|
||||||
delegatePageListener.onPageSelected(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIndicatorColor(int indicatorColor) {
|
|
||||||
this.indicatorColor = indicatorColor;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIndicatorColorResource(int resId) {
|
|
||||||
this.indicatorColor = getResources().getColor(resId);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIndicatorColor() {
|
|
||||||
return this.indicatorColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIndicatorHeight(int indicatorLineHeightPx) {
|
|
||||||
this.indicatorHeight = indicatorLineHeightPx;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIndicatorHeight() {
|
|
||||||
return indicatorHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUnderlineColor(int underlineColor) {
|
|
||||||
this.underlineColor = underlineColor;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUnderlineColorResource(int resId) {
|
|
||||||
this.underlineColor = getResources().getColor(resId);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getUnderlineColor() {
|
|
||||||
return underlineColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDividerColor(int dividerColor) {
|
|
||||||
this.dividerColor = dividerColor;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDividerColorResource(int resId) {
|
|
||||||
this.dividerColor = getResources().getColor(resId);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDividerColor() {
|
|
||||||
return dividerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUnderlineHeight(int underlineHeightPx) {
|
|
||||||
this.underlineHeight = underlineHeightPx;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getUnderlineHeight() {
|
|
||||||
return underlineHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDividerPadding(int dividerPaddingPx) {
|
|
||||||
this.dividerPadding = dividerPaddingPx;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDividerPadding() {
|
|
||||||
return dividerPadding;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScrollOffset(int scrollOffsetPx) {
|
|
||||||
this.scrollOffset = scrollOffsetPx;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getScrollOffset() {
|
|
||||||
return scrollOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShouldExpand(boolean shouldExpand) {
|
|
||||||
this.shouldExpand = shouldExpand;
|
|
||||||
requestLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getShouldExpand() {
|
|
||||||
return shouldExpand;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTextAllCaps() {
|
|
||||||
return textAllCaps;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAllCaps(boolean textAllCaps) {
|
|
||||||
this.textAllCaps = textAllCaps;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTextSize(int textSizePx) {
|
|
||||||
this.tabTextSize = textSizePx;
|
|
||||||
updateTabStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTextSize() {
|
|
||||||
return tabTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTextColor(int textColor) {
|
|
||||||
this.tabTextColor = textColor;
|
|
||||||
updateTabStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTextColorResource(int resId) {
|
|
||||||
this.tabTextColor = getResources().getColor(resId);
|
|
||||||
updateTabStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTextColor() {
|
|
||||||
return tabTextColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTypeface(Typeface typeface, int style) {
|
|
||||||
this.tabTypeface = typeface;
|
|
||||||
this.tabTypefaceStyle = style;
|
|
||||||
updateTabStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTabBackground(int resId) {
|
|
||||||
this.tabBackgroundResId = resId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTabBackground() {
|
|
||||||
return tabBackgroundResId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTabPaddingLeftRight(int paddingPx) {
|
|
||||||
this.tabPadding = paddingPx;
|
|
||||||
updateTabStyles();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTabPaddingLeftRight() {
|
|
||||||
return tabPadding;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRestoreInstanceState(Parcelable state) {
|
|
||||||
SavedState savedState = (SavedState) state;
|
|
||||||
super.onRestoreInstanceState(savedState.getSuperState());
|
|
||||||
currentPosition = savedState.currentPosition;
|
|
||||||
requestLayout();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Parcelable onSaveInstanceState() {
|
|
||||||
Parcelable superState = super.onSaveInstanceState();
|
|
||||||
SavedState savedState = new SavedState(superState);
|
|
||||||
savedState.currentPosition = currentPosition;
|
|
||||||
return savedState;
|
|
||||||
}
|
|
||||||
|
|
||||||
static class SavedState extends BaseSavedState {
|
|
||||||
int currentPosition;
|
|
||||||
|
|
||||||
public SavedState(Parcelable superState) {
|
|
||||||
super(superState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SavedState(Parcel in) {
|
|
||||||
super(in);
|
|
||||||
currentPosition = in.readInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
super.writeToParcel(dest, flags);
|
|
||||||
dest.writeInt(currentPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
|
|
||||||
@Override
|
|
||||||
public SavedState createFromParcel(Parcel in) {
|
|
||||||
return new SavedState(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SavedState[] newArray(int size) {
|
|
||||||
return new SavedState[size];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
324
src/com/example/android/common/view/SlidingTabLayout.java
Normal file
324
src/com/example/android/common/view/SlidingTabLayout.java
Normal file
|
@ -0,0 +1,324 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.common.view;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.support.v4.view.PagerAdapter;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be used with ViewPager to provide a tab indicator component which give constant feedback as to
|
||||||
|
* the user's scroll progress.
|
||||||
|
* <p>
|
||||||
|
* To use the component, simply add it to your view hierarchy. Then in your
|
||||||
|
* {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call
|
||||||
|
* {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for.
|
||||||
|
* <p>
|
||||||
|
* The colors can be customized in two ways. The first and simplest is to provide an array of colors
|
||||||
|
* via {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)}. The
|
||||||
|
* alternative is via the {@link TabColorizer} interface which provides you complete control over
|
||||||
|
* which color is used for any individual position.
|
||||||
|
* <p>
|
||||||
|
* The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)},
|
||||||
|
* providing the layout ID of your custom layout.
|
||||||
|
*/
|
||||||
|
public class SlidingTabLayout extends HorizontalScrollView {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows complete control over the colors drawn in the tab layout. Set with
|
||||||
|
* {@link #setCustomTabColorizer(TabColorizer)}.
|
||||||
|
*/
|
||||||
|
public interface TabColorizer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return return the color of the indicator used when {@code position} is selected.
|
||||||
|
*/
|
||||||
|
int getIndicatorColor(int position);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return return the color of the divider drawn to the right of {@code position}.
|
||||||
|
*/
|
||||||
|
int getDividerColor(int position);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int TITLE_OFFSET_DIPS = 24;
|
||||||
|
private static final int TAB_VIEW_HORIZONTAL_PADDING_DIPS = 24;
|
||||||
|
private static final int TAB_VIEW_VERTICAL_PADDING_DIPS = 16;
|
||||||
|
private static final int TAB_VIEW_TEXT_SIZE_SP = 12;
|
||||||
|
|
||||||
|
private int mTitleOffset;
|
||||||
|
|
||||||
|
private int mTabViewLayoutId;
|
||||||
|
private int mTabViewTextViewId;
|
||||||
|
|
||||||
|
private ViewPager mViewPager;
|
||||||
|
private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;
|
||||||
|
|
||||||
|
private final SlidingTabStrip mTabStrip;
|
||||||
|
|
||||||
|
public SlidingTabLayout(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SlidingTabLayout(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
|
||||||
|
// Disable the Scroll Bar
|
||||||
|
setHorizontalScrollBarEnabled(false);
|
||||||
|
// Make sure that the Tab Strips fills this View
|
||||||
|
setFillViewport(true);
|
||||||
|
|
||||||
|
mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);
|
||||||
|
|
||||||
|
mTabStrip = new SlidingTabStrip(context);
|
||||||
|
addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the custom {@link TabColorizer} to be used.
|
||||||
|
*
|
||||||
|
* If you only require simple custmisation then you can use
|
||||||
|
* {@link #setSelectedIndicatorColors(int...)} and {@link #setDividerColors(int...)} to achieve
|
||||||
|
* similar effects.
|
||||||
|
*/
|
||||||
|
public void setCustomTabColorizer(TabColorizer tabColorizer) {
|
||||||
|
mTabStrip.setCustomTabColorizer(tabColorizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the colors to be used for indicating the selected tab. These colors are treated as a
|
||||||
|
* circular array. Providing one color will mean that all tabs are indicated with the same
|
||||||
|
* color.
|
||||||
|
*/
|
||||||
|
public void setSelectedIndicatorColors(int... colors) {
|
||||||
|
mTabStrip.setSelectedIndicatorColors(colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the colors to be used for tab dividers. These colors are treated as a circular array.
|
||||||
|
* Providing one color will mean that all tabs are indicated with the same color.
|
||||||
|
*/
|
||||||
|
public void setDividerColors(int... colors) {
|
||||||
|
mTabStrip.setDividerColors(colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
|
||||||
|
* required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
|
||||||
|
* that the layout can update it's scroll position correctly.
|
||||||
|
*
|
||||||
|
* @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
|
||||||
|
*/
|
||||||
|
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
|
||||||
|
mViewPagerPageChangeListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the custom layout to be inflated for the tab views.
|
||||||
|
*
|
||||||
|
* @param layoutResId
|
||||||
|
* Layout id to be inflated
|
||||||
|
* @param textViewId
|
||||||
|
* id of the {@link TextView} in the inflated view
|
||||||
|
*/
|
||||||
|
public void setCustomTabView(int layoutResId, int textViewId) {
|
||||||
|
mTabViewLayoutId = layoutResId;
|
||||||
|
mTabViewTextViewId = textViewId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the associated view pager. Note that the assumption here is that the pager content
|
||||||
|
* (number of tabs and tab titles) does not change after this call has been made.
|
||||||
|
*/
|
||||||
|
public void setViewPager(ViewPager viewPager) {
|
||||||
|
mTabStrip.removeAllViews();
|
||||||
|
|
||||||
|
mViewPager = viewPager;
|
||||||
|
if (viewPager != null) {
|
||||||
|
viewPager.setOnPageChangeListener(new InternalViewPagerListener());
|
||||||
|
populateTabStrip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a default view to be used for tabs. This is called if a custom tab view is not set via
|
||||||
|
* {@link #setCustomTabView(int, int)}.
|
||||||
|
*/
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
protected TextView createDefaultTabView(Context context) {
|
||||||
|
TextView textView = new TextView(context);
|
||||||
|
textView.setGravity(Gravity.CENTER);
|
||||||
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP);
|
||||||
|
textView.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||||
|
// If we're running on Honeycomb or newer, then we can use the Theme's
|
||||||
|
// selectableItemBackground to ensure that the View has a pressed state
|
||||||
|
TypedValue outValue = new TypedValue();
|
||||||
|
getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
|
||||||
|
textView.setBackgroundResource(outValue.resourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
|
// If we're running on ICS or newer, enable all-caps to match the Action Bar tab style
|
||||||
|
textView.setAllCaps(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
float density = getResources().getDisplayMetrics().density;
|
||||||
|
int horizontalPadding = (int) (TAB_VIEW_HORIZONTAL_PADDING_DIPS * density);
|
||||||
|
int verticalPadding = (int) (TAB_VIEW_VERTICAL_PADDING_DIPS * density);
|
||||||
|
textView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding);
|
||||||
|
|
||||||
|
return textView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateTabStrip() {
|
||||||
|
final PagerAdapter adapter = mViewPager.getAdapter();
|
||||||
|
final View.OnClickListener tabClickListener = new TabClickListener();
|
||||||
|
|
||||||
|
for (int i = 0; i < adapter.getCount(); i++) {
|
||||||
|
View tabView = null;
|
||||||
|
TextView tabTitleView = null;
|
||||||
|
|
||||||
|
if (mTabViewLayoutId != 0) {
|
||||||
|
// If there is a custom tab view layout id set, try and inflate it
|
||||||
|
tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, false);
|
||||||
|
tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabView == null) {
|
||||||
|
tabView = createDefaultTabView(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tabTitleView == null && TextView.class.isInstance(tabView)) {
|
||||||
|
tabTitleView = (TextView) tabView;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
|
tabTitleView.setText(adapter.getPageTitle(i));
|
||||||
|
} else {
|
||||||
|
// Emulate allCaps
|
||||||
|
tabTitleView.setText(adapter.getPageTitle(i).toString().toUpperCase(Locale.getDefault()));
|
||||||
|
}
|
||||||
|
tabView.setOnClickListener(tabClickListener);
|
||||||
|
|
||||||
|
mTabStrip.addView(tabView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
|
||||||
|
if (mViewPager != null) {
|
||||||
|
scrollToTab(mViewPager.getCurrentItem(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scrollToTab(int tabIndex, int positionOffset) {
|
||||||
|
final int tabStripChildCount = mTabStrip.getChildCount();
|
||||||
|
if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
View selectedChild = mTabStrip.getChildAt(tabIndex);
|
||||||
|
if (selectedChild != null) {
|
||||||
|
int targetScrollX = selectedChild.getLeft() + positionOffset;
|
||||||
|
|
||||||
|
if (tabIndex > 0 || positionOffset > 0) {
|
||||||
|
// If we're not at the first child and are mid-scroll, make sure we obey the offset
|
||||||
|
targetScrollX -= mTitleOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollTo(targetScrollX, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
|
||||||
|
private int mScrollState;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
int tabStripChildCount = mTabStrip.getChildCount();
|
||||||
|
if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mTabStrip.onViewPagerPageChanged(position, positionOffset);
|
||||||
|
|
||||||
|
View selectedTitle = mTabStrip.getChildAt(position);
|
||||||
|
int extraOffset = (selectedTitle != null) ? (int) (positionOffset * selectedTitle.getWidth()) : 0;
|
||||||
|
scrollToTab(position, extraOffset);
|
||||||
|
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
mScrollState = state;
|
||||||
|
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageScrollStateChanged(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
|
||||||
|
mTabStrip.onViewPagerPageChanged(position, 0f);
|
||||||
|
scrollToTab(position, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mViewPagerPageChangeListener != null) {
|
||||||
|
mViewPagerPageChangeListener.onPageSelected(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TabClickListener implements View.OnClickListener {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
for (int i = 0; i < mTabStrip.getChildCount(); i++) {
|
||||||
|
if (v == mTabStrip.getChildAt(i)) {
|
||||||
|
mViewPager.setCurrentItem(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
200
src/com/example/android/common/view/SlidingTabStrip.java
Normal file
200
src/com/example/android/common/view/SlidingTabStrip.java
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.example.android.common.view;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
class SlidingTabStrip extends LinearLayout {
|
||||||
|
|
||||||
|
private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 2;
|
||||||
|
private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
|
||||||
|
private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 8;
|
||||||
|
private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;
|
||||||
|
|
||||||
|
private static final int DEFAULT_DIVIDER_THICKNESS_DIPS = 1;
|
||||||
|
private static final byte DEFAULT_DIVIDER_COLOR_ALPHA = 0x20;
|
||||||
|
private static final float DEFAULT_DIVIDER_HEIGHT = 0.5f;
|
||||||
|
|
||||||
|
private final int mBottomBorderThickness;
|
||||||
|
private final Paint mBottomBorderPaint;
|
||||||
|
|
||||||
|
private final int mSelectedIndicatorThickness;
|
||||||
|
private final Paint mSelectedIndicatorPaint;
|
||||||
|
|
||||||
|
private final int mDefaultBottomBorderColor;
|
||||||
|
|
||||||
|
private final Paint mDividerPaint;
|
||||||
|
private final float mDividerHeight;
|
||||||
|
|
||||||
|
private int mSelectedPosition;
|
||||||
|
private float mSelectionOffset;
|
||||||
|
|
||||||
|
private SlidingTabLayout.TabColorizer mCustomTabColorizer;
|
||||||
|
private final SimpleTabColorizer mDefaultTabColorizer;
|
||||||
|
|
||||||
|
SlidingTabStrip(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
SlidingTabStrip(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setWillNotDraw(false);
|
||||||
|
|
||||||
|
final float density = getResources().getDisplayMetrics().density;
|
||||||
|
|
||||||
|
TypedValue outValue = new TypedValue();
|
||||||
|
context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
|
||||||
|
final int themeForegroundColor = outValue.data;
|
||||||
|
|
||||||
|
mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor, DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);
|
||||||
|
|
||||||
|
mDefaultTabColorizer = new SimpleTabColorizer();
|
||||||
|
mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);
|
||||||
|
mDefaultTabColorizer.setDividerColors(setColorAlpha(themeForegroundColor, DEFAULT_DIVIDER_COLOR_ALPHA));
|
||||||
|
|
||||||
|
mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
|
||||||
|
mBottomBorderPaint = new Paint();
|
||||||
|
mBottomBorderPaint.setColor(mDefaultBottomBorderColor);
|
||||||
|
|
||||||
|
mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
|
||||||
|
mSelectedIndicatorPaint = new Paint();
|
||||||
|
|
||||||
|
mDividerHeight = DEFAULT_DIVIDER_HEIGHT;
|
||||||
|
mDividerPaint = new Paint();
|
||||||
|
mDividerPaint.setStrokeWidth((int) (DEFAULT_DIVIDER_THICKNESS_DIPS * density));
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
|
||||||
|
mCustomTabColorizer = customTabColorizer;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSelectedIndicatorColors(int... colors) {
|
||||||
|
// Make sure that the custom colorizer is removed
|
||||||
|
mCustomTabColorizer = null;
|
||||||
|
mDefaultTabColorizer.setIndicatorColors(colors);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDividerColors(int... colors) {
|
||||||
|
// Make sure that the custom colorizer is removed
|
||||||
|
mCustomTabColorizer = null;
|
||||||
|
mDefaultTabColorizer.setDividerColors(colors);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onViewPagerPageChanged(int position, float positionOffset) {
|
||||||
|
mSelectedPosition = position;
|
||||||
|
mSelectionOffset = positionOffset;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
final int height = getHeight();
|
||||||
|
final int childCount = getChildCount();
|
||||||
|
final int dividerHeightPx = (int) (Math.min(Math.max(0f, mDividerHeight), 1f) * height);
|
||||||
|
final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null ? mCustomTabColorizer : mDefaultTabColorizer;
|
||||||
|
|
||||||
|
// Thick colored underline below the current selection
|
||||||
|
if (childCount > 0) {
|
||||||
|
View selectedTitle = getChildAt(mSelectedPosition);
|
||||||
|
int left = selectedTitle.getLeft();
|
||||||
|
int right = selectedTitle.getRight();
|
||||||
|
int color = tabColorizer.getIndicatorColor(mSelectedPosition);
|
||||||
|
|
||||||
|
if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
|
||||||
|
int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
|
||||||
|
if (color != nextColor) {
|
||||||
|
color = blendColors(nextColor, color, mSelectionOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the selection partway between the tabs
|
||||||
|
View nextTitle = getChildAt(mSelectedPosition + 1);
|
||||||
|
left = (int) (mSelectionOffset * nextTitle.getLeft() + (1.0f - mSelectionOffset) * left);
|
||||||
|
right = (int) (mSelectionOffset * nextTitle.getRight() + (1.0f - mSelectionOffset) * right);
|
||||||
|
}
|
||||||
|
|
||||||
|
mSelectedIndicatorPaint.setColor(color);
|
||||||
|
|
||||||
|
canvas.drawRect(left, height - mSelectedIndicatorThickness, right, height, mSelectedIndicatorPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thin underline along the entire bottom edge
|
||||||
|
canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
|
||||||
|
|
||||||
|
// Vertical separators between the titles
|
||||||
|
int separatorTop = (height - dividerHeightPx) / 2;
|
||||||
|
for (int i = 0; i < childCount - 1; i++) {
|
||||||
|
View child = getChildAt(i);
|
||||||
|
mDividerPaint.setColor(tabColorizer.getDividerColor(i));
|
||||||
|
canvas.drawLine(child.getRight(), separatorTop, child.getRight(), separatorTop + dividerHeightPx, mDividerPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the alpha value of the {@code color} to be the given {@code alpha} value.
|
||||||
|
*/
|
||||||
|
private static int setColorAlpha(int color, byte alpha) {
|
||||||
|
return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blend {@code color1} and {@code color2} using the given ratio.
|
||||||
|
*
|
||||||
|
* @param ratio
|
||||||
|
* of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
|
||||||
|
* 0.0 will return {@code color2}.
|
||||||
|
*/
|
||||||
|
private static int blendColors(int color1, int color2, float ratio) {
|
||||||
|
final float inverseRation = 1f - ratio;
|
||||||
|
float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
|
||||||
|
float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
|
||||||
|
float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
|
||||||
|
return Color.rgb((int) r, (int) g, (int) b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
|
||||||
|
private int[] mIndicatorColors;
|
||||||
|
private int[] mDividerColors;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getIndicatorColor(int position) {
|
||||||
|
return mIndicatorColors[position % mIndicatorColors.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int getDividerColor(int position) {
|
||||||
|
return mDividerColors[position % mDividerColors.length];
|
||||||
|
}
|
||||||
|
|
||||||
|
void setIndicatorColors(int... colors) {
|
||||||
|
mIndicatorColors = colors;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDividerColors(int... colors) {
|
||||||
|
mDividerColors = colors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue