PieMenu.java revision a4befac241ec69791fd30e11355ed3dc10d7fb37
11cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato/* 21cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * Copyright (C) 2010 The Android Open Source Project 31cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * 41cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * Licensed under the Apache License, Version 2.0 (the "License"); 51cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * you may not use this file except in compliance with the License. 61cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * You may obtain a copy of the License at 71cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * 81cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * http://www.apache.org/licenses/LICENSE-2.0 91cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * 101cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * Unless required by applicable law or agreed to in writing, software 111cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * distributed under the License is distributed on an "AS IS" BASIS, 121cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * See the License for the specific language governing permissions and 141cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * limitations under the License. 151cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato */ 161cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 171cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratopackage com.android.browser.view; 181cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 191cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport com.android.browser.R; 201cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 211cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.content.Context; 221cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.content.res.Resources; 23b13b9bdad2baf6ad1ec2e56b6b7598fa20f55fc4Mathias Agopianimport android.graphics.Canvas; 241cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.graphics.Paint; 251cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.graphics.Path; 261cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.graphics.Point; 271cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.graphics.PointF; 284528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.graphics.RectF; 291cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.graphics.drawable.Drawable; 301cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.util.AttributeSet; 311cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.view.MotionEvent; 321cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.view.SoundEffectConstants; 331cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.view.View; 341cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport android.view.ViewGroup; 35a3804cf77f0edd93f6247a055cdafb856b117eecElliott Hughesimport android.widget.FrameLayout; 361cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 371cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport java.util.ArrayList; 381cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratoimport java.util.List; 391cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 401cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onoratopublic class PieMenu extends FrameLayout { 411cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 421cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato private static final int MAX_LEVELS = 5; 431cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 441cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato public interface PieController { 451cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato /** 461cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * called before menu opens to customize menu 471cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * returns if pie state has been changed 481cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato */ 491cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato public boolean onOpen(); 501cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato } 511cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 521cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato /** 535f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * A view like object that lives off of the pie menu 541cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato */ 551cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato public interface PieView { 562fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 571cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato public interface OnLayoutListener { 585f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato public void onLayout(int ax, int ay, boolean left); 595f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato } 602fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 612fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate public void setLayoutListener(OnLayoutListener l); 622fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 632fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate public void layout(int anchorX, int anchorY, boolean onleft, float angle, 642fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int parentHeight); 652fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 662fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate public void draw(Canvas c); 672fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 682fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate public boolean onTouchEvent(MotionEvent evt); 692fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 702fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 712fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 722fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private Point mCenter; 732fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private int mRadius; 742fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private int mRadiusInc; 751cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato private int mSlop; 762fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private int mTouchOffset; 772fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 782fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private boolean mOpen; 792fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private PieController mController; 802fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 812fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private List<PieItem> mItems; 822fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private int mLevels; 835baa3a62a97544669fba6d65a11c07f252e654ddSteve Block private int[] mCounts; 842fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private PieView mPieView = null; 852fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 862fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private Drawable mBackground; 871cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato private Paint mNormalPaint; 881cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato private Paint mSelectedPaint; 891cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 901cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato // touch handling 911cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato PieItem mCurrentItem; 925f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato 931cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato private boolean mUseBackground; 941cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 951cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato /** 961cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * @param context 975f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * @param attrs 981cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * @param defStyle 991cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato */ 1001cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato public PieMenu(Context context, AttributeSet attrs, int defStyle) { 1011cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato super(context, attrs, defStyle); 1021cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato init(context); 1031cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato } 1042fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 1051cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato /** 1061cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato * @param context 1075f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * @param attrs 1081cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato */ 1091cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato public PieMenu(Context context, AttributeSet attrs) { 1101cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato super(context, attrs); 1111cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato init(context); 1121cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato } 1131cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 1145f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato /** 1155f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato * @param context 1165f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato */ 1175f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato public PieMenu(Context context) { 1185f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato super(context); 1195f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato init(context); 1205f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato } 1215f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato 1225f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato private void init(Context ctx) { 1235f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato mItems = new ArrayList<PieItem>(); 1245f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato mLevels = 0; 1251cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mCounts = new int[MAX_LEVELS]; 1261cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato Resources res = ctx.getResources(); 1271cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mRadius = (int) res.getDimension(R.dimen.qc_radius_start); 1284528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tate mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_increment); 1291cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mSlop = (int) res.getDimension(R.dimen.qc_slop); 1305f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato mTouchOffset = (int) res.getDimension(R.dimen.qc_touch_offset); 1315f15d151b5101fadfe6cba1e8f4aa6367e8c603eJoe Onorato mOpen = false; 1321cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato setWillNotDraw(false); 1331cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato setDrawingCacheEnabled(false); 1341cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mCenter = new Point(0,0); 1351cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mBackground = res.getDrawable(R.drawable.qc_background_normal); 1365baa3a62a97544669fba6d65a11c07f252e654ddSteve Block mNormalPaint = new Paint(); 1371cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mNormalPaint.setColor(res.getColor(R.color.qc_normal)); 138a3804cf77f0edd93f6247a055cdafb856b117eecElliott Hughes mNormalPaint.setAntiAlias(true); 1394528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tate mSelectedPaint = new Paint(); 1401cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mSelectedPaint.setColor(res.getColor(R.color.qc_selected)); 1411cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mSelectedPaint.setAntiAlias(true); 1424528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tate } 14303f4df4b3bf8b8828e795a0bf1f913e6e08f12f1Joe Onorato 1441cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato public void setController(PieController ctl) { 1454528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tate mController = ctl; 1461cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato } 1474528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tate 1481cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato public void setUseBackground(boolean useBackground) { 1491cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato mUseBackground = useBackground; 1501cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato } 1511cf587496fcb1d652bab9fc6792fb106b6fefaa4Joe Onorato 152 public void addItem(PieItem item) { 153 // add the item to the pie itself 154 mItems.add(item); 155 int l = item.getLevel(); 156 mLevels = Math.max(mLevels, l); 157 mCounts[l]++; 158 } 159 160 public void removeItem(PieItem item) { 161 mItems.remove(item); 162 } 163 164 public void clearItems() { 165 mItems.clear(); 166 } 167 168 private boolean onTheLeft() { 169 return mCenter.x < mSlop; 170 } 171 172 /** 173 * guaranteed has center set 174 * @param show 175 */ 176 private void show(boolean show) { 177 mOpen = show; 178 if (mOpen) { 179 if (mController != null) { 180 boolean changed = mController.onOpen(); 181 } 182 layoutPie(); 183 } 184 if (!show) { 185 mCurrentItem = null; 186 mPieView = null; 187 } 188 invalidate(); 189 } 190 191 private void setCenter(int x, int y) { 192 if (x < mSlop) { 193 mCenter.x = 0; 194 } else { 195 mCenter.x = getWidth(); 196 } 197 mCenter.y = y; 198 } 199 200 private void layoutPie() { 201 float emptyangle = (float) Math.PI / 16; 202 int rgap = 2; 203 int inner = mRadius + rgap; 204 int outer = mRadius + mRadiusInc - rgap; 205 int radius = mRadius; 206 int gap = 1; 207 for (int i = 0; i < mLevels; i++) { 208 int level = i + 1; 209 float sweep = (float) (Math.PI - 2 * emptyangle) / mCounts[level]; 210 float angle = emptyangle + sweep / 2; 211 for (PieItem item : mItems) { 212 if (item.getLevel() == level) { 213 View view = item.getView(); 214 view.measure(view.getLayoutParams().width, 215 view.getLayoutParams().height); 216 int w = view.getMeasuredWidth(); 217 int h = view.getMeasuredHeight(); 218 int r = inner + (outer - inner) * 2 / 3; 219 int x = (int) (r * Math.sin(angle)); 220 int y = mCenter.y - (int) (r * Math.cos(angle)) - h / 2; 221 if (onTheLeft()) { 222 x = mCenter.x + x - w / 2; 223 } else { 224 x = mCenter.x - x - w / 2; 225 } 226 view.layout(x, y, x + w, y + h); 227 float itemstart = angle - sweep / 2; 228 Path slice = makeSlice(getDegrees(itemstart) - gap, 229 getDegrees(itemstart + sweep) + gap, 230 outer, inner, mCenter); 231 item.setGeometry(itemstart, sweep, inner, outer, slice); 232 angle += sweep; 233 } 234 } 235 inner += mRadiusInc; 236 outer += mRadiusInc; 237 } 238 } 239 240 241 /** 242 * converts a 243 * 244 * @param angle from 0..PI to Android degrees (clockwise starting at 3 245 * o'clock) 246 * @return skia angle 247 */ 248 private float getDegrees(double angle) { 249 return (float) (270 - 180 * angle / Math.PI); 250 } 251 252 @Override 253 protected void onDraw(Canvas canvas) { 254 if (mOpen) { 255 int state; 256 if (mUseBackground) { 257 int w = mBackground.getIntrinsicWidth(); 258 int h = mBackground.getIntrinsicHeight(); 259 int left = mCenter.x - w; 260 int top = mCenter.y - h / 2; 261 mBackground.setBounds(left, top, left + w, top + h); 262 state = canvas.save(); 263 if (onTheLeft()) { 264 canvas.scale(-1, 1); 265 } 266 mBackground.draw(canvas); 267 canvas.restoreToCount(state); 268 } 269 for (PieItem item : mItems) { 270 Paint p = item.isSelected() ? mSelectedPaint : mNormalPaint; 271 state = canvas.save(); 272 if (onTheLeft()) { 273 canvas.scale(-1, 1); 274 } 275 drawPath(canvas, item.getPath(), p); 276 canvas.restoreToCount(state); 277 drawItem(canvas, item); 278 } 279 if (mPieView != null) { 280 mPieView.draw(canvas); 281 } 282 } 283 } 284 285 private void drawItem(Canvas canvas, PieItem item) { 286 int outer = item.getOuterRadius(); 287 int left = mCenter.x - outer; 288 int top = mCenter.y - outer; 289 // draw the item view 290 View view = item.getView(); 291 int state = canvas.save(); 292 canvas.translate(view.getX(), view.getY()); 293 view.draw(canvas); 294 canvas.restoreToCount(state); 295 } 296 297 private void drawPath(Canvas canvas, Path path, Paint paint) { 298 canvas.drawPath(path, paint); 299 } 300 301 private Path makeSlice(float start, float end, int outer, int inner, Point center) { 302 RectF bb = 303 new RectF(center.x - outer, center.y - outer, center.x + outer, 304 center.y + outer); 305 RectF bbi = 306 new RectF(center.x - inner, center.y - inner, center.x + inner, 307 center.y + inner); 308 Path path = new Path(); 309 path.arcTo(bb, start, end - start, true); 310 path.arcTo(bbi, end, start - end); 311 path.close(); 312 return path; 313 } 314 315 // touch handling for pie 316 317 @Override 318 public boolean onTouchEvent(MotionEvent evt) { 319 float x = evt.getX(); 320 float y = evt.getY(); 321 int action = evt.getActionMasked(); 322 if (MotionEvent.ACTION_DOWN == action) { 323 if ((x > getWidth() - mSlop) || (x < mSlop)) { 324 setCenter((int) x, (int) y); 325 show(true); 326 return true; 327 } 328 } else if (MotionEvent.ACTION_UP == action) { 329 if (mOpen) { 330 boolean handled = false; 331 if (mPieView != null) { 332 handled = mPieView.onTouchEvent(evt); 333 } 334 PieItem item = mCurrentItem; 335 deselect(); 336 show(false); 337 if (!handled && (item != null)) { 338 item.getView().performClick(); 339 } 340 return true; 341 } 342 } else if (MotionEvent.ACTION_CANCEL == action) { 343 if (mOpen) { 344 show(false); 345 } 346 deselect(); 347 return false; 348 } else if (MotionEvent.ACTION_MOVE == action) { 349 boolean handled = false; 350 PointF polar = getPolar(x, y); 351 int maxr = mRadius + mLevels * mRadiusInc + 50; 352 if (mPieView != null) { 353 handled = mPieView.onTouchEvent(evt); 354 } 355 if (handled) { 356 invalidate(); 357 return false; 358 } 359 if (polar.y > maxr) { 360 deselect(); 361 show(false); 362 evt.setAction(MotionEvent.ACTION_DOWN); 363 if (getParent() != null) { 364 ((ViewGroup) getParent()).dispatchTouchEvent(evt); 365 } 366 return false; 367 } 368 PieItem item = findItem(polar); 369 if (mCurrentItem != item) { 370 onEnter(item); 371 if ((item != null) && item.isPieView()) { 372 int cx = item.getView().getLeft() + (onTheLeft() 373 ? item.getView().getWidth() : 0); 374 int cy = item.getView().getTop(); 375 mPieView = item.getPieView(); 376 layoutPieView(mPieView, cx, cy, 377 (item.getStartAngle() + item.getSweep()) / 2); 378 } 379 invalidate(); 380 } 381 } 382 // always re-dispatch event 383 return false; 384 } 385 386 private void layoutPieView(PieView pv, int x, int y, float angle) { 387 pv.layout(x, y, onTheLeft(), angle, getHeight()); 388 } 389 390 /** 391 * enter a slice for a view 392 * updates model only 393 * @param item 394 */ 395 private void onEnter(PieItem item) { 396 // deselect 397 if (mCurrentItem != null) { 398 mCurrentItem.setSelected(false); 399 } 400 if (item != null) { 401 // clear up stack 402 playSoundEffect(SoundEffectConstants.CLICK); 403 item.setSelected(true); 404 mPieView = null; 405 } 406 mCurrentItem = item; 407 } 408 409 private void deselect() { 410 if (mCurrentItem != null) { 411 mCurrentItem.setSelected(false); 412 } 413 mCurrentItem = null; 414 mPieView = null; 415 } 416 417 private PointF getPolar(float x, float y) { 418 PointF res = new PointF(); 419 // get angle and radius from x/y 420 res.x = (float) Math.PI / 2; 421 x = mCenter.x - x; 422 if (mCenter.x < mSlop) { 423 x = -x; 424 } 425 y = mCenter.y - y; 426 res.y = (float) Math.sqrt(x * x + y * y); 427 if (y > 0) { 428 res.x = (float) Math.asin(x / res.y); 429 } else if (y < 0) { 430 res.x = (float) (Math.PI - Math.asin(x / res.y )); 431 } 432 return res; 433 } 434 435 /** 436 * 437 * @param polar x: angle, y: dist 438 * @return the item at angle/dist or null 439 */ 440 private PieItem findItem(PointF polar) { 441 // find the matching item: 442 for (PieItem item : mItems) { 443 if ((item.getInnerRadius() - mTouchOffset < polar.y) 444 && (item.getOuterRadius() - mTouchOffset > polar.y) 445 && (item.getStartAngle() < polar.x) 446 && (item.getStartAngle() + item.getSweep() > polar.x)) { 447 return item; 448 } 449 } 450 return null; 451 } 452 453} 454