From 5bc865e787b51ebbdebb14bf1eefc3a91f5f7c8d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 18 Jul 2013 15:18:25 -0700 Subject: [PATCH] Adding workaround for page indicators to support many pages. - Tweaking assets until we get something proper Change-Id: Ie07946acb529ff747d76896ff38837f8db6a7258 --- res/drawable-hdpi/active_page.png | Bin 1295 -> 1223 bytes res/drawable-hdpi/inactive_page.png | Bin 1402 -> 1275 bytes res/drawable-mdpi/active_page.png | Bin 1186 -> 1225 bytes res/drawable-mdpi/inactive_page.png | Bin 1268 -> 1189 bytes res/drawable-xhdpi/active_page.png | Bin 1367 -> 1225 bytes res/drawable-xhdpi/inactive_page.png | Bin 1450 -> 1235 bytes res/layout/page_indicator.xml | 3 +- res/values/attrs.xml | 7 + res/values/config.xml | 3 + src/com/android/launcher3/PageIndicator.java | 151 ++++++++++++++++-- .../launcher3/PageIndicatorMarker.java | 44 +++-- 11 files changed, 181 insertions(+), 27 deletions(-) diff --git a/res/drawable-hdpi/active_page.png b/res/drawable-hdpi/active_page.png index ce2d5b1699431ad6c438f88660d2628dacf53c51..58aa7f9ee49f19b831ad35c8b4ecc1e14ff5737f 100644 GIT binary patch delta 321 zcmV-H0lxl^3daesTLKF=Ff=qoMm0q@GDVYS0x=6WFf=qoMl?k?GDVZ30(Sv2lNtk@ ze{jt#ivR!s^+`lQRCwC#)u9T)P!tB>ZKyaHTEr|?o55xmgF(R;Fk9?CfG=S5A!f1I z1m8dyVi1FZ78#v9%re+IIf(-+N6VgpuS}M#7T@{GANrTNA2KfD;_! z3dq3#IzIs$n88x}cTiJ}GPpuLHyDGZf7+p2?!ZFOW)vl0DCl#BTG$>{i)`pJ3Q&SJ zr=}H&GmWs)r~({`GeyW!1FqmR11|8Q2JFFO2CU#s4S32!^dTmIJI|4m<4)$$TQwvp91k7AYKc^KXK{?#Q_e?3B-*+{PI6;@CS%50dWv6b8s0h1;ji46A%7E9m7P7 z1-wAK>pyv*k{AoR|C0~C1F<_k3)F!4f8&43z;1o58ot6VlY&h43W&7`6&0*NypRmTK_zP}v85@tmSQLow1My=ZK8KuQumMIg nS{0!B_%AJ66@N)l3=m)dXYd^y?1_{X00000NkvXXu0mjf)ZCqu diff --git a/res/drawable-hdpi/inactive_page.png b/res/drawable-hdpi/inactive_page.png index 2186f519a8e388e9ad311fd2653aae1e34a396ee..b70d9f418a7d5d4f969646a9bb91e205ce3fa69a 100644 GIT binary patch delta 375 zcmV--0f_$k3i}DLS^^9+Ml(Y)H8C_rH!?+&W&$t_Ge$E*GBq(XMK>}AsD8S@pS(-FWM+p=~VVraQ0<40`ljMKP!5qxN6i~GS529`b23zzaPQev81{{^b-;N*v00F2;L_t(|+RWC?D}+G+$8o#bSmIz=(w3qeSd=1VuX1p6kd%X?9GzU$ zo|XTAQf^WXLQ+mHk}WTViv!DYu(7R|wY!h+Q}fg`p0TsFWa?8p`^?O5_Ic)+S&wB| zWxrWG|1F?ONFrCNfEJ8l0ClJq0deeM9Up(%-gbb7A5lD5`jW*7Ms?%p25-iWWnOY( zGG3E_8tiHYy22suG}q@$0v41`d2FB)0o0-qBREsqzG3*MfOb49Ki!^{M{_HK*i=Hc z#q)Cwh$@ZO-0rI)giEPU8iQ^D_AIxgh8eUK7~C;d1UdH{LDfC~7fdde-}Nxc3}38T+2U`T{GpW6QyGL{s z+A4fl(d;!ZxD~so+s-;^x|`Y|i}*)0+)eK>QOtJj7{|V5#?CESMUQR_lM~H8A|vQR t2o1Q#4UTbC>O;TW^gRBPe-&Q>3;^4ETPdFIAB_M2002ovPDHLkV1fxX=g|NF diff --git a/res/drawable-mdpi/active_page.png b/res/drawable-mdpi/active_page.png index 9e23eccb0b5e32b4f4f274f85721db8da313be09..296a9a6b0b40a16ce5d2ef6aa00bbefcbde2efec 100644 GIT binary patch delta 360 zcmZ3)d6HAHGr-TCmrII^fq{Y7)59eQNGpIa2OE$quB!U7QPGl#$I{%u#KgtT($(C^ zb#fY$0ZhOGEU<{FoY82q6!V<=slOi?0`-0Oba4!+xRvzh|9|_#4oqrojBJbfBuWo7 zSU0r#)E@KrAubTl_NdmOrafh{!&3%M3xSUH|BmzeF>SQfOcPo?L9#>rzq;I&z}t+* z|0nakTJf~u!~ZAD(oWBeg;sFSX#Bw@uzJGGh<=8Rx~exXOkz6icz~_rsYYQ#y{+h` zUFNEM0ZW>gRWu42q(x;8cy=<&1s-JV*C;$tsAcg$!Ok;j(?e&4X=;~01v}*Ud2;+= zedNVe6YsqCREDPH(gbnS*GzswvWARNje%zw4P>~F@PCZpS+#1fr{Uy@XX+Ylx6Ei0 y4KZK1@~_THHp9L$A2(TpR}GD23=R#9q716Hd0!qAdUGEb2n?RCelF{r5}E+427*rj delta 320 zcmX@fxrkG-Gr-TCmrII^fq{Y7)59eQNDF{42OE%-|NK93qoO4fkDIfZlc|%1fvcH; z;p8+X1E_$x8(3fwQ#qsIWGUu3_5P7}gn;@kc)B=-RNPu}*^ujyfylAK?C9@kDjqr(+tr zA2c)WEn3JuLCxZc$-ND2tKV%mSs!@stVHdW!h@QY)vMO5I{#Dq07KQ@<@0ZD(y;}4 Oj=|H_&t;ucLK6U;GlQc5 diff --git a/res/drawable-mdpi/inactive_page.png b/res/drawable-mdpi/inactive_page.png index 9468a62edb7f5e4f71a7e9c5952c0235cafb70db..2225d250bb86e8e67029f0b4b0ab1ea5656bdb03 100644 GIT binary patch delta 288 zcmV+*0pI@g38e|JS^^9+Ml(Y)H8D6vH!?+&W&$t_Ge$E*GBq(bMK>}FK_p?a$wz3Ld+&O^YVW-oGmIKJMhk@XNm-VvD2jeZW0bYFTR?)Hb4qJ1Q~4wy z09TMpz2Z1lX_|HmR8a^9GyHDAg2%jp9XN1Oytf7cgO}hbiGeBDg87glCUkz()?UnZ zJwzb-vhKO}Q{X~(({Z%(5CN%p#*`;SoExbbp_yNRkU~ge# zYj2y7B8{b%TG&_#eu7x|L<30(A|X-E|G=C~mfN$)!Vi|+*}2=9yC4k1)FdPQ&wdWG zNdZ~U`YsNzj6B-7L=7Kfk#YrN8nw_rTG)TFXYAn&8YcK1O`PG@taq>;m!N8buCd^} z-62N0e}$}@;6gWFP;w4OPBrEm8?qL4VFpV&mtsA7id)AJ8LZki=Cx%qItg7HU}PTp zm!OZH&NmYhlu^+6liSAVem~fcx&IvQHRcubZV{#GZ4LOs3D&TPia9LfD6WlS51&9L zaEPw+F`Z+|mq2XeCgv-A#xZg>KJfgFQrs=9poj};v!VhN0e>&Q z&yWBB0QX5mK~#9!V*LOAKf^!*CWZk9qYfB#z<@h|g-m_MNca~JUk2iRKzxT}lR)JF z2{;glul~mkP6Dw#DP|I@kQs<4|0f>QQQHAC|C0{tsO5n0|KtN3Lgq6Ps0)~Z_y!QG zkeOJI0I@A4+0c>#%R#^fh~+48fPXy|vbr}V4p5|m10*PM02>t?z)6V%9#O#ok0^1# zaVj|AAU;WAvf)D@mL}8kQ$TD^NeKJ};z|m9Uqh|RXek+`U>7y(0!|VgF#%eEQOyA` z91Fw`@mLJ1s=Y}ulab7RfjzXCh80vdp{e~WsRl8Qc1%VcFzSGTb^t(t0Tlox+YRZ) S671gq0000xb z86p4x0fk9KK~#9!V*LOAKf^!*CWZk91MUD8GWE#;u_zF$1Mx2)z7NDtf%pY29UuY3 znLr!}#JV`t90%e}Ks+6YKN4jsC=n9CoIui8WU?8KW5uhGE)Hwig_mZ5M8z0H8`fWJVzhK3GUPk&571j>q+ zXiyr08YoJ{6vd!um_-BMcVLTNMjXv@ejq*%t(d44_qNdXBT>~cD7bup_%#(Q2Q^rO zuvt#1AOb~&IkW;H7aRm)D`<_#c5O*a{cf0ED)12te9ftZcbMkz8@1ma!-?Zg|< z7PJJh<}#AmH&lmKDzKXEEF?6A-zU`|#?j8{Ksx{+zyN};v!VhN0e@LJ z4n+U}0Rc%wK~#9!V*LOAKf^!*CWZk9qYfB#z<@h|1)t=@hYzV@iM+i05DftwfES2O zfLIQQ*`Z?JfcOCrp8?_@v~++b5L*B-E1EbL5NiRk5)dDPI)>_r5M+TZ7R!+V2N1gf zu>!Rnzz4+UBskU%DSD{l022Z}$A1W3kx+IKuKBgj~a%5 z1>!$sSpJz>4gfWoo|55!$JERP7s&|vE7Wwr3n0Egf&-2N@ds)b#iyXO0fFc`2E$& zmcc?$5aIBJg%3m&#TZGU24(#(Y!S}c=LOEffnSl$&ECw;+{~=U7|UfTBe{$va|6a4 z^`<}xR6rkGgM093y%Wy?L&{(U#D7dK`yc{E(|t`I%7azVW9IS#mceLR1Gv`*%w2Xs z!PS6CaLdw5tO|)IsRMb?2J?#aIRcl!2h(6q@$4>WfN#?z+)#ML|MLkQ?&X$3FER~5 z2)>97Zor~J(l)>=q0f^zFfm}AQ2qdQi%)jR^b{!N2STxF@p4rdV_gzf!GCEYP2Rz{ zL$xe|F0noVI-BBi3uGW>g{+o>TOi4zU*e>2|EO*ir)RsU)(pCTT1= z2a1wUu{`)(@&wL^Q{Y}S9lT#7)((Ho420!WIqiZO3-9Y`3Q<*3iqFA diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml index 8aae752c53..14eff75d0f 100644 --- a/res/layout/page_indicator.xml +++ b/res/layout/page_indicator.xml @@ -16,5 +16,6 @@ + android:animateLayoutChanges="true" + launcher:windowSize="@integer/config_maxNumberOfPageIndicatorsToShow"> diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 1545083791..09b8804205 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -32,6 +32,13 @@ + + + + + + diff --git a/res/values/config.xml b/res/values/config.xml index 6aaca1ad57..cc3d4f0bfe 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -3,6 +3,9 @@ false false + + 21 + -1500 diff --git a/src/com/android/launcher3/PageIndicator.java b/src/com/android/launcher3/PageIndicator.java index ecaa8f5446..d7778fb1a0 100644 --- a/src/com/android/launcher3/PageIndicator.java +++ b/src/com/android/launcher3/PageIndicator.java @@ -19,6 +19,7 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.LayoutTransition; +import android.animation.TimeInterpolator; import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; @@ -37,8 +38,22 @@ 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; + + private TimeInterpolator mAlphaInterpolator = new TimeInterpolator() { + public float getInterpolation(float t) { + return t; + } + }; public PageIndicator(Context context) { this(context, null); @@ -50,16 +65,110 @@ public class PageIndicator extends LinearLayout { 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(250); + 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) { - index = Math.max(0, Math.min(index, getChildCount())); - View marker = mLayoutInflater.inflate(R.layout.page_indicator_marker, this, false); - addView(marker, index); + index = Math.max(0, Math.min(index, mMarkers.size())); + + int mLayoutId = R.layout.page_indicator_marker; + PageIndicatorMarker marker = + (PageIndicatorMarker) mLayoutInflater.inflate(mLayoutId, this, false); + mMarkers.add(index, marker); + offsetWindowCenterTo(mActiveMarkerIndex, true); } void addMarkers(int count) { for (int i = 0; i < count; ++i) { @@ -68,25 +177,37 @@ public class PageIndicator extends LinearLayout { } void removeMarker(int index) { - if (getChildCount() > 0) { - index = Math.max(0, Math.min(index, getChildCount() - 1)); - removeViewAt(index); + if (mMarkers.size() > 0) { + index = Math.max(0, Math.min(mMarkers.size() - 1, index)); + mMarkers.remove(index); + offsetWindowCenterTo(mActiveMarkerIndex, true); } } void removeAllMarkers() { - while (getChildCount() > 0) { + while (mMarkers.size() > 0) { removeMarker(Integer.MAX_VALUE); } } void setActiveMarker(int index) { - for (int i = 0; i < getChildCount(); ++i) { - PageIndicatorMarker marker = (PageIndicatorMarker) getChildAt(i); - if (index == i) { - marker.activate(); - } else { - marker.inactivate(); - } + // 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); } } diff --git a/src/com/android/launcher3/PageIndicatorMarker.java b/src/com/android/launcher3/PageIndicatorMarker.java index 852ee518f9..f64c14fdc0 100644 --- a/src/com/android/launcher3/PageIndicatorMarker.java +++ b/src/com/android/launcher3/PageIndicatorMarker.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import android.animation.AnimatorListenerAdapter; import android.animation.LayoutTransition; import android.content.Context; import android.util.AttributeSet; @@ -29,10 +30,11 @@ public class PageIndicatorMarker extends FrameLayout { @SuppressWarnings("unused") private static final String TAG = "PageIndicator"; - private static final int MARKER_FADE_DURATION = 150; + private static final int MARKER_FADE_DURATION = 175; private View mActiveMarker; private View mInactiveMarker; + private boolean mIsActive = false; public PageIndicatorMarker(Context context) { this(context, null); @@ -51,16 +53,36 @@ public class PageIndicatorMarker extends FrameLayout { mInactiveMarker = findViewById(R.id.inactive); } - public void activate() { - mActiveMarker.animate().alpha(1f) - .setDuration(MARKER_FADE_DURATION).start(); - mInactiveMarker.animate().alpha(0f) - .setDuration(MARKER_FADE_DURATION).start(); + void activate(boolean immediate) { + if (immediate) { + mActiveMarker.animate().cancel(); + mActiveMarker.setAlpha(1f); + mInactiveMarker.animate().cancel(); + mInactiveMarker.setAlpha(0f); + } else { + mActiveMarker.animate().alpha(1f) + .setDuration(MARKER_FADE_DURATION).start(); + mInactiveMarker.animate().alpha(0f) + .setDuration(MARKER_FADE_DURATION).start(); + } + mIsActive = true; } - public void inactivate() { - mInactiveMarker.animate().alpha(1f) - .setDuration(MARKER_FADE_DURATION).start(); - mActiveMarker.animate().alpha(0f) - .setDuration(MARKER_FADE_DURATION).start(); + void inactivate(boolean immediate) { + if (immediate) { + mInactiveMarker.animate().cancel(); + mInactiveMarker.setAlpha(1f); + mActiveMarker.animate().cancel(); + mActiveMarker.setAlpha(0f); + } else { + mInactiveMarker.animate().alpha(1f) + .setDuration(MARKER_FADE_DURATION).start(); + mActiveMarker.animate().alpha(0f) + .setDuration(MARKER_FADE_DURATION).start(); + } + mIsActive = false; + } + + boolean isActive() { + return mIsActive; } }