1b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee/* 2b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * Copyright (C) 2015 The Android Open Source Project 3b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * 4b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * Licensed under the Apache License, Version 2.0 (the "License"); 5b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * you may not use this file except in compliance with the License. 6b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * You may obtain a copy of the License at 7b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * 8b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * http://www.apache.org/licenses/LICENSE-2.0 9b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * 10b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * Unless required by applicable law or agreed to in writing, software 11b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * distributed under the License is distributed on an "AS IS" BASIS, 12b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * See the License for the specific language governing permissions and 14b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * limitations under the License. 15b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee */ 16b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 17b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leepackage android.support.v17.leanback.widget; 18b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 19b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.animation.Animator; 20b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.animation.AnimatorSet; 216a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Leeimport android.animation.ObjectAnimator; 226a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Leeimport android.animation.TimeInterpolator; 23b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.content.Context; 24b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.content.res.Resources; 259b5ef3e2e264a624058bc514e05cf5e5dc8d94b9Chulwoo Leeimport android.content.res.TypedArray; 26b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Bitmap; 27b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.BitmapFactory; 28b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Canvas; 29b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Color; 306cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Leeimport android.graphics.Matrix; 31b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Paint; 32b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.graphics.Rect; 33b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.support.annotation.ColorInt; 34b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.support.annotation.VisibleForTesting; 35b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.support.v17.leanback.R; 36b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.util.AttributeSet; 376a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Leeimport android.util.Property; 38b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leeimport android.view.View; 396a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Leeimport android.view.animation.DecelerateInterpolator; 40b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 41b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee/** 42b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * A page indicator with dots. 43b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * @hide 44b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee */ 45b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Leepublic class PagingIndicator extends View { 466a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private static final long DURATION_ALPHA = 167; 476a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private static final long DURATION_DIAMETER = 417; 486a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private static final long DURATION_TRANSLATION_X = DURATION_DIAMETER; 496a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private static final TimeInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator(); 506a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 516a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private static final Property<Dot, Float> DOT_ALPHA 526a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee = new Property<Dot, Float>(Float.class, "alpha") { 536a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee @Override 546a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee public Float get(Dot dot) { 556a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee return dot.getAlpha(); 566a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 576a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 586a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee @Override 596a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee public void set(Dot dot, Float value) { 606a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee dot.setAlpha(value); 616a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 626a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee }; 636a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 646a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private static final Property<Dot, Float> DOT_DIAMETER 656a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee = new Property<Dot, Float>(Float.class, "diameter") { 666a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee @Override 676a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee public Float get(Dot dot) { 686a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee return dot.getDiameter(); 696a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 706a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 716a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee @Override 726a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee public void set(Dot dot, Float value) { 736a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee dot.setDiameter(value); 746a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 756a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee }; 766a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 776a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private static final Property<Dot, Float> DOT_TRANSLATION_X 786a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee = new Property<Dot, Float>(Float.class, "translation_x") { 796a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee @Override 806a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee public Float get(Dot dot) { 816a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee return dot.getTranslationX(); 826a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 836a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 846a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee @Override 856a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee public void set(Dot dot, Float value) { 866a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee dot.setTranslationX(value); 876a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 886a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee }; 896a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 90b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee // attribute 916cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee private boolean mIsLtr; 92b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final int mDotDiameter; 93b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final int mDotRadius; 94b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final int mDotGap; 95b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final int mArrowDiameter; 96b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final int mArrowRadius; 97b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final int mArrowGap; 98b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final int mShadowRadius; 99b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private Dot[] mDots; 100b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee // X position when the dot is selected. 101b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private int[] mDotSelectedX; 102b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee // X position when the dot is located to the left of the selected dot. 1036cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee private int[] mDotSelectedPrevX; 104b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee // X position when the dot is located to the right of the selected dot. 1056cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee private int[] mDotSelectedNextX; 106b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private int mDotCenterY; 107b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 108b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee // state 109b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private int mPageCount; 110b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private int mCurrentPage; 111b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private int mPreviousPage; 112b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 113b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee // drawing 114b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @ColorInt 115b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final int mDotFgSelectColor; 116b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final Paint mBgPaint; 117b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final Paint mFgPaint; 1186a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private final AnimatorSet mShowAnimator; 1196a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private final AnimatorSet mHideAnimator; 120b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final AnimatorSet mAnimator = new AnimatorSet(); 1216cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee private Bitmap mArrow; 122b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final Rect mArrowRect; 123b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private final float mArrowToBgRatio; 124b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 125b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public PagingIndicator(Context context) { 126b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee this(context, null, 0); 127b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 128b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 129b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public PagingIndicator(Context context, AttributeSet attrs) { 130b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee this(context, attrs, 0); 131b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 132b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 133b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public PagingIndicator(Context context, AttributeSet attrs, int defStyle) { 134b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee super(context, attrs, defStyle); 135b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee Resources res = getResources(); 1369b5ef3e2e264a624058bc514e05cf5e5dc8d94b9Chulwoo Lee TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PagingIndicator, 1379b5ef3e2e264a624058bc514e05cf5e5dc8d94b9Chulwoo Lee defStyle, 0); 1386a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mDotRadius = getDimensionFromTypedArray(typedArray, R.styleable.PagingIndicator_dotRadius, 1396a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee R.dimen.lb_page_indicator_dot_radius); 140b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDotDiameter = mDotRadius * 2; 1416a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mArrowRadius = getDimensionFromTypedArray(typedArray, 1426a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee R.styleable.PagingIndicator_arrowRadius, R.dimen.lb_page_indicator_arrow_radius); 1436a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mArrowDiameter = mArrowRadius * 2; 1446a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mDotGap = getDimensionFromTypedArray(typedArray, R.styleable.PagingIndicator_dotToDotGap, 1456a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee R.dimen.lb_page_indicator_dot_gap); 1466a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mArrowGap = getDimensionFromTypedArray(typedArray, 1476a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee R.styleable.PagingIndicator_dotToArrowGap, R.dimen.lb_page_indicator_arrow_gap); 1486a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee int bgColor = getColorFromTypedArray(typedArray, R.styleable.PagingIndicator_dotBgColor, 1496a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee R.color.lb_page_indicator_dot); 150b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 151b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mBgPaint.setColor(bgColor); 1526a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mDotFgSelectColor = getColorFromTypedArray(typedArray, 1536a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee R.styleable.PagingIndicator_arrowBgColor, 1546a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee R.color.lb_page_indicator_arrow_background); 1556a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee typedArray.recycle(); 1566a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mIsLtr = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; 1576a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee int shadowColor = res.getColor(R.color.lb_page_indicator_arrow_shadow); 158b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mShadowRadius = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_shadow_radius); 159b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mFgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 160b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int shadowOffset = res.getDimensionPixelSize(R.dimen.lb_page_indicator_arrow_shadow_offset); 161b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mFgPaint.setShadowLayer(mShadowRadius, shadowOffset, shadowOffset, shadowColor); 1626cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mArrow = loadArrow(); 163b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mArrowRect = new Rect(0, 0, mArrow.getWidth(), mArrow.getHeight()); 164b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mArrowToBgRatio = (float) mArrow.getWidth() / (float) mArrowDiameter; 165b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee // Initialize animations. 1666a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mShowAnimator = new AnimatorSet(); 1676a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mShowAnimator.playTogether(createDotAlphaAnimator(0.0f, 1.0f), 1686a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee createDotDiameterAnimator(mDotRadius * 2, mArrowRadius * 2), 1696a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee createDotTranslationXAnimator()); 1706a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mHideAnimator = new AnimatorSet(); 1716a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mHideAnimator.playTogether(createDotAlphaAnimator(1.0f, 0.0f), 1726a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee createDotDiameterAnimator(mArrowRadius * 2, mDotRadius * 2), 1736a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee createDotTranslationXAnimator()); 1746a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee mAnimator.playTogether(mShowAnimator, mHideAnimator); 175b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee // Use software layer to show shadows. 176b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee setLayerType(View.LAYER_TYPE_SOFTWARE, null); 1776a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 1786a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 1796a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private int getDimensionFromTypedArray(TypedArray typedArray, int attr, int defaultId) { 1806a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee return typedArray.getDimensionPixelOffset(attr, 1816a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee getResources().getDimensionPixelOffset(defaultId)); 1826a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 1831dd331211beded89ec02c37d68afced18d329375Chulwoo Lee 1846a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private int getColorFromTypedArray(TypedArray typedArray, int attr, int defaultId) { 1856a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee return typedArray.getColor(attr, getResources().getColor(defaultId)); 186b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 187b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 1886cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee private Bitmap loadArrow() { 1896cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee Bitmap arrow = BitmapFactory.decodeResource(getResources(), R.drawable.lb_ic_nav_arrow); 1906cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee if (mIsLtr) { 1916cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee return arrow; 1926cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } else { 1936cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee Matrix matrix = new Matrix(); 1946cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee matrix.preScale(-1, 1); 1956cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee return Bitmap.createBitmap(arrow, 0, 0, arrow.getWidth(), arrow.getHeight(), matrix, 1966cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee false); 1976cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 1986cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 1996cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee 2006a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private Animator createDotAlphaAnimator(float from, float to) { 2016a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_ALPHA, from, to); 2026a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee animator.setDuration(DURATION_ALPHA); 2036a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee animator.setInterpolator(DECELERATE_INTERPOLATOR); 2046a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee return animator; 2056a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 2066a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 2076a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private Animator createDotDiameterAnimator(float from, float to) { 2086a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_DIAMETER, from, to); 2096a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee animator.setDuration(DURATION_DIAMETER); 2106a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee animator.setInterpolator(DECELERATE_INTERPOLATOR); 2116a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee return animator; 2126a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 2136a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 2146a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee private Animator createDotTranslationXAnimator() { 2156a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee // The direction is determined in the Dot. 2166a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee ObjectAnimator animator = ObjectAnimator.ofFloat(null, DOT_TRANSLATION_X, 2176a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee -mArrowGap + mDotGap, 0.0f); 2186a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee animator.setDuration(DURATION_TRANSLATION_X); 2196a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee animator.setInterpolator(DECELERATE_INTERPOLATOR); 2206a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee return animator; 2216a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee } 2226a8b0fd35afda4d204e3f46d192bf20f510185d9Chulwoo Lee 223b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee /** 224b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * Sets the page count. 225b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee */ 226b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public void setPageCount(int pages) { 227b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee if (pages <= 0) { 228b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee throw new IllegalArgumentException("The page count should be a positive integer"); 229b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 230b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mPageCount = pages; 231b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots = new Dot[mPageCount]; 232b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee for (int i = 0; i < mPageCount; ++i) { 233b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[i] = new Dot(); 234b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 235b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee calculateDotPositions(); 236b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee setSelectedPage(0); 237b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 238b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 239b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee /** 240b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee * Called when the page has been selected. 241b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee */ 242b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public void onPageSelected(int pageIndex, boolean withAnimation) { 243b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee if (mCurrentPage == pageIndex) { 244b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return; 245b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 246b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee if (mAnimator.isStarted()) { 247b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mAnimator.end(); 248b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 249b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mPreviousPage = mCurrentPage; 250b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee if (withAnimation) { 251b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mHideAnimator.setTarget(mDots[mPreviousPage]); 252b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mShowAnimator.setTarget(mDots[pageIndex]); 253b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mAnimator.start(); 254b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 255b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee setSelectedPage(pageIndex); 256b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 257b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 258b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private void calculateDotPositions() { 259b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int left = getPaddingLeft(); 260b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int top = getPaddingTop(); 261b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int right = getWidth() - getPaddingRight(); 262b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int requiredWidth = getRequiredWidth(); 263b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int mid = (left + right) / 2; 264b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDotSelectedX = new int[mPageCount]; 2656cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedPrevX = new int[mPageCount]; 2666cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedNextX = new int[mPageCount]; 2676cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee if (mIsLtr) { 2686cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee int startLeft = mid - requiredWidth / 2; 2696cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee // mDotSelectedX[0] should be mDotSelectedPrevX[-1] + mArrowGap 2706cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedX[0] = startLeft + mDotRadius - mDotGap + mArrowGap; 2716cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedPrevX[0] = startLeft + mDotRadius; 2726cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedNextX[0] = startLeft + mDotRadius - 2 * mDotGap + 2 * mArrowGap; 2736cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee for (int i = 1; i < mPageCount; i++) { 2746cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedX[i] = mDotSelectedPrevX[i - 1] + mArrowGap; 2756cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedPrevX[i] = mDotSelectedPrevX[i - 1] + mDotGap; 2766cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedNextX[i] = mDotSelectedX[i - 1] + mArrowGap; 2776cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 2786cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } else { 2796cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee int startRight = mid + requiredWidth / 2; 2806cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee // mDotSelectedX[0] should be mDotSelectedPrevX[-1] - mArrowGap 2816cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedX[0] = startRight - mDotRadius + mDotGap - mArrowGap; 2826cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedPrevX[0] = startRight - mDotRadius; 2836cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedNextX[0] = startRight - mDotRadius + 2 * mDotGap - 2 * mArrowGap; 2846cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee for (int i = 1; i < mPageCount; i++) { 2856cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedX[i] = mDotSelectedPrevX[i - 1] - mArrowGap; 2866cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedPrevX[i] = mDotSelectedPrevX[i - 1] - mDotGap; 2876cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDotSelectedNextX[i] = mDotSelectedX[i - 1] - mArrowGap; 2886cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 289b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 290b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDotCenterY = top + mArrowRadius; 291b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee adjustDotPosition(); 292b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 293b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 294b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @VisibleForTesting 295b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int getPageCount() { 296b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return mPageCount; 297b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 298b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 299b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @VisibleForTesting 300b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int[] getDotSelectedX() { 301b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return mDotSelectedX; 302b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 303b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 304b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @VisibleForTesting 305b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int[] getDotSelectedLeftX() { 3066cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee return mDotSelectedPrevX; 307b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 308b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 309b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @VisibleForTesting 310b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int[] getDotSelectedRightX() { 3116cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee return mDotSelectedNextX; 312b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 313b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 314b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @Override 315b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 316b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int desiredHeight = getDesiredHeight(); 317b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int height; 318b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee switch (MeasureSpec.getMode(heightMeasureSpec)) { 319b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee case MeasureSpec.EXACTLY: 320b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee height = MeasureSpec.getSize(heightMeasureSpec); 321b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee break; 322b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee case MeasureSpec.AT_MOST: 323b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee height = Math.min(desiredHeight, MeasureSpec.getSize(heightMeasureSpec)); 324b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee break; 325b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee case MeasureSpec.UNSPECIFIED: 326b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee default: 327b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee height = desiredHeight; 328b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee break; 329b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 330b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int desiredWidth = getDesiredWidth(); 331b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int width; 332b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee switch (MeasureSpec.getMode(widthMeasureSpec)) { 333b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee case MeasureSpec.EXACTLY: 334b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee width = MeasureSpec.getSize(widthMeasureSpec); 335b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee break; 336b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee case MeasureSpec.AT_MOST: 337b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee width = Math.min(desiredWidth, MeasureSpec.getSize(widthMeasureSpec)); 338b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee break; 339b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee case MeasureSpec.UNSPECIFIED: 340b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee default: 341b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee width = desiredWidth; 342b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee break; 343b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 344b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee setMeasuredDimension(width, height); 345b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 346b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 347b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @Override 348b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee protected void onSizeChanged(int width, int height, int oldWidth, int oldHeight) { 349b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee setMeasuredDimension(width, height); 350b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee calculateDotPositions(); 351b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 352b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 353b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private int getDesiredHeight() { 354b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return getPaddingTop() + mArrowDiameter + getPaddingBottom() + mShadowRadius; 355b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 356b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 357b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private int getRequiredWidth() { 358b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return 2 * mDotRadius + 2 * mArrowGap + (mPageCount - 3) * mDotGap; 359b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 360b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 361b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private int getDesiredWidth() { 362b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return getPaddingLeft() + getRequiredWidth() + getPaddingRight(); 363b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 364b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 365b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @Override 366b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee protected void onDraw(Canvas canvas) { 367b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee for (int i = 0; i < mPageCount; ++i) { 368b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[i].draw(canvas); 369b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 370b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 371b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 372b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private void setSelectedPage(int now) { 373b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee if (now == mCurrentPage) { 374b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return; 375b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 376b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 377b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mCurrentPage = now; 378b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee adjustDotPosition(); 379b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 380b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 381b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee private void adjustDotPosition() { 382b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee for (int i = 0; i < mCurrentPage; ++i) { 383b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[i].deselect(); 384b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[i].mDirection = i == mPreviousPage ? Dot.LEFT : Dot.RIGHT; 3856cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDots[i].mCenterX = mDotSelectedPrevX[i]; 386b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 387b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[mCurrentPage].select(); 388b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[mCurrentPage].mDirection = mPreviousPage < mCurrentPage ? Dot.LEFT : Dot.RIGHT; 389b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[mCurrentPage].mCenterX = mDotSelectedX[mCurrentPage]; 390b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee for (int i = mCurrentPage + 1; i < mPageCount; ++i) { 391b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[i].deselect(); 392b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDots[i].mDirection = Dot.RIGHT; 3936cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mDots[i].mCenterX = mDotSelectedNextX[i]; 3946cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 3956cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 3966cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee 3976cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee @Override 3986cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee public void onRtlPropertiesChanged(int layoutDirection) { 3996cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee super.onRtlPropertiesChanged(layoutDirection); 4006cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee boolean isLtr = layoutDirection == View.LAYOUT_DIRECTION_LTR; 4016cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee if (mIsLtr != isLtr) { 4026cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mIsLtr = isLtr; 4036cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mArrow = loadArrow(); 4046cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee if (mDots != null) { 4056cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee for (Dot dot : mDots) { 4066cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee dot.onRtlPropertiesChanged(); 4076cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 4086cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 4096cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee calculateDotPositions(); 4106cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee invalidate(); 411b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 412b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 413b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 414b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public class Dot { 415b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee static final float LEFT = -1; 416b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee static final float RIGHT = 1; 4176cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee static final float LTR = 1; 4186cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee static final float RTL = -1; 419b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 420b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee float mAlpha; 421b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee @ColorInt 422b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int mFgColor; 423b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee float mTranslationX; 424b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee float mCenterX; 425b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee float mDiameter; 426b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee float mRadius; 427b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee float mArrowImageRadius; 428b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee float mDirection = RIGHT; 4296cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee float mLayoutDirection = mIsLtr ? LTR : RTL; 430b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 431b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee void select() { 432b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mTranslationX = 0.0f; 433b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mCenterX = 0.0f; 434b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDiameter = mArrowDiameter; 435b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mRadius = mArrowRadius; 436b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mArrowImageRadius = mRadius * mArrowToBgRatio; 437b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mAlpha = 1.0f; 438b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee adjustAlpha(); 439b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 440b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 441b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee void deselect() { 442b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mTranslationX = 0.0f; 443b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mCenterX = 0.0f; 444b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mDiameter = mDotDiameter; 445b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mRadius = mDotRadius; 446b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mArrowImageRadius = mRadius * mArrowToBgRatio; 447b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mAlpha = 0.0f; 448b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee adjustAlpha(); 449b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 450b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 451b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public void adjustAlpha() { 452b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int alpha = Math.round(0xFF * mAlpha); 453b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int red = Color.red(mDotFgSelectColor); 454b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int green = Color.green(mDotFgSelectColor); 455b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee int blue = Color.blue(mDotFgSelectColor); 456b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mFgColor = Color.argb(alpha, red, green, blue); 457b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 458b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 459b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public float getAlpha() { 460b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return mAlpha; 461b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 462b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 463b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public void setAlpha(float alpha) { 464b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee this.mAlpha = alpha; 465b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee adjustAlpha(); 466b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee invalidate(); 467b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 468b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 469b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public float getTranslationX() { 470b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return mTranslationX; 471b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 472b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 473b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public void setTranslationX(float translationX) { 4746cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee this.mTranslationX = translationX * mDirection * mLayoutDirection; 475b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee invalidate(); 476b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 477b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 478b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public float getDiameter() { 479b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee return mDiameter; 480b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 481b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 482b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee public void setDiameter(float diameter) { 483b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee this.mDiameter = diameter; 484b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee this.mRadius = diameter / 2; 485b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee this.mArrowImageRadius = diameter / 2 * mArrowToBgRatio; 486b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee invalidate(); 487b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 488b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee 489b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee void draw(Canvas canvas) { 490b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee float centerX = mCenterX + mTranslationX; 491b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee canvas.drawCircle(centerX, mDotCenterY, mRadius, mBgPaint); 492b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee if (mAlpha > 0) { 493b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee mFgPaint.setColor(mFgColor); 494b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee canvas.drawCircle(centerX, mDotCenterY, mRadius, mFgPaint); 495b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee canvas.drawBitmap(mArrow, mArrowRect, new Rect((int) (centerX - mArrowImageRadius), 496b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee (int) (mDotCenterY - mArrowImageRadius), 497b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee (int) (centerX + mArrowImageRadius), 498b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee (int) (mDotCenterY + mArrowImageRadius)), null); 499b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 500b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 5016cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee 5026cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee void onRtlPropertiesChanged() { 5036cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee mLayoutDirection = mIsLtr ? LTR : RTL; 5046cb24d438e700ceb0a8b7d5a93683b19eae6c1b5Chulwoo Lee } 505b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee } 506b78877670c813ccfc46071a8d361f45185fd06f4Chulwoo Lee} 507