/* * Copyright (C) 2011 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.android.launcher3; import android.animation.LayoutTransition; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.LayoutInflater; import android.widget.LinearLayout; import java.util.ArrayList; public class PageIndicator extends LinearLayout { @SuppressWarnings("unused") private static final String TAG = "PageIndicator"; // Want this to look good? Keep it odd private static final boolean MODULATE_ALPHA_ENABLED = false; private LayoutInflater mLayoutInflater; private int[] mWindowRange = new int[2]; private int mMaxWindowSize; private ArrayList mMarkers = new ArrayList(); private int mActiveMarkerIndex; public static class PageMarkerResources { int activeId; int inactiveId; public PageMarkerResources() { activeId = R.drawable.ic_pageindicator_current; inactiveId = R.drawable.ic_pageindicator_default; } public PageMarkerResources(int aId, int iaId) { activeId = aId; inactiveId = iaId; } } public PageIndicator(Context context) { this(context, null); } public PageIndicator(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PageIndicator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PageIndicator, defStyle, 0); mMaxWindowSize = a.getInteger(R.styleable.PageIndicator_windowSize, 15); mWindowRange[0] = 0; mWindowRange[1] = 0; mLayoutInflater = LayoutInflater.from(context); a.recycle(); // Set the layout transition properties LayoutTransition transition = getLayoutTransition(); transition.setDuration(175); } private void enableLayoutTransitions() { LayoutTransition transition = getLayoutTransition(); transition.enableTransitionType(LayoutTransition.APPEARING); transition.enableTransitionType(LayoutTransition.DISAPPEARING); transition.enableTransitionType(LayoutTransition.CHANGE_APPEARING); transition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); } private void disableLayoutTransitions() { LayoutTransition transition = getLayoutTransition(); transition.disableTransitionType(LayoutTransition.APPEARING); transition.disableTransitionType(LayoutTransition.DISAPPEARING); transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING); transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); } void offsetWindowCenterTo(int activeIndex, boolean allowAnimations) { if (activeIndex < 0) { new Throwable().printStackTrace(); } int windowSize = Math.min(mMarkers.size(), mMaxWindowSize); int hWindowSize = (int) windowSize / 2; float hfWindowSize = windowSize / 2f; int windowStart = Math.max(0, activeIndex - hWindowSize); int windowEnd = Math.min(mMarkers.size(), windowStart + mMaxWindowSize); windowStart = windowEnd - Math.min(mMarkers.size(), windowSize); int windowMid = windowStart + (windowEnd - windowStart) / 2; boolean windowAtStart = (windowStart == 0); boolean windowAtEnd = (windowEnd == mMarkers.size()); boolean windowMoved = (mWindowRange[0] != windowStart) || (mWindowRange[1] != windowEnd); if (!allowAnimations) { disableLayoutTransitions(); } // Remove all the previous children that are no longer in the window for (int i = getChildCount() - 1; i >= 0; --i) { PageIndicatorMarker marker = (PageIndicatorMarker) getChildAt(i); int markerIndex = mMarkers.indexOf(marker); if (markerIndex < windowStart || markerIndex >= windowEnd) { removeView(marker); } } // Add all the new children that belong in the window for (int i = 0; i < mMarkers.size(); ++i) { PageIndicatorMarker marker = (PageIndicatorMarker) mMarkers.get(i); if (windowStart <= i && i < windowEnd) { if (indexOfChild(marker) < 0) { addView(marker, i - windowStart); } if (i == activeIndex) { marker.activate(windowMoved); } else { marker.inactivate(windowMoved); } } else { marker.inactivate(true); } if (MODULATE_ALPHA_ENABLED) { // Update the marker's alpha float alpha = 1f; if (mMarkers.size() > windowSize) { if ((windowAtStart && i > hWindowSize) || (windowAtEnd && i < (mMarkers.size() - hWindowSize)) || (!windowAtStart && !windowAtEnd)) { alpha = 1f - Math.abs((i - windowMid) / hfWindowSize); } } marker.animate().alpha(alpha).setDuration(500).start(); } } if (!allowAnimations) { enableLayoutTransitions(); } mWindowRange[0] = windowStart; mWindowRange[1] = windowEnd; } void addMarker(int index, PageMarkerResources marker, boolean allowAnimations) { index = Math.max(0, Math.min(index, mMarkers.size())); PageIndicatorMarker m = (PageIndicatorMarker) mLayoutInflater.inflate(R.layout.page_indicator_marker, this, false); m.setMarkerDrawables(marker.activeId, marker.inactiveId); mMarkers.add(index, m); offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations); } void addMarkers(ArrayList markers, boolean allowAnimations) { for (int i = 0; i < markers.size(); ++i) { addMarker(Integer.MAX_VALUE, markers.get(i), allowAnimations); } } void updateMarker(int index, PageMarkerResources marker) { PageIndicatorMarker m = mMarkers.get(index); m.setMarkerDrawables(marker.activeId, marker.inactiveId); } void removeMarker(int index, boolean allowAnimations) { if (mMarkers.size() > 0) { index = Math.max(0, Math.min(mMarkers.size() - 1, index)); mMarkers.remove(index); offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations); } } void removeAllMarkers(boolean allowAnimations) { while (mMarkers.size() > 0) { removeMarker(Integer.MAX_VALUE, allowAnimations); } } void setActiveMarker(int index) { // Center the active marker mActiveMarkerIndex = index; offsetWindowCenterTo(index, false); } void dumpState(String txt) { System.out.println(txt); System.out.println("\tmMarkers: " + mMarkers.size()); for (int i = 0; i < mMarkers.size(); ++i) { PageIndicatorMarker m = mMarkers.get(i); System.out.println("\t\t(" + i + ") " + m); } System.out.println("\twindow: [" + mWindowRange[0] + ", " + mWindowRange[1] + "]"); System.out.println("\tchildren: " + getChildCount()); for (int i = 0; i < getChildCount(); ++i) { PageIndicatorMarker m = (PageIndicatorMarker) getChildAt(i); System.out.println("\t\t(" + i + ") " + m); } System.out.println("\tactive: " + mActiveMarkerIndex); } }