Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.settings.widget; |
| 18 | |
| 19 | import android.content.Context; |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 20 | import android.content.res.TypedArray; |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 21 | import android.graphics.Canvas; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 22 | import android.graphics.Color; |
| 23 | import android.graphics.Paint; |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 24 | import android.graphics.Paint.Style; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 25 | import android.graphics.Point; |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 26 | import android.graphics.Rect; |
| 27 | import android.graphics.drawable.Drawable; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 28 | import android.text.DynamicLayout; |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 29 | import android.text.Layout; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 30 | import android.text.Layout.Alignment; |
| 31 | import android.text.SpannableStringBuilder; |
| 32 | import android.text.TextPaint; |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 33 | import android.util.AttributeSet; |
| 34 | import android.util.MathUtils; |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 35 | import android.view.MotionEvent; |
| 36 | import android.view.View; |
| 37 | |
Jeff Sharkey | e6c5003 | 2013-03-06 11:46:54 -0800 | [diff] [blame] | 38 | import com.android.internal.util.Preconditions; |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 39 | import com.android.settings.R; |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 40 | |
| 41 | /** |
| 42 | * Sweep across a {@link ChartView} at a specific {@link ChartAxis} value, which |
| 43 | * a user can drag. |
| 44 | */ |
Jeff Sharkey | 54d0af5 | 2011-08-11 18:26:57 -0700 | [diff] [blame] | 45 | public class ChartSweepView extends View { |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 46 | |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 47 | private static final boolean DRAW_OUTLINE = false; |
| 48 | |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 49 | // TODO: clean up all the various padding/offset/margins |
| 50 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 51 | private Drawable mSweep; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 52 | private Rect mSweepPadding = new Rect(); |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 53 | |
| 54 | /** Offset of content inside this view. */ |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 55 | private Rect mContentOffset = new Rect(); |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 56 | /** Offset of {@link #mSweep} inside this view. */ |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 57 | private Point mSweepOffset = new Point(); |
| 58 | |
| 59 | private Rect mMargins = new Rect(); |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 60 | private float mNeighborMargin; |
Jeff Sharkey | b654846 | 2014-07-21 15:38:06 -0700 | [diff] [blame] | 61 | private int mSafeRegion; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 62 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 63 | private int mFollowAxis; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 64 | |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 65 | private int mLabelMinSize; |
| 66 | private float mLabelSize; |
| 67 | |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 68 | private int mLabelTemplateRes; |
| 69 | private int mLabelColor; |
| 70 | |
| 71 | private SpannableStringBuilder mLabelTemplate; |
| 72 | private DynamicLayout mLabelLayout; |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 73 | |
| 74 | private ChartAxis mAxis; |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 75 | private long mValue; |
Jeff Sharkey | 28130d9 | 2011-09-02 16:10:24 -0700 | [diff] [blame] | 76 | private long mLabelValue; |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 77 | |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 78 | private long mValidAfter; |
| 79 | private long mValidBefore; |
| 80 | private ChartSweepView mValidAfterDynamic; |
| 81 | private ChartSweepView mValidBeforeDynamic; |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 82 | |
Jeff Sharkey | b98c55b | 2011-09-11 17:29:49 -0700 | [diff] [blame] | 83 | private float mLabelOffset; |
| 84 | |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 85 | private Paint mOutlinePaint = new Paint(); |
| 86 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 87 | public static final int HORIZONTAL = 0; |
| 88 | public static final int VERTICAL = 1; |
| 89 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 90 | private int mTouchMode = MODE_NONE; |
| 91 | |
| 92 | private static final int MODE_NONE = 0; |
| 93 | private static final int MODE_DRAG = 1; |
| 94 | private static final int MODE_LABEL = 2; |
| 95 | |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 96 | private static final int LARGE_WIDTH = 1024; |
| 97 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 98 | private long mDragInterval = 1; |
| 99 | |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 100 | public interface OnSweepListener { |
| 101 | public void onSweep(ChartSweepView sweep, boolean sweepDone); |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 102 | public void requestEdit(ChartSweepView sweep); |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 103 | } |
| 104 | |
| 105 | private OnSweepListener mListener; |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 106 | |
| 107 | private float mTrackingStart; |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 108 | private MotionEvent mTracking; |
| 109 | |
Jeff Sharkey | 461842a | 2011-09-25 18:22:48 -0700 | [diff] [blame] | 110 | private ChartSweepView[] mNeighbors = new ChartSweepView[0]; |
| 111 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 112 | public ChartSweepView(Context context) { |
Jeff Sharkey | 54d0af5 | 2011-08-11 18:26:57 -0700 | [diff] [blame] | 113 | this(context, null); |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 114 | } |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 115 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 116 | public ChartSweepView(Context context, AttributeSet attrs) { |
| 117 | this(context, attrs, 0); |
| 118 | } |
| 119 | |
| 120 | public ChartSweepView(Context context, AttributeSet attrs, int defStyle) { |
| 121 | super(context, attrs, defStyle); |
| 122 | |
| 123 | final TypedArray a = context.obtainStyledAttributes( |
| 124 | attrs, R.styleable.ChartSweepView, defStyle, 0); |
| 125 | |
Jason Monk | 2d49953 | 2015-06-03 10:53:03 -0400 | [diff] [blame] | 126 | final int color = a.getColor(R.styleable.ChartSweepView_labelColor, Color.BLUE); |
| 127 | setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable), color); |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 128 | setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1)); |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 129 | setNeighborMargin(a.getDimensionPixelSize(R.styleable.ChartSweepView_neighborMargin, 0)); |
Jeff Sharkey | b654846 | 2014-07-21 15:38:06 -0700 | [diff] [blame] | 130 | setSafeRegion(a.getDimensionPixelSize(R.styleable.ChartSweepView_safeRegion, 0)); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 131 | |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 132 | setLabelMinSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0)); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 133 | setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0)); |
Jason Monk | 2d49953 | 2015-06-03 10:53:03 -0400 | [diff] [blame] | 134 | setLabelColor(color); |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 135 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 136 | // TODO: moved focused state directly into assets |
| 137 | setBackgroundResource(R.drawable.data_usage_sweep_background); |
| 138 | |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 139 | mOutlinePaint.setColor(Color.RED); |
| 140 | mOutlinePaint.setStrokeWidth(1f); |
| 141 | mOutlinePaint.setStyle(Style.STROKE); |
| 142 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 143 | a.recycle(); |
| 144 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 145 | setClickable(true); |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 146 | setOnClickListener(mClickListener); |
| 147 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 148 | setWillNotDraw(false); |
| 149 | } |
| 150 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 151 | private OnClickListener mClickListener = new OnClickListener() { |
| 152 | public void onClick(View v) { |
| 153 | dispatchRequestEdit(); |
| 154 | } |
| 155 | }; |
| 156 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 157 | void init(ChartAxis axis) { |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 158 | mAxis = Preconditions.checkNotNull(axis, "missing axis"); |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 159 | } |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 160 | |
Jeff Sharkey | 461842a | 2011-09-25 18:22:48 -0700 | [diff] [blame] | 161 | public void setNeighbors(ChartSweepView... neighbors) { |
| 162 | mNeighbors = neighbors; |
| 163 | } |
| 164 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 165 | public int getFollowAxis() { |
| 166 | return mFollowAxis; |
| 167 | } |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 168 | |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 169 | public Rect getMargins() { |
| 170 | return mMargins; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 171 | } |
| 172 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 173 | public void setDragInterval(long dragInterval) { |
| 174 | mDragInterval = dragInterval; |
| 175 | } |
| 176 | |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 177 | /** |
| 178 | * Return the number of pixels that the "target" area is inset from the |
| 179 | * {@link View} edge, along the current {@link #setFollowAxis(int)}. |
| 180 | */ |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 181 | private float getTargetInset() { |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 182 | if (mFollowAxis == VERTICAL) { |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 183 | final float targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top |
| 184 | - mSweepPadding.bottom; |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 185 | return mSweepPadding.top + (targetHeight / 2) + mSweepOffset.y; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 186 | } else { |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 187 | final float targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left |
| 188 | - mSweepPadding.right; |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 189 | return mSweepPadding.left + (targetWidth / 2) + mSweepOffset.x; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 190 | } |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 191 | } |
| 192 | |
| 193 | public void addOnSweepListener(OnSweepListener listener) { |
| 194 | mListener = listener; |
| 195 | } |
| 196 | |
| 197 | private void dispatchOnSweep(boolean sweepDone) { |
| 198 | if (mListener != null) { |
| 199 | mListener.onSweep(this, sweepDone); |
| 200 | } |
| 201 | } |
| 202 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 203 | private void dispatchRequestEdit() { |
| 204 | if (mListener != null) { |
| 205 | mListener.requestEdit(this); |
| 206 | } |
| 207 | } |
| 208 | |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 209 | @Override |
| 210 | public void setEnabled(boolean enabled) { |
| 211 | super.setEnabled(enabled); |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 212 | setFocusable(enabled); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 213 | requestLayout(); |
| 214 | } |
| 215 | |
Jason Monk | 2d49953 | 2015-06-03 10:53:03 -0400 | [diff] [blame] | 216 | public void setSweepDrawable(Drawable sweep, int color) { |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 217 | if (mSweep != null) { |
| 218 | mSweep.setCallback(null); |
| 219 | unscheduleDrawable(mSweep); |
| 220 | } |
| 221 | |
| 222 | if (sweep != null) { |
| 223 | sweep.setCallback(this); |
| 224 | if (sweep.isStateful()) { |
| 225 | sweep.setState(getDrawableState()); |
| 226 | } |
| 227 | sweep.setVisible(getVisibility() == VISIBLE, false); |
| 228 | mSweep = sweep; |
Jason Monk | 2d49953 | 2015-06-03 10:53:03 -0400 | [diff] [blame] | 229 | // Match the text. |
| 230 | mSweep.setTint(color); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 231 | sweep.getPadding(mSweepPadding); |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 232 | } else { |
| 233 | mSweep = null; |
| 234 | } |
| 235 | |
| 236 | invalidate(); |
| 237 | } |
| 238 | |
| 239 | public void setFollowAxis(int followAxis) { |
| 240 | mFollowAxis = followAxis; |
| 241 | } |
| 242 | |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 243 | public void setLabelMinSize(int minSize) { |
| 244 | mLabelMinSize = minSize; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 245 | invalidateLabelTemplate(); |
| 246 | } |
| 247 | |
| 248 | public void setLabelTemplate(int resId) { |
| 249 | mLabelTemplateRes = resId; |
| 250 | invalidateLabelTemplate(); |
| 251 | } |
| 252 | |
| 253 | public void setLabelColor(int color) { |
| 254 | mLabelColor = color; |
| 255 | invalidateLabelTemplate(); |
| 256 | } |
| 257 | |
| 258 | private void invalidateLabelTemplate() { |
| 259 | if (mLabelTemplateRes != 0) { |
| 260 | final CharSequence template = getResources().getText(mLabelTemplateRes); |
| 261 | |
| 262 | final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); |
| 263 | paint.density = getResources().getDisplayMetrics().density; |
| 264 | paint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale); |
| 265 | paint.setColor(mLabelColor); |
| 266 | |
| 267 | mLabelTemplate = new SpannableStringBuilder(template); |
Roozbeh Pournader | a7f6bb5 | 2017-08-22 16:54:27 -0700 | [diff] [blame] | 268 | mLabelLayout = DynamicLayout.Builder.obtain(mLabelTemplate, paint, LARGE_WIDTH) |
| 269 | .setAlignment(Alignment.ALIGN_RIGHT) |
| 270 | .setIncludePad(false) |
| 271 | .setUseLineSpacingFromFallbacks(true) |
| 272 | .build(); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 273 | invalidateLabel(); |
| 274 | |
| 275 | } else { |
| 276 | mLabelTemplate = null; |
| 277 | mLabelLayout = null; |
| 278 | } |
| 279 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 280 | invalidate(); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 281 | requestLayout(); |
| 282 | } |
| 283 | |
| 284 | private void invalidateLabel() { |
| 285 | if (mLabelTemplate != null && mAxis != null) { |
Jeff Sharkey | 28130d9 | 2011-09-02 16:10:24 -0700 | [diff] [blame] | 286 | mLabelValue = mAxis.buildLabel(getResources(), mLabelTemplate, mValue); |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 287 | setContentDescription(mLabelTemplate); |
Jeff Sharkey | b98c55b | 2011-09-11 17:29:49 -0700 | [diff] [blame] | 288 | invalidateLabelOffset(); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 289 | invalidate(); |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 290 | } else { |
| 291 | mLabelValue = mValue; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 292 | } |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 293 | } |
| 294 | |
Jeff Sharkey | b98c55b | 2011-09-11 17:29:49 -0700 | [diff] [blame] | 295 | /** |
| 296 | * When overlapping with neighbor, split difference and push label. |
| 297 | */ |
| 298 | public void invalidateLabelOffset() { |
| 299 | float margin; |
| 300 | float labelOffset = 0; |
| 301 | if (mFollowAxis == VERTICAL) { |
| 302 | if (mValidAfterDynamic != null) { |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 303 | mLabelSize = Math.max(getLabelWidth(this), getLabelWidth(mValidAfterDynamic)); |
Jeff Sharkey | b98c55b | 2011-09-11 17:29:49 -0700 | [diff] [blame] | 304 | margin = getLabelTop(mValidAfterDynamic) - getLabelBottom(this); |
| 305 | if (margin < 0) { |
| 306 | labelOffset = margin / 2; |
| 307 | } |
| 308 | } else if (mValidBeforeDynamic != null) { |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 309 | mLabelSize = Math.max(getLabelWidth(this), getLabelWidth(mValidBeforeDynamic)); |
Jeff Sharkey | b98c55b | 2011-09-11 17:29:49 -0700 | [diff] [blame] | 310 | margin = getLabelTop(this) - getLabelBottom(mValidBeforeDynamic); |
| 311 | if (margin < 0) { |
| 312 | labelOffset = -margin / 2; |
| 313 | } |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 314 | } else { |
| 315 | mLabelSize = getLabelWidth(this); |
Jeff Sharkey | b98c55b | 2011-09-11 17:29:49 -0700 | [diff] [blame] | 316 | } |
| 317 | } else { |
| 318 | // TODO: implement horizontal labels |
| 319 | } |
| 320 | |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 321 | mLabelSize = Math.max(mLabelSize, mLabelMinSize); |
| 322 | |
Jeff Sharkey | b98c55b | 2011-09-11 17:29:49 -0700 | [diff] [blame] | 323 | // when offsetting label, neighbor probably needs to offset too |
| 324 | if (labelOffset != mLabelOffset) { |
| 325 | mLabelOffset = labelOffset; |
| 326 | invalidate(); |
| 327 | if (mValidAfterDynamic != null) mValidAfterDynamic.invalidateLabelOffset(); |
| 328 | if (mValidBeforeDynamic != null) mValidBeforeDynamic.invalidateLabelOffset(); |
| 329 | } |
| 330 | } |
| 331 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 332 | @Override |
| 333 | public void jumpDrawablesToCurrentState() { |
| 334 | super.jumpDrawablesToCurrentState(); |
| 335 | if (mSweep != null) { |
| 336 | mSweep.jumpToCurrentState(); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | @Override |
| 341 | public void setVisibility(int visibility) { |
| 342 | super.setVisibility(visibility); |
| 343 | if (mSweep != null) { |
| 344 | mSweep.setVisible(visibility == VISIBLE, false); |
| 345 | } |
| 346 | } |
| 347 | |
| 348 | @Override |
| 349 | protected boolean verifyDrawable(Drawable who) { |
| 350 | return who == mSweep || super.verifyDrawable(who); |
| 351 | } |
| 352 | |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 353 | public ChartAxis getAxis() { |
| 354 | return mAxis; |
| 355 | } |
| 356 | |
Jeff Sharkey | 8a50364 | 2011-06-10 13:31:21 -0700 | [diff] [blame] | 357 | public void setValue(long value) { |
| 358 | mValue = value; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 359 | invalidateLabel(); |
Jeff Sharkey | 8a50364 | 2011-06-10 13:31:21 -0700 | [diff] [blame] | 360 | } |
| 361 | |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 362 | public long getValue() { |
| 363 | return mValue; |
| 364 | } |
| 365 | |
Jeff Sharkey | 28130d9 | 2011-09-02 16:10:24 -0700 | [diff] [blame] | 366 | public long getLabelValue() { |
| 367 | return mLabelValue; |
| 368 | } |
| 369 | |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 370 | public float getPoint() { |
Jeff Sharkey | 2af35fb | 2011-06-24 17:30:27 -0700 | [diff] [blame] | 371 | if (isEnabled()) { |
| 372 | return mAxis.convertToPoint(mValue); |
| 373 | } else { |
| 374 | // when disabled, show along top edge |
| 375 | return 0; |
| 376 | } |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 377 | } |
| 378 | |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 379 | /** |
| 380 | * Set valid range this sweep can move within, in {@link #mAxis} values. The |
| 381 | * most restrictive combination of all valid ranges is used. |
| 382 | */ |
| 383 | public void setValidRange(long validAfter, long validBefore) { |
| 384 | mValidAfter = validAfter; |
| 385 | mValidBefore = validBefore; |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 386 | } |
| 387 | |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 388 | public void setNeighborMargin(float neighborMargin) { |
| 389 | mNeighborMargin = neighborMargin; |
| 390 | } |
| 391 | |
Jeff Sharkey | b654846 | 2014-07-21 15:38:06 -0700 | [diff] [blame] | 392 | public void setSafeRegion(int safeRegion) { |
| 393 | mSafeRegion = safeRegion; |
| 394 | } |
| 395 | |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 396 | /** |
| 397 | * Set valid range this sweep can move within, defined by the given |
| 398 | * {@link ChartSweepView}. The most restrictive combination of all valid |
| 399 | * ranges is used. |
| 400 | */ |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 401 | public void setValidRangeDynamic(ChartSweepView validAfter, ChartSweepView validBefore) { |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 402 | mValidAfterDynamic = validAfter; |
| 403 | mValidBeforeDynamic = validBefore; |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 404 | } |
| 405 | |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 406 | /** |
| 407 | * Test if given {@link MotionEvent} is closer to another |
| 408 | * {@link ChartSweepView} compared to ourselves. |
| 409 | */ |
| 410 | public boolean isTouchCloserTo(MotionEvent eventInParent, ChartSweepView another) { |
Jeff Sharkey | 461842a | 2011-09-25 18:22:48 -0700 | [diff] [blame] | 411 | final float selfDist = getTouchDistanceFromTarget(eventInParent); |
| 412 | final float anotherDist = another.getTouchDistanceFromTarget(eventInParent); |
| 413 | return anotherDist < selfDist; |
| 414 | } |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 415 | |
Jeff Sharkey | 461842a | 2011-09-25 18:22:48 -0700 | [diff] [blame] | 416 | private float getTouchDistanceFromTarget(MotionEvent eventInParent) { |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 417 | if (mFollowAxis == HORIZONTAL) { |
Jeff Sharkey | 461842a | 2011-09-25 18:22:48 -0700 | [diff] [blame] | 418 | return Math.abs(eventInParent.getX() - (getX() + getTargetInset())); |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 419 | } else { |
Jeff Sharkey | 461842a | 2011-09-25 18:22:48 -0700 | [diff] [blame] | 420 | return Math.abs(eventInParent.getY() - (getY() + getTargetInset())); |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 421 | } |
| 422 | } |
| 423 | |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 424 | @Override |
| 425 | public boolean onTouchEvent(MotionEvent event) { |
Jeff Sharkey | 8a50364 | 2011-06-10 13:31:21 -0700 | [diff] [blame] | 426 | if (!isEnabled()) return false; |
| 427 | |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 428 | final View parent = (View) getParent(); |
| 429 | switch (event.getAction()) { |
| 430 | case MotionEvent.ACTION_DOWN: { |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 431 | |
| 432 | // only start tracking when in sweet spot |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 433 | final boolean acceptDrag; |
| 434 | final boolean acceptLabel; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 435 | if (mFollowAxis == VERTICAL) { |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 436 | acceptDrag = event.getX() > getWidth() - (mSweepPadding.right * 8); |
| 437 | acceptLabel = mLabelLayout != null ? event.getX() < mLabelLayout.getWidth() |
| 438 | : false; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 439 | } else { |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 440 | acceptDrag = event.getY() > getHeight() - (mSweepPadding.bottom * 8); |
| 441 | acceptLabel = mLabelLayout != null ? event.getY() < mLabelLayout.getHeight() |
| 442 | : false; |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 443 | } |
| 444 | |
| 445 | final MotionEvent eventInParent = event.copy(); |
| 446 | eventInParent.offsetLocation(getLeft(), getTop()); |
| 447 | |
| 448 | // ignore event when closer to a neighbor |
Jeff Sharkey | 461842a | 2011-09-25 18:22:48 -0700 | [diff] [blame] | 449 | for (ChartSweepView neighbor : mNeighbors) { |
| 450 | if (isTouchCloserTo(eventInParent, neighbor)) { |
| 451 | return false; |
| 452 | } |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 453 | } |
| 454 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 455 | if (acceptDrag) { |
| 456 | if (mFollowAxis == VERTICAL) { |
| 457 | mTrackingStart = getTop() - mMargins.top; |
| 458 | } else { |
| 459 | mTrackingStart = getLeft() - mMargins.left; |
| 460 | } |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 461 | mTracking = event.copy(); |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 462 | mTouchMode = MODE_DRAG; |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 463 | |
| 464 | // starting drag should activate entire chart |
| 465 | if (!parent.isActivated()) { |
| 466 | parent.setActivated(true); |
| 467 | } |
| 468 | |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 469 | return true; |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 470 | } else if (acceptLabel) { |
| 471 | mTouchMode = MODE_LABEL; |
| 472 | return true; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 473 | } else { |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 474 | mTouchMode = MODE_NONE; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 475 | return false; |
| 476 | } |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 477 | } |
| 478 | case MotionEvent.ACTION_MOVE: { |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 479 | if (mTouchMode == MODE_LABEL) { |
| 480 | return true; |
| 481 | } |
| 482 | |
Jeff Sharkey | 8a50364 | 2011-06-10 13:31:21 -0700 | [diff] [blame] | 483 | getParent().requestDisallowInterceptTouchEvent(true); |
| 484 | |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 485 | // content area of parent |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 486 | final Rect parentContent = getParentContentRect(); |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 487 | final Rect clampRect = computeClampRect(parentContent); |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 488 | if (clampRect.isEmpty()) return true; |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 489 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 490 | long value; |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 491 | if (mFollowAxis == VERTICAL) { |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 492 | final float currentTargetY = getTop() - mMargins.top; |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 493 | final float requestedTargetY = mTrackingStart |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 494 | + (event.getRawY() - mTracking.getRawY()); |
| 495 | final float clampedTargetY = MathUtils.constrain( |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 496 | requestedTargetY, clampRect.top, clampRect.bottom); |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 497 | setTranslationY(clampedTargetY - currentTargetY); |
| 498 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 499 | value = mAxis.convertToValue(clampedTargetY - parentContent.top); |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 500 | } else { |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 501 | final float currentTargetX = getLeft() - mMargins.left; |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 502 | final float requestedTargetX = mTrackingStart |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 503 | + (event.getRawX() - mTracking.getRawX()); |
| 504 | final float clampedTargetX = MathUtils.constrain( |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 505 | requestedTargetX, clampRect.left, clampRect.right); |
Jeff Sharkey | f54f435 | 2011-06-23 22:15:54 -0700 | [diff] [blame] | 506 | setTranslationX(clampedTargetX - currentTargetX); |
| 507 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 508 | value = mAxis.convertToValue(clampedTargetX - parentContent.left); |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 509 | } |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 510 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 511 | // round value from drag to nearest increment |
| 512 | value -= value % mDragInterval; |
| 513 | setValue(value); |
| 514 | |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 515 | dispatchOnSweep(false); |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 516 | return true; |
| 517 | } |
| 518 | case MotionEvent.ACTION_UP: { |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 519 | if (mTouchMode == MODE_LABEL) { |
| 520 | performClick(); |
| 521 | } else if (mTouchMode == MODE_DRAG) { |
| 522 | mTrackingStart = 0; |
| 523 | mTracking = null; |
| 524 | mValue = mLabelValue; |
| 525 | dispatchOnSweep(true); |
| 526 | setTranslationX(0); |
| 527 | setTranslationY(0); |
| 528 | requestLayout(); |
| 529 | } |
| 530 | |
| 531 | mTouchMode = MODE_NONE; |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 532 | return true; |
| 533 | } |
| 534 | default: { |
| 535 | return false; |
| 536 | } |
| 537 | } |
| 538 | } |
| 539 | |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 540 | /** |
| 541 | * Update {@link #mValue} based on current position, including any |
| 542 | * {@link #onTouchEvent(MotionEvent)} in progress. Typically used when |
| 543 | * {@link ChartAxis} changes during sweep adjustment. |
| 544 | */ |
| 545 | public void updateValueFromPosition() { |
| 546 | final Rect parentContent = getParentContentRect(); |
| 547 | if (mFollowAxis == VERTICAL) { |
| 548 | final float effectiveY = getY() - mMargins.top - parentContent.top; |
| 549 | setValue(mAxis.convertToValue(effectiveY)); |
| 550 | } else { |
| 551 | final float effectiveX = getX() - mMargins.left - parentContent.left; |
| 552 | setValue(mAxis.convertToValue(effectiveX)); |
| 553 | } |
| 554 | } |
| 555 | |
| 556 | public int shouldAdjustAxis() { |
| 557 | return mAxis.shouldAdjustAxis(getValue()); |
| 558 | } |
| 559 | |
| 560 | private Rect getParentContentRect() { |
| 561 | final View parent = (View) getParent(); |
| 562 | return new Rect(parent.getPaddingLeft(), parent.getPaddingTop(), |
| 563 | parent.getWidth() - parent.getPaddingRight(), |
| 564 | parent.getHeight() - parent.getPaddingBottom()); |
| 565 | } |
| 566 | |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 567 | @Override |
| 568 | public void addOnLayoutChangeListener(OnLayoutChangeListener listener) { |
| 569 | // ignored to keep LayoutTransition from animating us |
| 570 | } |
| 571 | |
| 572 | @Override |
| 573 | public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) { |
| 574 | // ignored to keep LayoutTransition from animating us |
| 575 | } |
| 576 | |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 577 | private long getValidAfterDynamic() { |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 578 | final ChartSweepView dynamic = mValidAfterDynamic; |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 579 | return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MIN_VALUE; |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 580 | } |
| 581 | |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 582 | private long getValidBeforeDynamic() { |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 583 | final ChartSweepView dynamic = mValidBeforeDynamic; |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 584 | return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MAX_VALUE; |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 585 | } |
| 586 | |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 587 | /** |
| 588 | * Compute {@link Rect} in {@link #getParent()} coordinates that we should |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 589 | * be clamped inside of, usually from {@link #setValidRange(long, long)} |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 590 | * style rules. |
| 591 | */ |
| 592 | private Rect computeClampRect(Rect parentContent) { |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 593 | // create two rectangles, and pick most restrictive combination |
| 594 | final Rect rect = buildClampRect(parentContent, mValidAfter, mValidBefore, 0f); |
| 595 | final Rect dynamicRect = buildClampRect( |
| 596 | parentContent, getValidAfterDynamic(), getValidBeforeDynamic(), mNeighborMargin); |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 597 | |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 598 | if (!rect.intersect(dynamicRect)) { |
| 599 | rect.setEmpty(); |
| 600 | } |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 601 | return rect; |
| 602 | } |
| 603 | |
| 604 | private Rect buildClampRect( |
| 605 | Rect parentContent, long afterValue, long beforeValue, float margin) { |
| 606 | if (mAxis instanceof InvertedChartAxis) { |
| 607 | long temp = beforeValue; |
| 608 | beforeValue = afterValue; |
| 609 | afterValue = temp; |
Jeff Sharkey | d360e5e | 2011-07-26 19:30:26 -0700 | [diff] [blame] | 610 | } |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 611 | |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 612 | final boolean afterValid = afterValue != Long.MIN_VALUE && afterValue != Long.MAX_VALUE; |
| 613 | final boolean beforeValid = beforeValue != Long.MIN_VALUE && beforeValue != Long.MAX_VALUE; |
| 614 | |
| 615 | final float afterPoint = mAxis.convertToPoint(afterValue) + margin; |
| 616 | final float beforePoint = mAxis.convertToPoint(beforeValue) - margin; |
| 617 | |
| 618 | final Rect clampRect = new Rect(parentContent); |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 619 | if (mFollowAxis == VERTICAL) { |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 620 | if (beforeValid) clampRect.bottom = clampRect.top + (int) beforePoint; |
| 621 | if (afterValid) clampRect.top += afterPoint; |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 622 | } else { |
Jeff Sharkey | e2afc0f | 2011-08-01 15:29:30 -0700 | [diff] [blame] | 623 | if (beforeValid) clampRect.right = clampRect.left + (int) beforePoint; |
| 624 | if (afterValid) clampRect.left += afterPoint; |
Jeff Sharkey | 4ec71e7 | 2011-07-10 12:23:55 -0700 | [diff] [blame] | 625 | } |
| 626 | return clampRect; |
| 627 | } |
| 628 | |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 629 | @Override |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 630 | protected void drawableStateChanged() { |
| 631 | super.drawableStateChanged(); |
| 632 | if (mSweep.isStateful()) { |
| 633 | mSweep.setState(getDrawableState()); |
| 634 | } |
| 635 | } |
| 636 | |
| 637 | @Override |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 638 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 639 | |
| 640 | // TODO: handle vertical labels |
| 641 | if (isEnabled() && mLabelLayout != null) { |
| 642 | final int sweepHeight = mSweep.getIntrinsicHeight(); |
| 643 | final int templateHeight = mLabelLayout.getHeight(); |
| 644 | |
| 645 | mSweepOffset.x = 0; |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 646 | mSweepOffset.y = 0; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 647 | mSweepOffset.y = (int) ((templateHeight / 2) - getTargetInset()); |
| 648 | setMeasuredDimension(mSweep.getIntrinsicWidth(), Math.max(sweepHeight, templateHeight)); |
| 649 | |
| 650 | } else { |
| 651 | mSweepOffset.x = 0; |
| 652 | mSweepOffset.y = 0; |
| 653 | setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight()); |
| 654 | } |
| 655 | |
| 656 | if (mFollowAxis == VERTICAL) { |
| 657 | final int targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top |
| 658 | - mSweepPadding.bottom; |
| 659 | mMargins.top = -(mSweepPadding.top + (targetHeight / 2)); |
| 660 | mMargins.bottom = 0; |
| 661 | mMargins.left = -mSweepPadding.left; |
| 662 | mMargins.right = mSweepPadding.right; |
| 663 | } else { |
| 664 | final int targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left |
| 665 | - mSweepPadding.right; |
| 666 | mMargins.left = -(mSweepPadding.left + (targetWidth / 2)); |
| 667 | mMargins.right = 0; |
| 668 | mMargins.top = -mSweepPadding.top; |
| 669 | mMargins.bottom = mSweepPadding.bottom; |
| 670 | } |
| 671 | |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 672 | mContentOffset.set(0, 0, 0, 0); |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 673 | |
| 674 | // make touch target area larger |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 675 | final int widthBefore = getMeasuredWidth(); |
| 676 | final int heightBefore = getMeasuredHeight(); |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 677 | if (mFollowAxis == HORIZONTAL) { |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 678 | final int widthAfter = widthBefore * 3; |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 679 | setMeasuredDimension(widthAfter, heightBefore); |
| 680 | mContentOffset.left = (widthAfter - widthBefore) / 2; |
| 681 | |
| 682 | final int offset = mSweepPadding.bottom * 2; |
| 683 | mContentOffset.bottom -= offset; |
| 684 | mMargins.bottom += offset; |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 685 | } else { |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 686 | final int heightAfter = heightBefore * 2; |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 687 | setMeasuredDimension(widthBefore, heightAfter); |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 688 | mContentOffset.offset(0, (heightAfter - heightBefore) / 2); |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 689 | |
| 690 | final int offset = mSweepPadding.right * 2; |
| 691 | mContentOffset.right -= offset; |
| 692 | mMargins.right += offset; |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 693 | } |
| 694 | |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 695 | mSweepOffset.offset(mContentOffset.left, mContentOffset.top); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 696 | mMargins.offset(-mSweepOffset.x, -mSweepOffset.y); |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 697 | } |
| 698 | |
| 699 | @Override |
Jeff Sharkey | b98c55b | 2011-09-11 17:29:49 -0700 | [diff] [blame] | 700 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
| 701 | super.onLayout(changed, left, top, right, bottom); |
| 702 | invalidateLabelOffset(); |
| 703 | } |
| 704 | |
| 705 | @Override |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 706 | protected void onDraw(Canvas canvas) { |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 707 | super.onDraw(canvas); |
| 708 | |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 709 | final int width = getWidth(); |
| 710 | final int height = getHeight(); |
| 711 | |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 712 | final int labelSize; |
| 713 | if (isEnabled() && mLabelLayout != null) { |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 714 | final int count = canvas.save(); |
| 715 | { |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 716 | final float alignOffset = mLabelSize - LARGE_WIDTH; |
| 717 | canvas.translate( |
| 718 | mContentOffset.left + alignOffset, mContentOffset.top + mLabelOffset); |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 719 | mLabelLayout.draw(canvas); |
| 720 | } |
| 721 | canvas.restoreToCount(count); |
Jeff Sharkey | b654846 | 2014-07-21 15:38:06 -0700 | [diff] [blame] | 722 | labelSize = (int) mLabelSize + mSafeRegion; |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 723 | } else { |
| 724 | labelSize = 0; |
| 725 | } |
| 726 | |
| 727 | if (mFollowAxis == VERTICAL) { |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 728 | mSweep.setBounds(labelSize, mSweepOffset.y, width + mContentOffset.right, |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 729 | mSweepOffset.y + mSweep.getIntrinsicHeight()); |
| 730 | } else { |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 731 | mSweep.setBounds(mSweepOffset.x, labelSize, mSweepOffset.x + mSweep.getIntrinsicWidth(), |
| 732 | height + mContentOffset.bottom); |
Jeff Sharkey | ec3be8a | 2011-07-09 01:02:56 -0700 | [diff] [blame] | 733 | } |
| 734 | |
Jeff Sharkey | 52c3f44 | 2011-06-23 00:39:38 -0700 | [diff] [blame] | 735 | mSweep.draw(canvas); |
Jeff Sharkey | a53188f | 2011-09-13 19:56:45 -0700 | [diff] [blame] | 736 | |
| 737 | if (DRAW_OUTLINE) { |
| 738 | mOutlinePaint.setColor(Color.RED); |
| 739 | canvas.drawRect(0, 0, width, height, mOutlinePaint); |
| 740 | } |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 741 | } |
| 742 | |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 743 | public static float getLabelTop(ChartSweepView view) { |
Jeff Sharkey | 5d70679 | 2011-09-08 18:57:17 -0700 | [diff] [blame] | 744 | return view.getY() + view.mContentOffset.top; |
Jeff Sharkey | 55d18a5 | 2011-08-27 17:09:43 -0700 | [diff] [blame] | 745 | } |
| 746 | |
| 747 | public static float getLabelBottom(ChartSweepView view) { |
| 748 | return getLabelTop(view) + view.mLabelLayout.getHeight(); |
| 749 | } |
Jeff Sharkey | bdf98e8 | 2011-11-10 17:17:24 -0800 | [diff] [blame] | 750 | |
| 751 | public static float getLabelWidth(ChartSweepView view) { |
| 752 | return Layout.getDesiredWidth(view.mLabelLayout.getText(), view.mLabelLayout.getPaint()); |
| 753 | } |
Jeff Sharkey | ab2d8d3 | 2011-05-30 16:19:56 -0700 | [diff] [blame] | 754 | } |