PieMenu.java revision 716773853705e67fe5d5afbf9c3b917ff8a5e298
1376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb/* 2376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Copyright (C) 2010 The Android Open Source Project 3376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 4376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Licensed under the Apache License, Version 2.0 (the "License"); 5376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * you may not use this file except in compliance with the License. 6376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * You may obtain a copy of the License at 7376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 8376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * http://www.apache.org/licenses/LICENSE-2.0 9376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * 10376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * Unless required by applicable law or agreed to in writing, software 11376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * distributed under the License is distributed on an "AS IS" BASIS, 12376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * See the License for the specific language governing permissions and 14376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * limitations under the License. 15376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 16376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 17376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpackage com.android.browser.view; 18376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 19376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport com.android.browser.R; 20376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 21376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.Context; 22376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.content.res.Resources; 23376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Canvas; 24565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolbimport android.graphics.Paint; 25565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolbimport android.graphics.Path; 26376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.Point; 27376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.graphics.PointF; 28565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolbimport android.graphics.RectF; 292a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.graphics.drawable.Drawable; 30376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.util.AttributeSet; 31376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.MotionEvent; 322a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolbimport android.view.SoundEffectConstants; 33376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.view.View; 344be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reckimport android.view.ViewGroup; 35376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport android.widget.FrameLayout; 36376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 37376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.ArrayList; 38376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbimport java.util.List; 39376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 40376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolbpublic class PieMenu extends FrameLayout { 41376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 420860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private static final int MAX_LEVELS = 5; 43376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 44376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public interface PieController { 45376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 46376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * called before menu opens to customize menu 47376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * returns if pie state has been changed 48376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 49376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public boolean onOpen(); 50376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 510860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb 521acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb /** 531acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb * A view like object that lives off of the pie menu 541acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb */ 551acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb public interface PieView { 561acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 57eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb public interface OnLayoutListener { 58eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb public void onLayout(int ax, int ay, boolean left); 59eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb } 60eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb 61eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb public void setLayoutListener(OnLayoutListener l); 62eb95db48b01b3db935601f25bd1a2358674b76daMichael Kolb 63565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb public void layout(int anchorX, int anchorY, boolean onleft, float angle); 641acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 651acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb public void draw(Canvas c); 661acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 671acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb public boolean onTouchEvent(MotionEvent evt); 681acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 691acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 701acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 71376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private Point mCenter; 72376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mRadius; 73376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mRadiusInc; 74376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private int mSlop; 75565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private int mTouchOffset; 76376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 77376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private boolean mOpen; 78376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private PieController mController; 79376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 800860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private List<PieItem> mItems; 810860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private int mLevels; 820860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private int[] mCounts; 831acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb private PieView mPieView = null; 842a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 850860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private Drawable mBackground; 86565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private Paint mNormalPaint; 87565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private Paint mSelectedPaint; 882a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 890860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb // touch handling 900860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb PieItem mCurrentItem; 912a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 92716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb private boolean mUseBackground; 93716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb 94376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 95376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 96376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param attrs 97376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param defStyle 98376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 99376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context, AttributeSet attrs, int defStyle) { 100376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context, attrs, defStyle); 101376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 102376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 103376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 104376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 105376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 106376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param attrs 107376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 108376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context, AttributeSet attrs) { 109376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context, attrs); 110376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 111376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 112376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 113376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 114376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * @param context 115376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 116376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public PieMenu(Context context) { 117376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb super(context); 118376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb init(context); 119376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 120376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 121376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void init(Context ctx) { 1220860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mItems = new ArrayList<PieItem>(); 1230860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mLevels = 0; 1240860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCounts = new int[MAX_LEVELS]; 125376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb Resources res = ctx.getResources(); 1260860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mRadius = (int) res.getDimension(R.dimen.qc_radius_start); 1270860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mRadiusInc = (int) res.getDimension(R.dimen.qc_radius_increment); 128376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mSlop = (int) res.getDimension(R.dimen.qc_slop); 129565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mTouchOffset = (int) res.getDimension(R.dimen.qc_touch_offset); 130376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mOpen = false; 131376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setWillNotDraw(false); 132376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setDrawingCacheEnabled(false); 133376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter = new Point(0,0); 1340860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mBackground = res.getDrawable(R.drawable.qc_background_normal); 135565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mNormalPaint = new Paint(); 136565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mNormalPaint.setColor(res.getColor(R.color.qc_normal)); 137565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mNormalPaint.setAntiAlias(true); 138565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mSelectedPaint = new Paint(); 139565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mSelectedPaint.setColor(res.getColor(R.color.qc_selected)); 140565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb mSelectedPaint.setAntiAlias(true); 141376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 142376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 143376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void setController(PieController ctl) { 144376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mController = ctl; 145376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 146376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 147716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb public void setUseBackground(boolean useBackground) { 148716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb mUseBackground = useBackground; 149716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb } 150716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb 1510860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb public void addItem(PieItem item) { 152376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // add the item to the pie itself 1530860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mItems.add(item); 1540860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int l = item.getLevel(); 1550860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mLevels = Math.max(mLevels, l); 1560860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCounts[l]++; 157376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 158376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 1590860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb public void removeItem(PieItem item) { 1600860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mItems.remove(item); 161376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 162376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 163376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public void clearItems() { 1640860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mItems.clear(); 165376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 166376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 1670860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private boolean onTheLeft() { 1680860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb return mCenter.x < mSlop; 1690860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb } 170376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 1710860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb /** 1720860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb * guaranteed has center set 1730860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb * @param show 1740860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb */ 1750860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private void show(boolean show) { 176376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mOpen = show; 177376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 178376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mController != null) { 179376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb boolean changed = mController.onOpen(); 180376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 1810860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb layoutPie(); 182376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 183376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (!show) { 1840860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCurrentItem = null; 1851acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView = null; 186376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 187376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb invalidate(); 188376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 189376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 190376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void setCenter(int x, int y) { 191376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (x < mSlop) { 192376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.x = 0; 193376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else { 194376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.x = getWidth(); 195376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 196376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb mCenter.y = y; 197376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 198376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 1990860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private void layoutPie() { 200565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb float emptyangle = (float) Math.PI / 16; 201565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int rgap = 2; 202565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int inner = mRadius + rgap; 203565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int outer = mRadius + mRadiusInc - rgap; 2041acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb int radius = mRadius; 205565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int gap = 1; 2060860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb for (int i = 0; i < mLevels; i++) { 2070860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int level = i + 1; 208565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb float sweep = (float) (Math.PI - 2 * emptyangle) / mCounts[level]; 209565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb float angle = emptyangle + sweep / 2; 2100860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb for (PieItem item : mItems) { 2110860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (item.getLevel() == level) { 2120860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb View view = item.getView(); 2130860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb view.measure(view.getLayoutParams().width, 2140860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb view.getLayoutParams().height); 2150860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int w = view.getMeasuredWidth(); 2160860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int h = view.getMeasuredHeight(); 217565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int r = inner + (outer - inner) * 2 / 3; 218565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int x = (int) (r * Math.sin(angle)); 219565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int y = mCenter.y - (int) (r * Math.cos(angle)) - h / 2; 2200860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (onTheLeft()) { 221565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb x = mCenter.x + x - w / 2; 2220860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb } else { 223565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb x = mCenter.x - x - w / 2; 2240860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb } 2250860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb view.layout(x, y, x + w, y + h); 2260860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb float itemstart = angle - sweep / 2; 227565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb Path slice = makeSlice(getDegrees(itemstart) - gap, 228565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb getDegrees(itemstart + sweep) + gap, 229565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb outer, inner, mCenter); 230565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb item.setGeometry(itemstart, sweep, inner, outer, slice); 2310860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb angle += sweep; 232376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 233376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 2340860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb inner += mRadiusInc; 2350860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb outer += mRadiusInc; 236376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 237376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 238376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 239565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb 240565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb /** 241565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * converts a 242565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * 243565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * @param angle from 0..PI to Android degrees (clockwise starting at 3 244565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * o'clock) 245565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb * @return skia angle 246565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb */ 247565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private float getDegrees(double angle) { 248565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb return (float) (270 - 180 * angle / Math.PI); 249565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb } 250565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb 2510860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb @Override 2520860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb protected void onDraw(Canvas canvas) { 2530860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (mOpen) { 254565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb int state; 255716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb if (mUseBackground) { 256716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb int w = mBackground.getIntrinsicWidth(); 257716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb int h = mBackground.getIntrinsicHeight(); 258716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb int left = mCenter.x - w; 259716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb int top = mCenter.y - h / 2; 260716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb mBackground.setBounds(left, top, left + w, top + h); 261716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb state = canvas.save(); 262716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb if (onTheLeft()) { 263716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb canvas.scale(-1, 1); 264716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb } 265716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb mBackground.draw(canvas); 266716773853705e67fe5d5afbf9c3b917ff8a5e298Michael Kolb canvas.restoreToCount(state); 267376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 2680860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb for (PieItem item : mItems) { 269565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb Paint p = item.isSelected() ? mSelectedPaint : mNormalPaint; 270565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb state = canvas.save(); 271565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb if (onTheLeft()) { 272565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb canvas.scale(-1, 1); 273565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb } 274565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb drawPath(canvas, item.getPath(), p); 275565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb canvas.restoreToCount(state); 2760860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb drawItem(canvas, item); 277376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 2781acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (mPieView != null) { 2791acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView.draw(canvas); 2801acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 281376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 2822a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb } 2832a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb 2840860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private void drawItem(Canvas canvas, PieItem item) { 2850860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int outer = item.getOuterRadius(); 2860860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int left = mCenter.x - outer; 2870860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int top = mCenter.y - outer; 2880860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb // draw the item view 2890860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb View view = item.getView(); 2900860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int state = canvas.save(); 2910860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb canvas.translate(view.getX(), view.getY()); 2920860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb view.draw(canvas); 2930860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb canvas.restoreToCount(state); 294376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 295376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 296565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private void drawPath(Canvas canvas, Path path, Paint paint) { 297565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb canvas.drawPath(path, paint); 298565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb } 299565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb 300565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private Path makeSlice(float start, float end, int outer, int inner, Point center) { 301565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb RectF bb = 302565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb new RectF(center.x - outer, center.y - outer, center.x + outer, 303565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb center.y + outer); 304565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb RectF bbi = 305565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb new RectF(center.x - inner, center.y - inner, center.x + inner, 306565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb center.y + inner); 307565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb Path path = new Path(); 308565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb path.arcTo(bb, start, end - start, true); 309565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb path.arcTo(bbi, end, start - end); 310565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb path.close(); 311565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb return path; 312565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb } 313565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb 314376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // touch handling for pie 315376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 316376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb @Override 317376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb public boolean onTouchEvent(MotionEvent evt) { 318376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float x = evt.getX(); 319376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb float y = evt.getY(); 320376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb int action = evt.getActionMasked(); 321376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (MotionEvent.ACTION_DOWN == action) { 322376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if ((x > getWidth() - mSlop) || (x < mSlop)) { 323376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb setCenter((int) x, (int) y); 324376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(true); 325376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return true; 326376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 327376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_UP == action) { 328376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 3291acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb boolean handled = false; 3301acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (mPieView != null) { 3311acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb handled = mPieView.onTouchEvent(evt); 3321acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 3330860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb PieItem item = mCurrentItem; 334376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb deselect(); 335376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(false); 3361acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (!handled && (item != null)) { 3370860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb item.getView().performClick(); 3380860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb } 339376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return true; 340376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 341376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_CANCEL == action) { 342376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mOpen) { 343376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb show(false); 344376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 345376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb deselect(); 346376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return false; 347376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (MotionEvent.ACTION_MOVE == action) { 3480860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb boolean handled = false; 349bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb PointF polar = getPolar(x, y); 3500860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb int maxr = mRadius + mLevels * mRadiusInc + 50; 3511acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (mPieView != null) { 3521acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb handled = mPieView.onTouchEvent(evt); 3531acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 3541acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if (handled) { 3551acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb invalidate(); 3561acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb return false; 3571acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 3580860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (polar.y > maxr) { 359bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb deselect(); 3600860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb show(false); 3614be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck evt.setAction(MotionEvent.ACTION_DOWN); 3624be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck if (getParent() != null) { 3634be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck ((ViewGroup) getParent()).dispatchTouchEvent(evt); 3644be9bc7f7f38723ae8c4ca1d3203de212cf214bdJohn Reck } 365bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb return false; 366bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb } 3670860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb PieItem item = findItem(polar); 3680860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (mCurrentItem != item) { 3690860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb onEnter(item); 3701acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb if ((item != null) && item.isPieView()) { 3711acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb int cx = item.getView().getLeft() + (onTheLeft() 3721acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb ? item.getView().getWidth() : 0); 3731acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb int cy = item.getView().getTop(); 3741acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView = item.getPieView(); 375565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb layoutPieView(mPieView, cx, cy, 376565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb (item.getStartAngle() + item.getSweep()) / 2); 3771acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 378376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb invalidate(); 379376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 380376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 381376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // always re-dispatch event 382376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return false; 383376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 384376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 385565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb private void layoutPieView(PieView pv, int x, int y, float angle) { 386565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb pv.layout(x, y, onTheLeft(), angle); 3871acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb } 3881acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb 389376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb /** 390376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * enter a slice for a view 391376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb * updates model only 3920860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb * @param item 393376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb */ 3940860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private void onEnter(PieItem item) { 395376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // deselect 3960860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (mCurrentItem != null) { 3970860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCurrentItem.setSelected(false); 398376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 3990860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (item != null) { 400376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // clear up stack 4012a56ecaf153d788a1acebc54b670347a1a58d693Michael Kolb playSoundEffect(SoundEffectConstants.CLICK); 4020860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb item.setSelected(true); 4031acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView = null; 404376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 4050860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCurrentItem = item; 406376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 407376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 408376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb private void deselect() { 4090860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb if (mCurrentItem != null) { 4100860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCurrentItem.setSelected(false); 411376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 4120860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb mCurrentItem = null; 4131acef69ffc079d1bc029ff7eb1f5043f7efd7f36Michael Kolb mPieView = null; 414376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 415376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 416bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb private PointF getPolar(float x, float y) { 417bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb PointF res = new PointF(); 418376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // get angle and radius from x/y 419bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) Math.PI / 2; 420376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = mCenter.x - x; 421376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (mCenter.x < mSlop) { 422376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb x = -x; 423376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 424376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb y = mCenter.y - y; 425bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.y = (float) Math.sqrt(x * x + y * y); 426376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb if (y > 0) { 427bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) Math.asin(x / res.y); 428376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } else if (y < 0) { 429bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb res.x = (float) (Math.PI - Math.asin(x / res.y )); 430376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 431bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb return res; 432bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb } 433bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb 434bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb /** 435bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * 436bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb * @param polar x: angle, y: dist 4370860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb * @return the item at angle/dist or null 438bf9c4ee33fe87881793f84091274dc59c16f3881Michael Kolb */ 4390860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb private PieItem findItem(PointF polar) { 440376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb // find the matching item: 4410860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb for (PieItem item : mItems) { 442565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb if ((item.getInnerRadius() - mTouchOffset < polar.y) 443565752e1025129ad16c030ceca9dee7a695ed73eMichael Kolb && (item.getOuterRadius() - mTouchOffset > polar.y) 4440860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb && (item.getStartAngle() < polar.x) 4450860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb && (item.getStartAngle() + item.getSweep() > polar.x)) { 4460860d99a463f7645bcc9aaa246fd8852e90dbb5dMichael Kolb return item; 447376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 448376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 449376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb return null; 450376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb } 451376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb 452376b54116e38b3b94c4d64663d1bff38352b0e59Michael Kolb} 453