1 /* 2 * Copyright (C) 2016 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.deskclock.stopwatch; 18 19 import android.content.Context; 20 import android.util.AttributeSet; 21 import android.view.View; 22 import android.view.ViewGroup; 23 24 import com.android.deskclock.R; 25 26 import static android.view.View.MeasureSpec.AT_MOST; 27 import static android.view.View.MeasureSpec.EXACTLY; 28 import static android.view.View.MeasureSpec.UNSPECIFIED; 29 30 /** 31 * Dynamically apportions size the stopwatch circle depending on the preferred width of the laps 32 * list and the container size. Layouts fall into two different buckets: 33 * 34 * When the width of the laps list is less than half the container width, the laps list and 35 * stopwatch display are each centered within half the container. 36 * <pre> 37 * --------------------------------------------------------------------------- 38 * | | Lap 5 | 39 * | | Lap 4 | 40 * | 21:45.67 | Lap 3 | 41 * | | Lap 2 | 42 * | | Lap 1 | 43 * --------------------------------------------------------------------------- 44 * </pre> 45 * 46 * When the width of the laps list is greater than half the container width, the laps list is 47 * granted all of the space it requires and the stopwatch display is centered within the remaining 48 * container width. 49 * <pre> 50 * --------------------------------------------------------------------------- 51 * | | Lap 5 | 52 * | | Lap 4 | 53 * | 21:45.67 | Lap 3 | 54 * | | Lap 2 | 55 * | | Lap 1 | 56 * --------------------------------------------------------------------------- 57 * </pre> 58 */ 59 public class StopwatchLandscapeLayout extends ViewGroup { 60 61 private View mLapsListView; 62 private View mStopwatchView; 63 StopwatchLandscapeLayout(Context context)64 public StopwatchLandscapeLayout(Context context) { 65 super(context); 66 } 67 StopwatchLandscapeLayout(Context context, AttributeSet attrs)68 public StopwatchLandscapeLayout(Context context, AttributeSet attrs) { 69 super(context, attrs); 70 } 71 StopwatchLandscapeLayout(Context context, AttributeSet attrs, int defStyleAttr)72 public StopwatchLandscapeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 73 super(context, attrs, defStyleAttr); 74 } 75 76 @Override onFinishInflate()77 protected void onFinishInflate() { 78 super.onFinishInflate(); 79 80 mLapsListView = findViewById(R.id.laps_list); 81 mStopwatchView = findViewById(R.id.stopwatch_time_wrapper); 82 } 83 84 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)85 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 86 final int height = MeasureSpec.getSize(heightMeasureSpec); 87 final int width = MeasureSpec.getSize(widthMeasureSpec); 88 final int halfWidth = width / 2; 89 90 final int minWidthSpec = MeasureSpec.makeMeasureSpec(width, UNSPECIFIED); 91 final int maxHeightSpec = MeasureSpec.makeMeasureSpec(height, AT_MOST); 92 93 // First determine the width of the laps list. 94 final int lapsListWidth; 95 if (mLapsListView != null && mLapsListView.getVisibility() != GONE) { 96 // Measure the intrinsic size of the laps list. 97 mLapsListView.measure(minWidthSpec, maxHeightSpec); 98 99 // Actual laps list width is the larger of half the container and its intrinsic width. 100 lapsListWidth = Math.max(mLapsListView.getMeasuredWidth(), halfWidth); 101 final int lapsListWidthSpec = MeasureSpec.makeMeasureSpec(lapsListWidth, EXACTLY); 102 mLapsListView.measure(lapsListWidthSpec, maxHeightSpec); 103 } else { 104 lapsListWidth = 0; 105 } 106 107 // Stopwatch timer consumes the remaining width of container not granted to laps list. 108 final int stopwatchWidth = width - lapsListWidth; 109 final int stopwatchWidthSpec = MeasureSpec.makeMeasureSpec(stopwatchWidth, EXACTLY); 110 mStopwatchView.measure(stopwatchWidthSpec, maxHeightSpec); 111 112 // Record the measured size of this container. 113 setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); 114 } 115 116 @Override onLayout(boolean changed, int l, int t, int r, int b)117 protected void onLayout(boolean changed, int l, int t, int r, int b) { 118 // Compute the space available for layout. 119 final int left = getPaddingLeft(); 120 final int top = getPaddingTop(); 121 final int right = getWidth() - getPaddingRight(); 122 final int bottom = getHeight() - getPaddingBottom(); 123 final int width = right - left; 124 final int height = bottom - top; 125 final int halfHeight = height / 2; 126 final boolean isLTR = getLayoutDirection() == LAYOUT_DIRECTION_LTR; 127 128 final int lapsListWidth; 129 if (mLapsListView != null && mLapsListView.getVisibility() != GONE) { 130 // Layout the laps list, centering it vertically. 131 lapsListWidth = mLapsListView.getMeasuredWidth(); 132 final int lapsListHeight = mLapsListView.getMeasuredHeight(); 133 final int lapsListTop = top + halfHeight - (lapsListHeight / 2); 134 final int lapsListBottom = lapsListTop + lapsListHeight; 135 final int lapsListLeft; 136 final int lapsListRight; 137 if (isLTR) { 138 lapsListLeft = right - lapsListWidth; 139 lapsListRight = right; 140 } else { 141 lapsListLeft = left; 142 lapsListRight = left + lapsListWidth; 143 } 144 145 mLapsListView.layout(lapsListLeft, lapsListTop, lapsListRight, lapsListBottom); 146 } else { 147 lapsListWidth = 0; 148 } 149 150 // Layout the stopwatch, centering it horizontally and vertically. 151 final int stopwatchWidth = mStopwatchView.getMeasuredWidth(); 152 final int stopwatchHeight = mStopwatchView.getMeasuredHeight(); 153 final int stopwatchTop = top + halfHeight - (stopwatchHeight / 2); 154 final int stopwatchBottom = stopwatchTop + stopwatchHeight; 155 final int stopwatchLeft; 156 final int stopwatchRight; 157 if (isLTR) { 158 stopwatchLeft = left + ((width - lapsListWidth - stopwatchWidth) / 2); 159 stopwatchRight = stopwatchLeft + stopwatchWidth; 160 } else { 161 stopwatchRight = right - ((width - lapsListWidth - stopwatchWidth) / 2); 162 stopwatchLeft = stopwatchRight - stopwatchWidth; 163 } 164 165 mStopwatchView.layout(stopwatchLeft, stopwatchTop, stopwatchRight, stopwatchBottom); 166 } 167 }