1 /* 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 17 package com.android.launcher3; 18 19 import android.animation.LayoutTransition; 20 import android.content.Context; 21 import android.content.res.TypedArray; 22 import android.util.AttributeSet; 23 import android.view.LayoutInflater; 24 import android.widget.LinearLayout; 25 26 import java.util.ArrayList; 27 28 public class PageIndicator extends LinearLayout { 29 @SuppressWarnings("unused") 30 private static final String TAG = "PageIndicator"; 31 // Want this to look good? Keep it odd 32 private static final boolean MODULATE_ALPHA_ENABLED = false; 33 34 private LayoutInflater mLayoutInflater; 35 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 42 public static class PageMarkerResources { 43 int activeId; 44 int inactiveId; 45 PageMarkerResources()46 public PageMarkerResources() { 47 activeId = R.drawable.ic_pageindicator_current; 48 inactiveId = R.drawable.ic_pageindicator_default; 49 } PageMarkerResources(int aId, int iaId)50 public PageMarkerResources(int aId, int iaId) { 51 activeId = aId; 52 inactiveId = iaId; 53 } 54 } 55 PageIndicator(Context context)56 public PageIndicator(Context context) { 57 this(context, null); 58 } 59 PageIndicator(Context context, AttributeSet attrs)60 public PageIndicator(Context context, AttributeSet attrs) { 61 this(context, attrs, 0); 62 } 63 PageIndicator(Context context, AttributeSet attrs, int defStyle)64 public PageIndicator(Context context, AttributeSet attrs, int defStyle) { 65 super(context, attrs, defStyle); 66 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; 71 mLayoutInflater = LayoutInflater.from(context); 72 a.recycle(); 73 74 // Set the layout transition properties 75 LayoutTransition transition = getLayoutTransition(); 76 transition.setDuration(175); 77 } 78 enableLayoutTransitions()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 disableLayoutTransitions()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 offsetWindowCenterTo(int activeIndex, boolean allowAnimations)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; 160 } 161 addMarker(int index, PageMarkerResources marker, boolean allowAnimations)162 void addMarker(int index, PageMarkerResources marker, boolean allowAnimations) { 163 index = Math.max(0, Math.min(index, mMarkers.size())); 164 165 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); 171 offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations); 172 } addMarkers(ArrayList<PageMarkerResources> markers, boolean allowAnimations)173 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); 176 } 177 } 178 updateMarker(int index, PageMarkerResources marker)179 void updateMarker(int index, PageMarkerResources marker) { 180 PageIndicatorMarker m = mMarkers.get(index); 181 m.setMarkerDrawables(marker.activeId, marker.inactiveId); 182 } 183 removeMarker(int index, boolean allowAnimations)184 void removeMarker(int index, boolean allowAnimations) { 185 if (mMarkers.size() > 0) { 186 index = Math.max(0, Math.min(mMarkers.size() - 1, index)); 187 mMarkers.remove(index); 188 offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations); 189 } 190 } removeAllMarkers(boolean allowAnimations)191 void removeAllMarkers(boolean allowAnimations) { 192 while (mMarkers.size() > 0) { 193 removeMarker(Integer.MAX_VALUE, allowAnimations); 194 } 195 } 196 setActiveMarker(int index)197 void setActiveMarker(int index) { 198 // Center the active marker 199 mActiveMarkerIndex = index; 200 offsetWindowCenterTo(index, false); 201 } 202 dumpState(String txt)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); 209 } 210 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); 217 } 218 } 219