Home | History | Annotate | Download | only in launcher3
      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 
     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 
     56     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);
     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 
     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;
    160     }
    161 
    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     }
    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 
    179     void updateMarker(int index, PageMarkerResources marker) {
    180         PageIndicatorMarker m = mMarkers.get(index);
    181         m.setMarkerDrawables(marker.activeId, marker.inactiveId);
    182     }
    183 
    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     }
    191     void removeAllMarkers(boolean allowAnimations) {
    192         while (mMarkers.size() > 0) {
    193             removeMarker(Integer.MAX_VALUE, allowAnimations);
    194         }
    195     }
    196 
    197     void setActiveMarker(int index) {
    198         // 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);
    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