16e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein/* 26e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * Copyright (C) 2013 The Android Open Source Project 36e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * 46e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * Licensed under the Apache License, Version 2.0 (the "License"); 56e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * you may not use this file except in compliance with the License. 66e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * You may obtain a copy of the License at 76e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * 86e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * http://www.apache.org/licenses/LICENSE-2.0 96e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * 106e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * Unless required by applicable law or agreed to in writing, software 116e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * distributed under the License is distributed on an "AS IS" BASIS, 126e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * See the License for the specific language governing permissions and 146e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein * limitations under the License. 156e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein */ 166e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 17b8f95646fc0510eebfeaa27864023d630f34090fSam Blitzsteinpackage com.android.datetimepicker.time; 186e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 196e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.Keyframe; 206e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.ObjectAnimator; 216e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.PropertyValuesHolder; 226e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.ValueAnimator; 236e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.animation.ValueAnimator.AnimatorUpdateListener; 246e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.content.Context; 256e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.content.res.Resources; 266e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.graphics.Canvas; 276e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.graphics.Paint; 286e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.graphics.Typeface; 296e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.graphics.Paint.Align; 306e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.util.Log; 316e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport android.view.View; 326e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 336e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinimport com.android.datetimepicker.R; 346e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 35f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein/** 36f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * A view to show a series of numbers in a circular pattern. 37f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 386e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzsteinpublic class RadialTextsView extends View { 396e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private final static String TAG = "RadialTextsView"; 406e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 416e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private final Paint mPaint = new Paint(); 426e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 436e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mDrawValuesReady; 446e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mIsInitialized; 456e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 463d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein private Typeface mTypefaceLight; 473d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein private Typeface mTypefaceRegular; 486e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private String[] mTexts; 496e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private String[] mInnerTexts; 506e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mIs24HourMode; 516e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mHasInnerCircle; 526e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mCircleRadiusMultiplier; 536e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mAmPmCircleRadiusMultiplier; 546e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mNumbersRadiusMultiplier; 556e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mInnerNumbersRadiusMultiplier; 566e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mTextSizeMultiplier; 576e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mInnerTextSizeMultiplier; 586e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 596e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private int mXCenter; 606e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private int mYCenter; 616e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mCircleRadius; 626e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private boolean mTextGridValuesDirty; 636e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mTextSize; 646e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mInnerTextSize; 656e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float[] mTextGridHeights; 666e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float[] mTextGridWidths; 676e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float[] mInnerTextGridHeights; 686e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float[] mInnerTextGridWidths; 696e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 706e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mAnimationRadiusMultiplier; 716e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mTransitionMidRadiusMultiplier; 726e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private float mTransitionEndRadiusMultiplier; 736e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein ObjectAnimator mDisappearAnimator; 746e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein ObjectAnimator mReappearAnimator; 756e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private InvalidateUpdateListener mInvalidateUpdateListener; 766e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 776e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public RadialTextsView(Context context) { 786e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein super(context); 796e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mIsInitialized = false; 806e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 816e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 826e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public void initialize(Resources res, String[] texts, String[] innerTexts, 836e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein boolean is24HourMode, boolean disappearsOut) { 846e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mIsInitialized) { 856e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Log.e(TAG, "This RadialTextsView may only be initialized once."); 866e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return; 876e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 886e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 89f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Set up the paint. 90f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein int numbersTextColor = res.getColor(R.color.numbers_text_color); 91f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein mPaint.setColor(numbersTextColor); 923d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein String typefaceFamily = res.getString(R.string.radial_numbers_typeface); 933d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein mTypefaceLight = Typeface.create(typefaceFamily, Typeface.NORMAL); 943d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein String typefaceFamilyRegular = res.getString(R.string.sans_serif); 953d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein mTypefaceRegular = Typeface.create(typefaceFamilyRegular, Typeface.NORMAL); 966e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mPaint.setAntiAlias(true); 976e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mPaint.setTextAlign(Align.CENTER); 986e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 996e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTexts = texts; 1006e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerTexts = innerTexts; 1016e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mIs24HourMode = is24HourMode; 1026e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mHasInnerCircle = (innerTexts != null); 1036e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 104f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Calculate the radius for the main circle. 1056e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (is24HourMode) { 1066e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mCircleRadiusMultiplier = Float.parseFloat( 1076e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.circle_radius_multiplier_24HourMode)); 1086e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 1096e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mCircleRadiusMultiplier = Float.parseFloat( 1106e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.circle_radius_multiplier)); 1116e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mAmPmCircleRadiusMultiplier = 1126e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Float.parseFloat(res.getString(R.string.ampm_circle_radius_multiplier)); 1136e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1146e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 115f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Initialize the widths and heights of the grid, and calculate the values for the numbers. 1166e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextGridHeights = new float[7]; 1176e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextGridWidths = new float[7]; 1186e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mHasInnerCircle) { 1196e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mNumbersRadiusMultiplier = Float.parseFloat( 1206e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.numbers_radius_multiplier_outer)); 1216e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextSizeMultiplier = Float.parseFloat( 1226e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.text_size_multiplier_outer)); 1236e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerNumbersRadiusMultiplier = Float.parseFloat( 1246e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.numbers_radius_multiplier_inner)); 1256e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerTextSizeMultiplier = Float.parseFloat( 1266e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.text_size_multiplier_inner)); 1276e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1286e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerTextGridHeights = new float[7]; 1296e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerTextGridWidths = new float[7]; 1306e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } else { 1316e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mNumbersRadiusMultiplier = Float.parseFloat( 1326e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.numbers_radius_multiplier_normal)); 1336e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextSizeMultiplier = Float.parseFloat( 1346e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein res.getString(R.string.text_size_multiplier_normal)); 1356e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1366e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1376e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mAnimationRadiusMultiplier = 1; 1386e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTransitionMidRadiusMultiplier = 1f + (0.05f * (disappearsOut? -1 : 1)); 1396e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTransitionEndRadiusMultiplier = 1f + (0.3f * (disappearsOut? 1 : -1)); 1406e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInvalidateUpdateListener = new InvalidateUpdateListener(); 1416e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1426e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextGridValuesDirty = true; 1436e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mIsInitialized = true; 1446e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1456e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1461f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein /* package */ void setTheme(Context context, boolean themeDark) { 1471f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein Resources res = context.getResources(); 1481f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein int textColor; 1491f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein if (themeDark) { 150a09b3c940e98b8606a16a94b48b6d0121d9d3635Scott Kennedy textColor = res.getColor(android.R.color.white); 1511f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein } else { 1521f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein textColor = res.getColor(R.color.numbers_text_color); 1531f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein } 1541f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein mPaint.setColor(textColor); 1551f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein } 1561f129e23db2dc5837a856f7734b15a5a8be6be94Sam Blitzstein 157f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 158f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Allows for smoother animation. 159f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 1603d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein @Override 1613d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein public boolean hasOverlappingRendering() { 1623d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein return false; 1633d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein } 1643d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein 165f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 166f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Used by the animation to move the numbers in and out. 167f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 1686e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public void setAnimationRadiusMultiplier(float animationRadiusMultiplier) { 1696e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mAnimationRadiusMultiplier = animationRadiusMultiplier; 1706e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextGridValuesDirty = true; 1716e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1726e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1736e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein @Override 1746e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public void onDraw(Canvas canvas) { 1756e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int viewWidth = getWidth(); 1766e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (viewWidth == 0 || !mIsInitialized) { 1776e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return; 1786e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1796e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1806e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (!mDrawValuesReady) { 1816e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mXCenter = getWidth() / 2; 1826e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mYCenter = getHeight() / 2; 1836e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mCircleRadius = Math.min(mXCenter, mYCenter) * mCircleRadiusMultiplier; 1846e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (!mIs24HourMode) { 1856e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // We'll need to draw the AM/PM circles, so the main circle will need to have 1866e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // a slightly higher center. To keep the entire view centered vertically, we'll 1876e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // have to push it up by half the radius of the AM/PM circles. 1886e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float amPmCircleRadius = mCircleRadius * mAmPmCircleRadiusMultiplier; 1896e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mYCenter -= amPmCircleRadius / 2; 1906e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1916e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 1926e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextSize = mCircleRadius * mTextSizeMultiplier; 1936e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mHasInnerCircle) { 1946e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerTextSize = mCircleRadius * mInnerTextSizeMultiplier; 1956e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 1966e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 197f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Because the text positions will be static, pre-render the animations. 1986e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein renderAnimations(); 1996e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2006e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextGridValuesDirty = true; 2016e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mDrawValuesReady = true; 2026e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2036e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 204f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Calculate the text positions, but only if they've changed since the last onDraw. 2056e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mTextGridValuesDirty) { 2066e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float numbersRadius = 2076e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mCircleRadius * mNumbersRadiusMultiplier * mAnimationRadiusMultiplier; 2086e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 209f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Calculate the positions for the 12 numbers in the main circle. 2106e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein calculateGridSizes(numbersRadius, mXCenter, mYCenter, 2116e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextSize, mTextGridHeights, mTextGridWidths); 2126e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mHasInnerCircle) { 213f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // If we have an inner circle, calculate those positions too. 2146e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float innerNumbersRadius = 2156e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mCircleRadius * mInnerNumbersRadiusMultiplier * mAnimationRadiusMultiplier; 2166e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein calculateGridSizes(innerNumbersRadius, mXCenter, mYCenter, 2176e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerTextSize, mInnerTextGridHeights, mInnerTextGridWidths); 2186e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2196e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mTextGridValuesDirty = false; 2206e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2216e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 222f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // Draw the texts in the pre-calculated positions. 2233d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein drawTexts(canvas, mTextSize, mTypefaceLight, mTexts, mTextGridWidths, mTextGridHeights); 2246e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein if (mHasInnerCircle) { 2253d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein drawTexts(canvas, mInnerTextSize, mTypefaceRegular, mInnerTexts, 2266e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mInnerTextGridWidths, mInnerTextGridHeights); 2276e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2286e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2296e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 230f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 231f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Using the trigonometric Unit Circle, calculate the positions that the text will need to be 232f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * drawn at based on the specified circle radius. Place the values in the textGridHeights and 233f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * textGridWidths parameters. 234f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 2356e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private void calculateGridSizes(float numbersRadius, float xCenter, float yCenter, 2366e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float textSize, float[] textGridHeights, float[] textGridWidths) { 2376e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein /* 238f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * The numbers need to be drawn in a 7x7 grid, representing the points on the Unit Circle. 2396e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein */ 2406e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float offset1 = numbersRadius; 2416e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // cos(30) = a / r => r * cos(30) = a => r * √3/2 = a 2426e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float offset2 = numbersRadius * ((float) Math.sqrt(3)) / 2f; 2436e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // sin(30) = o / r => r * sin(30) = o => r / 2 = a 2446e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float offset3 = numbersRadius / 2f; 2456e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mPaint.setTextSize(textSize); 246f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein // We'll need yTextBase to be slightly lower to account for the text's baseline. 2476e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein yCenter -= (mPaint.descent() + mPaint.ascent()) / 2; 248f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein 2496e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridHeights[0] = yCenter - offset1; 2506e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridWidths[0] = xCenter - offset1; 2516e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridHeights[1] = yCenter - offset2; 2526e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridWidths[1] = xCenter - offset2; 2536e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridHeights[2] = yCenter - offset3; 2546e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridWidths[2] = xCenter - offset3; 2556e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridHeights[3] = yCenter; 2566e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridWidths[3] = xCenter; 2576e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridHeights[4] = yCenter + offset3; 2586e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridWidths[4] = xCenter + offset3; 2596e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridHeights[5] = yCenter + offset2; 2606e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridWidths[5] = xCenter + offset2; 2616e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridHeights[6] = yCenter + offset1; 2626e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein textGridWidths[6] = xCenter + offset1; 2636e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2646e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 265f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 266f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Draw the 12 text values at the positions specified by the textGrid parameters. 267f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 2683d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein private void drawTexts(Canvas canvas, float textSize, Typeface typeface, String[] texts, 2696e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float[] textGridWidths, float[] textGridHeights) { 2706e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mPaint.setTextSize(textSize); 2713d5a23b698cb8c59f43914ea2f9bb4fb36575f88Sam Blitzstein mPaint.setTypeface(typeface); 2726e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[0], textGridWidths[3], textGridHeights[0], mPaint); 2736e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[1], textGridWidths[4], textGridHeights[1], mPaint); 2746e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[2], textGridWidths[5], textGridHeights[2], mPaint); 2756e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[3], textGridWidths[6], textGridHeights[3], mPaint); 2766e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[4], textGridWidths[5], textGridHeights[4], mPaint); 2776e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[5], textGridWidths[4], textGridHeights[5], mPaint); 2786e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[6], textGridWidths[3], textGridHeights[6], mPaint); 2796e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[7], textGridWidths[2], textGridHeights[5], mPaint); 2806e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[8], textGridWidths[1], textGridHeights[4], mPaint); 2816e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[9], textGridWidths[0], textGridHeights[3], mPaint); 2826e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[10], textGridWidths[1], textGridHeights[2], mPaint); 2836e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein canvas.drawText(texts[11], textGridWidths[2], textGridHeights[1], mPaint); 2846e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 2856e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 286f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein /** 287f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein * Render the animations for appearing and disappearing. 288f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein */ 2896e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private void renderAnimations() { 2906e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Keyframe kf0, kf1, kf2, kf3; 2916e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float midwayPoint = 0.2f; 2926e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int duration = 500; 2936e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 2946e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // Set up animator for disappearing. 2956e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf0 = Keyframe.ofFloat(0f, 1); 2966e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf1 = Keyframe.ofFloat(midwayPoint, mTransitionMidRadiusMultiplier); 2976e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf2 = Keyframe.ofFloat(1f, mTransitionEndRadiusMultiplier); 2986e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein PropertyValuesHolder radiusDisappear = PropertyValuesHolder.ofKeyframe( 2996e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein "animationRadiusMultiplier", kf0, kf1, kf2); 3006e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3016e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf0 = Keyframe.ofFloat(0f, 1f); 3026e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf1 = Keyframe.ofFloat(1f, 0f); 3036e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein PropertyValuesHolder fadeOut = PropertyValuesHolder.ofKeyframe("alpha", kf0, kf1); 3046e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3056e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mDisappearAnimator = ObjectAnimator.ofPropertyValuesHolder( 3066e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein this, radiusDisappear, fadeOut).setDuration(duration); 3076e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mDisappearAnimator.addUpdateListener(mInvalidateUpdateListener); 3086e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3096e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3106e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein // Set up animator for reappearing. 311b8f95646fc0510eebfeaa27864023d630f34090fSam Blitzstein float delayMultiplier = 0.25f; 312b8f95646fc0510eebfeaa27864023d630f34090fSam Blitzstein float transitionDurationMultiplier = 1f; 3136e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float totalDurationMultiplier = transitionDurationMultiplier + delayMultiplier; 3146e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein int totalDuration = (int) (duration * totalDurationMultiplier); 3156e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein float delayPoint = (delayMultiplier * duration) / totalDuration; 3166e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein midwayPoint = 1 - (midwayPoint * (1 - delayPoint)); 3176e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3186e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf0 = Keyframe.ofFloat(0f, mTransitionEndRadiusMultiplier); 3196e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf1 = Keyframe.ofFloat(delayPoint, mTransitionEndRadiusMultiplier); 3206e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf2 = Keyframe.ofFloat(midwayPoint, mTransitionMidRadiusMultiplier); 3216e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf3 = Keyframe.ofFloat(1f, 1); 3226e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein PropertyValuesHolder radiusReappear = PropertyValuesHolder.ofKeyframe( 3236e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein "animationRadiusMultiplier", kf0, kf1, kf2, kf3); 3246e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3256e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf0 = Keyframe.ofFloat(0f, 0f); 3266e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf1 = Keyframe.ofFloat(delayPoint, 0f); 3276e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein kf2 = Keyframe.ofFloat(1f, 1f); 3286e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein PropertyValuesHolder fadeIn = PropertyValuesHolder.ofKeyframe("alpha", kf0, kf1, kf2); 3296e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3306e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mReappearAnimator = ObjectAnimator.ofPropertyValuesHolder( 3316e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein this, radiusReappear, fadeIn).setDuration(totalDuration); 3326e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein mReappearAnimator.addUpdateListener(mInvalidateUpdateListener); 3336e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3346e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3356e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public ObjectAnimator getDisappearAnimator() { 336f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein if (!mIsInitialized || !mDrawValuesReady || mDisappearAnimator == null) { 3376e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Log.e(TAG, "RadialTextView was not ready for animation."); 3386e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return null; 3396e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3406e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3416e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return mDisappearAnimator; 3426e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3436e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3446e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public ObjectAnimator getReappearAnimator() { 345f3b38bd61d583d31200c501f5a74392aac510657Sam Blitzstein if (!mIsInitialized || !mDrawValuesReady || mReappearAnimator == null) { 3466e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein Log.e(TAG, "RadialTextView was not ready for animation."); 3476e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return null; 3486e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3496e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3506e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein return mReappearAnimator; 3516e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3526e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein 3536e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein private class InvalidateUpdateListener implements AnimatorUpdateListener { 3546e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein @Override 3556e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein public void onAnimationUpdate(ValueAnimator animation) { 3566e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein RadialTextsView.this.invalidate(); 3576e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3586e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein } 3596e896f805cac499b777c98755149f07ccd7ba5c3Sam Blitzstein} 360