diff options
author | Michael Kolb <kolby@google.com> | 2013-03-12 10:24:42 -0700 |
---|---|---|
committer | Michael Kolb <kolby@google.com> | 2013-04-01 16:35:44 -0700 |
commit | 3bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0 (patch) | |
tree | 43c8eae3c556b647bbac3d207e9b63491d1775f4 /src/com/android/camera/ui | |
parent | e5a79a5bd8ca3e25c14e4fe5df7c1ee1b544dc7e (diff) | |
download | android_packages_apps_Snap-3bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0.tar.gz android_packages_apps_Snap-3bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0.tar.bz2 android_packages_apps_Snap-3bc96b2d1106fc5ebec6fda6aad3bca4d62e81c0.zip |
Pie design update
Change-Id: Ifb0d50938332bdae50e45523850605d0aafed7fb
Diffstat (limited to 'src/com/android/camera/ui')
-rw-r--r-- | src/com/android/camera/ui/PieItem.java | 8 | ||||
-rw-r--r-- | src/com/android/camera/ui/PieRenderer.java | 300 |
2 files changed, 211 insertions, 97 deletions
diff --git a/src/com/android/camera/ui/PieItem.java b/src/com/android/camera/ui/PieItem.java index 677e5acc8..bbfa1dc82 100644 --- a/src/com/android/camera/ui/PieItem.java +++ b/src/com/android/camera/ui/PieItem.java @@ -57,7 +57,9 @@ public class PieItem { public PieItem(Drawable drawable, int level) { mDrawable = drawable; this.level = level; - setAlpha(1f); + if (drawable != null) { + setAlpha(1f); + } mEnabled = true; setAnimationAngle(getAnimationAngle()); start = -1; @@ -79,6 +81,10 @@ public class PieItem { mItems.add(item); } + public void clearItems() { + mItems = null; + } + public void setPath(Path p) { mPath = p; } diff --git a/src/com/android/camera/ui/PieRenderer.java b/src/com/android/camera/ui/PieRenderer.java index b60d9f668..dffa47e1e 100644 --- a/src/com/android/camera/ui/PieRenderer.java +++ b/src/com/android/camera/ui/PieRenderer.java @@ -27,6 +27,7 @@ import android.graphics.PointF; import android.graphics.RectF; import android.os.Handler; import android.os.Message; +import android.util.Log; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.animation.Animation; @@ -54,6 +55,8 @@ public class PieRenderer extends OverlayRenderer private static final int STATE_FINISHING = 2; private static final int STATE_PIE = 8; + private static final float MATH_PI_2 = (float)(Math.PI / 2); + private Runnable mDisappear = new Disappear(); private Animation.AnimationListener mEndAction = new EndAction(); private static final int SCALING_UP_TIME = 600; @@ -66,26 +69,26 @@ public class PieRenderer extends OverlayRenderer private static final long PIE_FADE_IN_DURATION = 200; private static final long PIE_XFADE_DURATION = 200; private static final long PIE_SELECT_FADE_DURATION = 300; + private static final long PIE_OPEN_SUB_DELAY = 400; private static final int MSG_OPEN = 0; private static final int MSG_CLOSE = 1; - private static final float PIE_SWEEP = (float)(Math.PI * 2 / 3); + private static final int MSG_OPENSUBMENU = 2; + private static final float PIE_SWEEP = (float)(Math.PI / 2); // geometry - private Point mCenter; + private Point mSliceCenter; private int mRadius; - private int mRadiusInc; // the detection if touch is inside a slice is offset // inbounds by this amount to allow the selection to show before the // finger covers it private int mTouchOffset; - private List<PieItem> mItems; - - private PieItem mOpenItem; + private List<PieItem> mOpen; private Paint mSelectedPaint; private Paint mSubPaint; + private Paint mMenuArcPaint; // touch handling private PieItem mCurrentItem; @@ -98,6 +101,11 @@ public class PieRenderer extends OverlayRenderer private int mFocusY; private int mCenterX; private int mCenterY; + private int mPieCenterX; + private int mPieCenterY; + private int mIconRadius; + private int mArcRadius; + private int mArcOffset; private int mDialAngle; private RectF mCircle; @@ -124,7 +132,7 @@ public class PieRenderer extends OverlayRenderer switch(msg.what) { case MSG_OPEN: if (mListener != null) { - mListener.onPieOpened(mCenter.x, mCenter.y); + mListener.onPieOpened(mPieCenterX, mPieCenterY); } break; case MSG_CLOSE: @@ -132,7 +140,11 @@ public class PieRenderer extends OverlayRenderer mListener.onPieClosed(); } break; + case MSG_OPENSUBMENU: + onEnterOpen(); + break; } + } }; @@ -153,13 +165,13 @@ public class PieRenderer extends OverlayRenderer private void init(Context ctx) { setVisible(false); - mItems = new ArrayList<PieItem>(); + mOpen = new ArrayList<PieItem>(); + mOpen.add(new PieItem(null, 0)); Resources res = ctx.getResources(); mRadius = (int) res.getDimensionPixelSize(R.dimen.pie_radius_start); mCircleSize = mRadius - res.getDimensionPixelSize(R.dimen.focus_radius_offset); - mRadiusInc = (int) res.getDimensionPixelSize(R.dimen.pie_radius_increment); mTouchOffset = (int) res.getDimensionPixelSize(R.dimen.pie_touch_offset); - mCenter = new Point(0,0); + mSliceCenter = new Point(0,0); mSelectedPaint = new Paint(); mSelectedPaint.setColor(Color.argb(255, 51, 181, 229)); mSelectedPaint.setAntiAlias(true); @@ -184,6 +196,18 @@ public class PieRenderer extends OverlayRenderer mTouchSlopSquared = ViewConfiguration.get(ctx).getScaledTouchSlop(); mTouchSlopSquared = mTouchSlopSquared * mTouchSlopSquared; mDown = new Point(); + mMenuArcPaint = new Paint(); + mMenuArcPaint.setAntiAlias(true); + mMenuArcPaint.setColor(Color.argb(140, 255, 255, 255)); + mMenuArcPaint.setStrokeWidth(10); + mMenuArcPaint.setStyle(Paint.Style.STROKE); + mIconRadius = res.getDimensionPixelSize(R.dimen.pie_item_radius); + mArcRadius = res.getDimensionPixelSize(R.dimen.pie_arc_radius); + mArcOffset = res.getDimensionPixelSize(R.dimen.pie_arc_offset); + } + + private PieItem getRoot() { + return mOpen.get(0); } public boolean showsItems() { @@ -192,15 +216,11 @@ public class PieRenderer extends OverlayRenderer public void addItem(PieItem item) { // add the item to the pie itself - mItems.add(item); - } - - public void removeItem(PieItem item) { - mItems.remove(item); + getRoot().addItem(item); } public void clearItems() { - mItems.clear(); + getRoot().clearItems(); } public void showInCenter() { @@ -212,7 +232,8 @@ public class PieRenderer extends OverlayRenderer cancelFocus(); } mState = STATE_PIE; - setCenter(mCenterX, mCenterY); + resetPieCenter(); + setCenter(mPieCenterX, mPieCenterY); mTapMode = true; show(true); } @@ -231,10 +252,16 @@ public class PieRenderer extends OverlayRenderer mState = STATE_PIE; // ensure clean state mCurrentItem = null; - mOpenItem = null; - for (PieItem item : mItems) { - item.setSelected(false); + PieItem root = getRoot(); + for (PieItem openItem : mOpen) { + if (openItem.hasItems()) { + for (PieItem item : openItem.getItems()) { + item.setSelected(false); + } + } } + mOpen.clear(); + mOpen.add(root); layoutPie(); fadeIn(); } else { @@ -270,22 +297,41 @@ public class PieRenderer extends OverlayRenderer } public void setCenter(int x, int y) { - mCenter.x = x; - mCenter.y = y; - // when using the pie menu, align the focus ring - alignFocus(x, y); + mPieCenterX = x; + mPieCenterY = y; + mSliceCenter.x = x; + mSliceCenter.y = y - mArcOffset + mIconRadius; + } + + @Override + public void layout(int l, int t, int r, int b) { + super.layout(l, t, r, b); + mCenterX = (r - l) / 2; + mCenterY = (b - t) / 2; + + mFocusX = mCenterX; + mFocusY = mCenterY; + resetPieCenter(); + setCircle(mFocusX, mFocusY); + if (isVisible() && mState == STATE_PIE) { + setCenter(mPieCenterX, mPieCenterY); + layoutPie(); + } + } + + private void resetPieCenter() { + mPieCenterX = mCenterX; + mPieCenterY = mCenterY + mCenterY / 3; } private void layoutPie() { - int rgap = 2; - int inner = mRadius + rgap; - int outer = mRadius + mRadiusInc - rgap; + int inner = mIconRadius; + int outer = inner + mTouchOffset; int gap = 1; - layoutItems(mItems, (float) (Math.PI / 2), inner, outer, gap); + layoutItems(0, getRoot().getItems(), (float) (Math.PI / 2), inner, outer, gap); } - private void layoutItems(List<PieItem> items, float centerAngle, int inner, - int outer, int gap) { + private void layoutItems(int level, List<PieItem> items, float centerAngle, int inner, int outer, int gap) { float emptyangle = PIE_SWEEP / 16; float sweep = (float) (PIE_SWEEP - 2 * emptyangle) / items.size(); float angle = centerAngle - PIE_SWEEP / 2 + emptyangle + sweep / 2; @@ -298,8 +344,10 @@ public class PieRenderer extends OverlayRenderer break; } } + Point p = new Point(mSliceCenter); + p.y -= level * mTouchOffset; Path path = makeSlice(getDegrees(0) - gap, getDegrees(sweep) + gap, - outer, inner, mCenter); + outer, inner, p); for (PieItem item : items) { // shared between items item.setPath(path); @@ -311,14 +359,14 @@ public class PieRenderer extends OverlayRenderer // move views to outer border int r = inner + (outer - inner) * 2 / 3; int x = (int) (r * Math.cos(angle)); - int y = mCenter.y - (int) (r * Math.sin(angle)) - h / 2; - x = mCenter.x + x - w / 2; + int y = mSliceCenter.y - (level * mTouchOffset) - (int) (r * Math.sin(angle)) - h / 2; + x = mSliceCenter.x + x - w / 2; item.setBounds(x, y, x + w, y + h); float itemstart = angle - sweep / 2; item.setGeometry(itemstart, sweep, inner, outer); if (item.hasItems()) { - layoutItems(item.getItems(), angle, inner, - outer + mRadiusInc / 2, gap); + layoutItems(level + 1, item.getItems(), MATH_PI_2, inner, + outer, gap); } angle += sweep; } @@ -378,6 +426,31 @@ public class PieRenderer extends OverlayRenderer mOverlay.startAnimation(mFadeOut); } + // root does not count + private boolean hasOpenItem() { + return mOpen.size() > 1; + } + + // pop an item of the open item stack + private PieItem closeOpenItem() { + PieItem item = getOpenItem(); + mOpen.remove(mOpen.size() -1); + return item; + } + + private PieItem getOpenItem() { + return mOpen.get(mOpen.size() - 1); + } + + // return the children either the root or parent of the current open item + private PieItem getParent() { + return mOpen.get(Math.max(0, mOpen.size() - 2)); + } + + private int getLevel() { + return mOpen.size() - 1; + } + @Override public void onDraw(Canvas canvas) { float alpha = 1; @@ -391,39 +464,55 @@ public class PieRenderer extends OverlayRenderer int state = canvas.save(); if (mFadeIn != null) { float sf = 0.9f + alpha * 0.1f; - canvas.scale(sf, sf, mCenter.x, mCenter.y); + canvas.scale(sf, sf, mPieCenterX, mPieCenterY); + } + if (mState != STATE_PIE) { + drawFocus(canvas); } - drawFocus(canvas); if (mState == STATE_FINISHING) { canvas.restoreToCount(state); return; } - if ((mOpenItem == null) || (mXFade != null)) { + if (!hasOpenItem() || (mXFade != null)) { // draw base menu - for (PieItem item : mItems) { - drawItem(canvas, item, alpha); + drawArc(canvas, getLevel()); + for (PieItem item : getParent().getItems()) { + drawItem(Math.max(0, mOpen.size() - 2), canvas, item, alpha); } } - if (mOpenItem != null) { - for (PieItem inner : mOpenItem.getItems()) { + if (hasOpenItem()) { + int level = getLevel(); + drawArc(canvas, level); + for (PieItem inner : getOpenItem().getItems()) { if (mFadeOut != null) { - drawItem(canvas, inner, alpha); + drawItem(level, canvas, inner, alpha); } else { - drawItem(canvas, inner, (mXFade != null) ? (1 - 0.5f * alpha) : 1); + drawItem(level, canvas, inner, (mXFade != null) ? (1 - 0.5f * alpha) : 1); } } } canvas.restoreToCount(state); } - private void drawItem(Canvas canvas, PieItem item, float alpha) { + private void drawArc(Canvas canvas, int level) { + // arc + if (mState == STATE_PIE) { + int nr = mArcRadius; + int cy = mPieCenterY - mArcOffset + mArcRadius - level * mTouchOffset; + canvas.drawArc(new RectF(mPieCenterX - nr, cy - mArcRadius, + mPieCenterX + nr, cy + mArcRadius), + 252, 36, false, mMenuArcPaint); + } + } + private void drawItem(int level, Canvas canvas, PieItem item, float alpha) { if (mState == STATE_PIE) { if (item.getPath() != null) { + int y = mSliceCenter.y - level * mTouchOffset; if (item.isSelected()) { Paint p = mSelectedPaint; int state = canvas.save(); float r = getDegrees(item.getStartAngle()); - canvas.rotate(r, mCenter.x, mCenter.y); + canvas.rotate(r, mSliceCenter.x, y); if (mFadeOut != null) { p.setAlpha((int)(255 * alpha)); } @@ -448,7 +537,7 @@ public class PieRenderer extends OverlayRenderer float x = evt.getX(); float y = evt.getY(); int action = evt.getActionMasked(); - PointF polar = getPolar(x, y, !(mTapMode)); + PointF polar = getPolar(x, y, !mTapMode); if (MotionEvent.ACTION_DOWN == action) { mDown.x = (int) evt.getX(); mDown.y = (int) evt.getY(); @@ -469,7 +558,7 @@ public class PieRenderer extends OverlayRenderer PieItem item = mCurrentItem; if (mTapMode) { item = findItem(polar); - if (item != null && mOpening) { + if (mOpening) { mOpening = false; return true; } @@ -477,10 +566,11 @@ public class PieRenderer extends OverlayRenderer if (item == null) { mTapMode = false; show(false); - } else if (!mOpening - && !item.hasItems()) { - startFadeOut(item); - mTapMode = false; + } else if (!mOpening && !item.hasItems()) { + startFadeOut(item); + mTapMode = false; + } else { + mTapMode = true; } return true; } @@ -489,11 +579,17 @@ public class PieRenderer extends OverlayRenderer show(false); } deselect(); + mHandler.removeMessages(MSG_OPENSUBMENU); return false; } else if (MotionEvent.ACTION_MOVE == action) { - if (polar.y < mRadius) { - if (mOpenItem != null) { - mOpenItem = null; + if (pulledToCenter(polar)) { + mHandler.removeMessages(MSG_OPENSUBMENU); + if (hasOpenItem()) { + if (mCurrentItem != null) { + mCurrentItem.setSelected(false); + } + closeOpenItem(); + mCurrentItem = null; } else { deselect(); } @@ -502,23 +598,63 @@ public class PieRenderer extends OverlayRenderer PieItem item = findItem(polar); boolean moved = hasMoved(evt); if ((item != null) && (mCurrentItem != item) && (!mOpening || moved)) { + mHandler.removeMessages(MSG_OPENSUBMENU); // only select if we didn't just open or have moved past slop - mOpening = false; if (moved) { // switch back to swipe mode mTapMode = false; } - onEnter(item); + onEnterSelect(item); + mHandler.sendEmptyMessageDelayed(MSG_OPENSUBMENU, PIE_OPEN_SUB_DELAY); } } return false; } + private boolean pulledToCenter(PointF polarCoords) { + return polarCoords.y < mIconRadius - mArcOffset; + } + + private PointF getPolar(float x, float y, boolean useOffset) { + PointF res = new PointF(); + // get angle and radius from x/y + res.x = (float) Math.PI / 2; + x = x - mSliceCenter.x; + y = mSliceCenter.y - getLevel() * mTouchOffset - y; + res.y = (float) Math.sqrt(x * x + y * y); + if (x != 0) { + res.x = (float) Math.atan2(y, x); + if (res.x < 0) { + res.x = (float) (2 * Math.PI + res.x); + } + } + res.y = res.y + (useOffset ? mTouchOffset : 0); + return res; + } + private boolean hasMoved(MotionEvent e) { return mTouchSlopSquared < (e.getX() - mDown.x) * (e.getX() - mDown.x) + (e.getY() - mDown.y) * (e.getY() - mDown.y); } + private void onEnterSelect(PieItem item) { + if (mCurrentItem != null) { + mCurrentItem.setSelected(false); + } + if (item != null && item.isEnabled()) { + item.setSelected(true); + mCurrentItem = item; + } else { + mCurrentItem = null; + } + } + + private void onEnterOpen() { + if ((mCurrentItem != getOpenItem()) && mCurrentItem.hasItems()) { + openCurrentItem(); + } + } + /** * enter a slice for a view * updates model only @@ -531,7 +667,7 @@ public class PieRenderer extends OverlayRenderer if (item != null && item.isEnabled()) { item.setSelected(true); mCurrentItem = item; - if ((mCurrentItem != mOpenItem) && mCurrentItem.hasItems()) { + if ((mCurrentItem != getOpenItem()) && mCurrentItem.hasItems()) { openCurrentItem(); } } else { @@ -543,22 +679,24 @@ public class PieRenderer extends OverlayRenderer if (mCurrentItem != null) { mCurrentItem.setSelected(false); } - if (mOpenItem != null) { - mOpenItem = null; + if (hasOpenItem()) { + PieItem item = closeOpenItem(); + onEnter(item); + } else { + mCurrentItem = null; } - mCurrentItem = null; } private void openCurrentItem() { if ((mCurrentItem != null) && mCurrentItem.hasItems()) { - mCurrentItem.setSelected(false); - mOpenItem = mCurrentItem; + mOpen.add(mCurrentItem); mOpening = true; if (mFadeIn != null) { mFadeIn.cancel(); } mXFade = new LinearAnimation(1, 0); mXFade.setDuration(PIE_XFADE_DURATION); + final PieItem ci = mCurrentItem; mXFade.setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { @@ -567,6 +705,8 @@ public class PieRenderer extends OverlayRenderer @Override public void onAnimationEnd(Animation animation) { mXFade = null; + ci.setSelected(false); + mOpening = false; } @Override @@ -578,30 +718,13 @@ public class PieRenderer extends OverlayRenderer } } - private PointF getPolar(float x, float y, boolean useOffset) { - PointF res = new PointF(); - // get angle and radius from x/y - res.x = (float) Math.PI / 2; - x = x - mCenter.x; - y = mCenter.y - y; - res.y = (float) Math.sqrt(x * x + y * y); - if (x != 0) { - res.x = (float) Math.atan2(y, x); - if (res.x < 0) { - res.x = (float) (2 * Math.PI + res.x); - } - } - res.y = res.y + (useOffset ? mTouchOffset : 0); - return res; - } - /** * @param polar x: angle, y: dist * @return the item at angle/dist or null */ private PieItem findItem(PointF polar) { // find the matching item: - List<PieItem> items = (mOpenItem != null) ? mOpenItem.getItems() : mItems; + List<PieItem> items = getOpenItem().getItems(); for (PieItem item : items) { if (inside(polar, item)) { return item; @@ -656,20 +779,6 @@ public class PieRenderer extends OverlayRenderer return (int)(-60 + 120 * Math.random()); } - @Override - public void layout(int l, int t, int r, int b) { - super.layout(l, t, r, b); - mCenterX = (r - l) / 2; - mCenterY = (b - t) / 2; - mFocusX = mCenterX; - mFocusY = mCenterY; - setCircle(mFocusX, mFocusY); - if (isVisible() && mState == STATE_PIE) { - setCenter(mCenterX, mCenterY); - layoutPie(); - } - } - private void setCircle(int cx, int cy) { mCircle.set(cx - mCircleSize, cy - mCircleSize, cx + mCircleSize, cy + mCircleSize); @@ -849,7 +958,6 @@ public class PieRenderer extends OverlayRenderer } } - private class LinearAnimation extends Animation { private float mFrom; private float mTo; |