Skip to content

Commit 0643658

Browse files
committed
Add IconPageIndicator.
1 parent 16e2b5a commit 0643658

39 files changed

+302
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
Change Log
22
==========
33

4-
Version 2.4 *(In Development)*
4+
Version 2.4.0 *(In Development)*
55
------------------------------
66

7+
* New `IconPageIndicator`! Uses state-list images to represent pages.
78
* Support `android:background` attribute on `Canvas`-based views.
89
* Title indicator allows for drawing its line, underline, and/or triangle on
910
top of the titles for placement underneath a `ViewPager`.

library/res/values/vpi__attrs.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
<declare-styleable name="ViewPagerIndicator">
2020
<!-- Style of the circle indicator. -->
2121
<attr name="vpiCirclePageIndicatorStyle" format="reference"/>
22+
<!-- Style of the icon indicator's views. -->
23+
<attr name="vpiIconPageIndicatorStyle" format="reference"/>
2224
<!-- Style of the line indicator. -->
2325
<attr name="vpiLinePageIndicatorStyle" format="reference"/>
2426
<!-- Style of the title indicator. -->

library/res/values/vpi__styles.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
<resources>
1818
<style name="Theme.PageIndicatorDefaults" parent="android:Theme">
19+
<item name="vpiIconPageIndicatorStyle">@style/Widget.IconPageIndicator</item>
1920
<item name="vpiTabPageIndicatorStyle">@style/Widget.TabPageIndicator</item>
2021
</style>
2122

@@ -38,4 +39,9 @@
3839
<item name="android:textStyle">bold</item>
3940
<item name="android:textColor">@color/vpi__dark_theme</item>
4041
</style>
42+
43+
<style name="Widget.IconPageIndicator" parent="Widget">
44+
<item name="android:layout_marginLeft">6dp</item>
45+
<item name="android:layout_marginRight">6dp</item>
46+
</style>
4147
</resources>
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
/*
2+
* Copyright (C) 2011 The Android Open Source Project
3+
* Copyright (C) 2012 Jake Wharton
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package com.viewpagerindicator;
18+
19+
import android.content.Context;
20+
import android.support.v4.view.PagerAdapter;
21+
import android.support.v4.view.ViewPager;
22+
import android.support.v4.view.ViewPager.OnPageChangeListener;
23+
import android.util.AttributeSet;
24+
import android.view.Gravity;
25+
import android.view.View;
26+
import android.widget.HorizontalScrollView;
27+
import android.widget.ImageView;
28+
29+
import static android.view.ViewGroup.LayoutParams.FILL_PARENT;
30+
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
31+
32+
/**
33+
* This widget implements the dynamic action bar tab behavior that can change
34+
* across different configurations or circumstances.
35+
*/
36+
public class IconPageIndicator extends HorizontalScrollView implements PageIndicator {
37+
private final IcsLinearLayout mIconsLayout;
38+
39+
private ViewPager mViewPager;
40+
private OnPageChangeListener mListener;
41+
private Runnable mIconSelector;
42+
private int mSelectedIndex;
43+
44+
public IconPageIndicator(Context context) {
45+
this(context, null);
46+
}
47+
48+
public IconPageIndicator(Context context, AttributeSet attrs) {
49+
super(context, attrs);
50+
setHorizontalScrollBarEnabled(false);
51+
52+
mIconsLayout = new IcsLinearLayout(context, R.attr.vpiIconPageIndicatorStyle);
53+
addView(mIconsLayout, new LayoutParams(WRAP_CONTENT, FILL_PARENT, Gravity.CENTER));
54+
}
55+
56+
private void animateToIcon(final int position) {
57+
final View iconView = mIconsLayout.getChildAt(position);
58+
if (mIconSelector != null) {
59+
removeCallbacks(mIconSelector);
60+
}
61+
mIconSelector = new Runnable() {
62+
public void run() {
63+
final int scrollPos = iconView.getLeft() - (getWidth() - iconView.getWidth()) / 2;
64+
smoothScrollTo(scrollPos, 0);
65+
mIconSelector = null;
66+
}
67+
};
68+
post(mIconSelector);
69+
}
70+
71+
@Override
72+
public void onAttachedToWindow() {
73+
super.onAttachedToWindow();
74+
if (mIconSelector != null) {
75+
// Re-post the selector we saved
76+
post(mIconSelector);
77+
}
78+
}
79+
80+
@Override
81+
public void onDetachedFromWindow() {
82+
super.onDetachedFromWindow();
83+
if (mIconSelector != null) {
84+
removeCallbacks(mIconSelector);
85+
}
86+
}
87+
88+
@Override
89+
public void onPageScrollStateChanged(int arg0) {
90+
if (mListener != null) {
91+
mListener.onPageScrollStateChanged(arg0);
92+
}
93+
}
94+
95+
@Override
96+
public void onPageScrolled(int arg0, float arg1, int arg2) {
97+
if (mListener != null) {
98+
mListener.onPageScrolled(arg0, arg1, arg2);
99+
}
100+
}
101+
102+
@Override
103+
public void onPageSelected(int arg0) {
104+
setCurrentItem(arg0);
105+
if (mListener != null) {
106+
mListener.onPageSelected(arg0);
107+
}
108+
}
109+
110+
@Override
111+
public void setViewPager(ViewPager view) {
112+
if (mViewPager == view) {
113+
return;
114+
}
115+
if (mViewPager != null) {
116+
mViewPager.setOnPageChangeListener(null);
117+
}
118+
PagerAdapter adapter = view.getAdapter();
119+
if (adapter == null) {
120+
throw new IllegalStateException("ViewPager does not have adapter instance.");
121+
}
122+
mViewPager = view;
123+
view.setOnPageChangeListener(this);
124+
notifyDataSetChanged();
125+
}
126+
127+
public void notifyDataSetChanged() {
128+
mIconsLayout.removeAllViews();
129+
IconPagerAdapter iconAdapter = (IconPagerAdapter) mViewPager.getAdapter();
130+
int count = iconAdapter.getCount();
131+
for (int i = 0; i < count; i++) {
132+
ImageView view = new ImageView(getContext(), null, R.attr.vpiIconPageIndicatorStyle);
133+
view.setImageResource(iconAdapter.getIconResId(i));
134+
mIconsLayout.addView(view);
135+
}
136+
if (mSelectedIndex > count) {
137+
mSelectedIndex = count - 1;
138+
}
139+
setCurrentItem(mSelectedIndex);
140+
requestLayout();
141+
}
142+
143+
@Override
144+
public void setViewPager(ViewPager view, int initialPosition) {
145+
setViewPager(view);
146+
setCurrentItem(initialPosition);
147+
}
148+
149+
@Override
150+
public void setCurrentItem(int item) {
151+
if (mViewPager == null) {
152+
throw new IllegalStateException("ViewPager has not been bound.");
153+
}
154+
mSelectedIndex = item;
155+
mViewPager.setCurrentItem(item);
156+
157+
int tabCount = mIconsLayout.getChildCount();
158+
for (int i = 0; i < tabCount; i++) {
159+
View child = mIconsLayout.getChildAt(i);
160+
boolean isSelected = (i == item);
161+
child.setSelected(isSelected);
162+
if (isSelected) {
163+
animateToIcon(item);
164+
}
165+
}
166+
}
167+
168+
@Override
169+
public void setOnPageChangeListener(OnPageChangeListener listener) {
170+
mListener = listener;
171+
}
172+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.viewpagerindicator;
2+
3+
public interface IconPagerAdapter {
4+
/**
5+
* Get icon representing the page at {@code index} in the adapter.
6+
*/
7+
int getIconResId(int index);
8+
9+
// From PagerAdapter
10+
int getCount();
11+
}

library/src/com/viewpagerindicator/IcsLinearLayout.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ class IcsLinearLayout extends LinearLayout {
3131
private int mDividerPadding;
3232

3333

34-
public IcsLinearLayout(Context context) {
34+
public IcsLinearLayout(Context context, int themeAttr) {
3535
super(context);
3636

37-
TypedArray a = context.obtainStyledAttributes(null, LL, R.attr.vpiTabPageIndicatorStyle, 0);
37+
TypedArray a = context.obtainStyledAttributes(null, LL, themeAttr, 0);
3838
setDividerDrawable(a.getDrawable(IcsLinearLayout.LL_DIVIDER));
3939
mDividerPadding = a.getDimensionPixelSize(LL_DIVIDER_PADDING, 0);
4040
mShowDividers = a.getInteger(LL_SHOW_DIVIDER, SHOW_DIVIDER_NONE);

library/src/com/viewpagerindicator/TabPageIndicator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public TabPageIndicator(Context context, AttributeSet attrs) {
8282
super(context, attrs);
8383
setHorizontalScrollBarEnabled(false);
8484

85-
mTabLayout = new IcsLinearLayout(context);
85+
mTabLayout = new IcsLinearLayout(context, R.attr.vpiTabPageIndicatorStyle);
8686
addView(mTabLayout, new ViewGroup.LayoutParams(WRAP_CONTENT, FILL_PARENT));
8787
}
8888

sample/AndroidManifest.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@
8787

8888

8989

90+
<!-- ICON INDICATOR -->
91+
<activity
92+
android:name=".SampleIconsDefault"
93+
android:label="Icons/Default">
94+
<intent-filter>
95+
<action android:name="android.intent.action.MAIN" />
96+
<category android:name="com.jakewharton.android.viewpagerindicator.sample.SAMPLE" />
97+
</intent-filter>
98+
</activity>
99+
100+
101+
102+
90103
<!-- LINE INDICATOR -->
91104
<activity
92105
android:name=".SampleLinesDefault"
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
4+
<item android:state_selected="true" android:drawable="@drawable/perm_group_calendar_selected" />
5+
<item android:drawable="@drawable/perm_group_calendar_normal" />
6+
</selector>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
4+
<item android:state_selected="true" android:drawable="@drawable/perm_group_camera_selected" />
5+
<item android:drawable="@drawable/perm_group_camera_normal" />
6+
</selector>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
4+
<item android:state_selected="true" android:drawable="@drawable/perm_group_device_alarms_selected" />
5+
<item android:drawable="@drawable/perm_group_device_alarms_normal" />
6+
</selector>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
4+
<item android:state_selected="true" android:drawable="@drawable/perm_group_location_selected" />
5+
<item android:drawable="@drawable/perm_group_location_normal" />
6+
</selector>

sample/res/layout/simple_icons.xml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- Copyright (C) 2011 Jake Wharton
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+
<LinearLayout
18+
xmlns:android="http://schemas.android.com/apk/res/android"
19+
android:orientation="vertical"
20+
android:layout_width="fill_parent"
21+
android:layout_height="fill_parent">
22+
23+
<android.support.v4.view.ViewPager
24+
android:id="@+id/pager"
25+
android:layout_width="fill_parent"
26+
android:layout_height="0dp"
27+
android:layout_weight="1"
28+
/>
29+
<com.viewpagerindicator.IconPageIndicator
30+
android:id="@+id/indicator"
31+
android:layout_height="wrap_content"
32+
android:layout_width="fill_parent"
33+
/>
34+
35+
</LinearLayout>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.viewpagerindicator.sample;
2+
3+
import android.os.Bundle;
4+
import android.support.v4.view.ViewPager;
5+
import com.viewpagerindicator.IconPageIndicator;
6+
7+
public class SampleIconsDefault extends BaseSampleActivity {
8+
@Override
9+
protected void onCreate(Bundle savedInstanceState) {
10+
super.onCreate(savedInstanceState);
11+
setContentView(R.layout.simple_icons);
12+
13+
mAdapter = new TestFragmentAdapter(getSupportFragmentManager());
14+
15+
mPager = (ViewPager)findViewById(R.id.pager);
16+
mPager.setAdapter(mAdapter);
17+
18+
mIndicator = (IconPageIndicator)findViewById(R.id.indicator);
19+
mIndicator.setViewPager(mPager);
20+
}
21+
}

sample/src/com/viewpagerindicator/sample/TestFragmentAdapter.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33
import android.support.v4.app.Fragment;
44
import android.support.v4.app.FragmentManager;
55
import android.support.v4.app.FragmentPagerAdapter;
6+
import com.viewpagerindicator.IconPagerAdapter;
67

7-
class TestFragmentAdapter extends FragmentPagerAdapter {
8+
class TestFragmentAdapter extends FragmentPagerAdapter implements IconPagerAdapter {
89
protected static final String[] CONTENT = new String[] { "This", "Is", "A", "Test", };
10+
protected static final int[] ICONS = new int[] {
11+
R.drawable.perm_group_calendar,
12+
R.drawable.perm_group_camera,
13+
R.drawable.perm_group_device_alarms,
14+
R.drawable.perm_group_location
15+
};
916

1017
private int mCount = CONTENT.length;
1118

@@ -28,6 +35,11 @@ public CharSequence getPageTitle(int position) {
2835
return TestFragmentAdapter.CONTENT[position % CONTENT.length];
2936
}
3037

38+
@Override
39+
public int getIconResId(int index) {
40+
return ICONS[index % ICONS.length];
41+
}
42+
3143
public void setCount(int count) {
3244
if (count > 0 && count <= 10) {
3345
mCount = count;

0 commit comments

Comments
 (0)