blob: 62ea03bcc85ff547a3b1ca4ddf5c0377d445b53a [file] [log] [blame]
Winson Chungd2be3812013-07-16 11:11:32 -07001/*
2 * Copyright (C) 2011 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
17package com.android.launcher3;
18
Winson Chungd2be3812013-07-16 11:11:32 -070019import android.animation.LayoutTransition;
Winson Chungd2be3812013-07-16 11:11:32 -070020import android.content.Context;
Winson Chungd2be3812013-07-16 11:11:32 -070021import android.content.res.TypedArray;
22import android.util.AttributeSet;
Winson Chungd2be3812013-07-16 11:11:32 -070023import android.view.LayoutInflater;
Winson Chungd2be3812013-07-16 11:11:32 -070024import android.widget.LinearLayout;
Winson Chungd2be3812013-07-16 11:11:32 -070025
26import java.util.ArrayList;
27
28public class PageIndicator extends LinearLayout {
29 @SuppressWarnings("unused")
30 private static final String TAG = "PageIndicator";
Winson Chung5bc865e2013-07-18 15:18:25 -070031 // Want this to look good? Keep it odd
32 private static final boolean MODULATE_ALPHA_ENABLED = false;
Winson Chungd2be3812013-07-16 11:11:32 -070033
34 private LayoutInflater mLayoutInflater;
Winson Chung5bc865e2013-07-18 15:18:25 -070035 private int[] mWindowRange = new int[2];
36 private int mMaxWindowSize;
37
38 private ArrayList<PageIndicatorMarker> mMarkers =
39 new ArrayList<PageIndicatorMarker>();
40 private int mActiveMarkerIndex;
41
Winson Chung7819a562013-09-19 15:55:45 -070042 public static class PageMarkerResources {
43 int activeId;
44 int inactiveId;
45
46 public PageMarkerResources() {
47 activeId = R.drawable.ic_pageindicator_current;
48 inactiveId = R.drawable.ic_pageindicator_default;
49 }
50 public PageMarkerResources(int aId, int iaId) {
51 activeId = aId;
52 inactiveId = iaId;
53 }
54 }
55
Winson Chungd2be3812013-07-16 11:11:32 -070056 public PageIndicator(Context context) {
57 this(context, null);
58 }
59
60 public PageIndicator(Context context, AttributeSet attrs) {
61 this(context, attrs, 0);
62 }
63
64 public PageIndicator(Context context, AttributeSet attrs, int defStyle) {
65 super(context, attrs, defStyle);
Winson Chung5bc865e2013-07-18 15:18:25 -070066 TypedArray a = context.obtainStyledAttributes(attrs,
67 R.styleable.PageIndicator, defStyle, 0);
68 mMaxWindowSize = a.getInteger(R.styleable.PageIndicator_windowSize, 15);
69 mWindowRange[0] = 0;
70 mWindowRange[1] = 0;
Winson Chungd2be3812013-07-16 11:11:32 -070071 mLayoutInflater = LayoutInflater.from(context);
Winson Chung5bc865e2013-07-18 15:18:25 -070072 a.recycle();
Winson Chungd2be3812013-07-16 11:11:32 -070073
Winson Chung5bc865e2013-07-18 15:18:25 -070074 // Set the layout transition properties
Winson Chungd2be3812013-07-16 11:11:32 -070075 LayoutTransition transition = getLayoutTransition();
Winson Chung5bc865e2013-07-18 15:18:25 -070076 transition.setDuration(175);
77 }
78
79 private void enableLayoutTransitions() {
80 LayoutTransition transition = getLayoutTransition();
81 transition.enableTransitionType(LayoutTransition.APPEARING);
82 transition.enableTransitionType(LayoutTransition.DISAPPEARING);
83 transition.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
84 transition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
85 }
86
87 private void disableLayoutTransitions() {
88 LayoutTransition transition = getLayoutTransition();
89 transition.disableTransitionType(LayoutTransition.APPEARING);
90 transition.disableTransitionType(LayoutTransition.DISAPPEARING);
91 transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
92 transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
93 }
94
95 void offsetWindowCenterTo(int activeIndex, boolean allowAnimations) {
96 if (activeIndex < 0) {
97 new Throwable().printStackTrace();
98 }
99 int windowSize = Math.min(mMarkers.size(), mMaxWindowSize);
100 int hWindowSize = (int) windowSize / 2;
101 float hfWindowSize = windowSize / 2f;
102 int windowStart = Math.max(0, activeIndex - hWindowSize);
103 int windowEnd = Math.min(mMarkers.size(), windowStart + mMaxWindowSize);
104 windowStart = windowEnd - Math.min(mMarkers.size(), windowSize);
105 int windowMid = windowStart + (windowEnd - windowStart) / 2;
106 boolean windowAtStart = (windowStart == 0);
107 boolean windowAtEnd = (windowEnd == mMarkers.size());
108 boolean windowMoved = (mWindowRange[0] != windowStart) ||
109 (mWindowRange[1] != windowEnd);
110
111 if (!allowAnimations) {
112 disableLayoutTransitions();
113 }
114
115 // Remove all the previous children that are no longer in the window
116 for (int i = getChildCount() - 1; i >= 0; --i) {
117 PageIndicatorMarker marker = (PageIndicatorMarker) getChildAt(i);
118 int markerIndex = mMarkers.indexOf(marker);
119 if (markerIndex < windowStart || markerIndex >= windowEnd) {
120 removeView(marker);
121 }
122 }
123
124 // Add all the new children that belong in the window
125 for (int i = 0; i < mMarkers.size(); ++i) {
126 PageIndicatorMarker marker = (PageIndicatorMarker) mMarkers.get(i);
127 if (windowStart <= i && i < windowEnd) {
128 if (indexOfChild(marker) < 0) {
129 addView(marker, i - windowStart);
130 }
131 if (i == activeIndex) {
132 marker.activate(windowMoved);
133 } else {
134 marker.inactivate(windowMoved);
135 }
136 } else {
137 marker.inactivate(true);
138 }
139
140 if (MODULATE_ALPHA_ENABLED) {
141 // Update the marker's alpha
142 float alpha = 1f;
143 if (mMarkers.size() > windowSize) {
144 if ((windowAtStart && i > hWindowSize) ||
145 (windowAtEnd && i < (mMarkers.size() - hWindowSize)) ||
146 (!windowAtStart && !windowAtEnd)) {
147 alpha = 1f - Math.abs((i - windowMid) / hfWindowSize);
148 }
149 }
150 marker.animate().alpha(alpha).setDuration(500).start();
151 }
152 }
153
154 if (!allowAnimations) {
155 enableLayoutTransitions();
156 }
157
158 mWindowRange[0] = windowStart;
159 mWindowRange[1] = windowEnd;
Winson Chungd2be3812013-07-16 11:11:32 -0700160 }
161
Winson Chung7819a562013-09-19 15:55:45 -0700162 void addMarker(int index, PageMarkerResources marker, boolean allowAnimations) {
Winson Chung5bc865e2013-07-18 15:18:25 -0700163 index = Math.max(0, Math.min(index, mMarkers.size()));
164
Winson Chung7819a562013-09-19 15:55:45 -0700165 PageIndicatorMarker m =
166 (PageIndicatorMarker) mLayoutInflater.inflate(R.layout.page_indicator_marker,
167 this, false);
168 m.setMarkerDrawables(marker.activeId, marker.inactiveId);
169
170 mMarkers.add(index, m);
Winson Chungc58497e2013-09-03 17:48:37 -0700171 offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations);
Winson Chungd2be3812013-07-16 11:11:32 -0700172 }
Winson Chung7819a562013-09-19 15:55:45 -0700173 void addMarkers(ArrayList<PageMarkerResources> markers, boolean allowAnimations) {
174 for (int i = 0; i < markers.size(); ++i) {
175 addMarker(Integer.MAX_VALUE, markers.get(i), allowAnimations);
Winson Chungd2be3812013-07-16 11:11:32 -0700176 }
177 }
178
Winson Chung89f97052013-09-20 11:32:26 -0700179 void updateMarker(int index, PageMarkerResources marker) {
180 PageIndicatorMarker m = mMarkers.get(index);
181 m.setMarkerDrawables(marker.activeId, marker.inactiveId);
182 }
183
Winson Chungc58497e2013-09-03 17:48:37 -0700184 void removeMarker(int index, boolean allowAnimations) {
Winson Chung5bc865e2013-07-18 15:18:25 -0700185 if (mMarkers.size() > 0) {
186 index = Math.max(0, Math.min(mMarkers.size() - 1, index));
187 mMarkers.remove(index);
Winson Chungc58497e2013-09-03 17:48:37 -0700188 offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations);
Winson Chungd2be3812013-07-16 11:11:32 -0700189 }
190 }
Winson Chungc58497e2013-09-03 17:48:37 -0700191 void removeAllMarkers(boolean allowAnimations) {
Winson Chung5bc865e2013-07-18 15:18:25 -0700192 while (mMarkers.size() > 0) {
Winson Chungc58497e2013-09-03 17:48:37 -0700193 removeMarker(Integer.MAX_VALUE, allowAnimations);
Winson Chungd2be3812013-07-16 11:11:32 -0700194 }
195 }
196
197 void setActiveMarker(int index) {
Winson Chung5bc865e2013-07-18 15:18:25 -0700198 // Center the active marker
199 mActiveMarkerIndex = index;
200 offsetWindowCenterTo(index, false);
201 }
202
203 void dumpState(String txt) {
204 System.out.println(txt);
205 System.out.println("\tmMarkers: " + mMarkers.size());
206 for (int i = 0; i < mMarkers.size(); ++i) {
207 PageIndicatorMarker m = mMarkers.get(i);
208 System.out.println("\t\t(" + i + ") " + m);
Winson Chungd2be3812013-07-16 11:11:32 -0700209 }
Winson Chung5bc865e2013-07-18 15:18:25 -0700210 System.out.println("\twindow: [" + mWindowRange[0] + ", " + mWindowRange[1] + "]");
211 System.out.println("\tchildren: " + getChildCount());
212 for (int i = 0; i < getChildCount(); ++i) {
213 PageIndicatorMarker m = (PageIndicatorMarker) getChildAt(i);
214 System.out.println("\t\t(" + i + ") " + m);
215 }
216 System.out.println("\tactive: " + mActiveMarkerIndex);
Winson Chungd2be3812013-07-16 11:11:32 -0700217 }
218}