package com.bigfat.arcmenu.view; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.AnimationSet; import android.view.animation.RotateAnimation; import android.view.animation.ScaleAnimation; import android.view.animation.TranslateAnimation; import com.bigfat.arcmenu.R; import com.bigfat.arcmenu.view.util.OnMenuItemClickListener; import com.bigfat.arcmenu.view.util.Position; import com.bigfat.arcmenu.view.util.Status; /** * @author <a href="mailto:fbzhh007@gmail.com">bigfat</a> * @since 2015/3/1 */ public class ArcMenu extends ViewGroup implements View.OnClickListener { public static final String TAG = "ArcMenu"; /** * 菜单展开半径 */ private int mRadius; /** * 菜单位置 */ private Position mPosition = Position.RIGHT_BOTTOM; /** * 菜单状态 */ private Status mCurrentStatus = Status.CLOSE; /** * 是否正在执行动画 */ private boolean mIsAnim; /** * 菜单主按钮 */ private View mCenterButton; /** * 子菜单点击回调接口 */ private OnMenuItemClickListener mOnMenuItemClickListener; public void setOnMenuClickListener(OnMenuItemClickListener onMenuItemClickListener) { this.mOnMenuItemClickListener = onMenuItemClickListener; } public ArcMenu(Context context) { this(context, null); } public ArcMenu(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ArcMenu(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取自定义属性值 TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ArcMenu, defStyleAttr, 0); //菜单位置 int pos = a.getInt(R.styleable.ArcMenu_position, Position.POS_RIGHT_BOTTOM); switch (pos) { case Position.POS_LEFT_TOP: mPosition = Position.LEFT_TOP; break; case Position.POS_LEFT_BOTTOM: mPosition = Position.LEFT_BOTTOM; break; case Position.POS_RIGHT_TOP: mPosition = Position.RIGHT_TOP; break; case Position.POS_RIGHT_BOTTOM: mPosition = Position.RIGHT_BOTTOM; break; } //菜单展开半径,默认100dp mRadius = (int) a.getDimension(R.styleable.ArcMenu_radius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics())); a.recycle(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); for (int i = 0; i < count; i++) { //测量Child measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (changed) { layoutCenterButton(); int count = getChildCount(); for (int i = 0; i < count - 1; i++) { View child = getChildAt(i); child.setVisibility(GONE); //子菜单相对于主按钮的偏移量,使子菜单与主按钮位于同一中心 int offsetX = (mCenterButton.getMeasuredWidth() - child.getMeasuredWidth()) / 2; int offsetY = (mCenterButton.getMeasuredHeight() - child.getMeasuredHeight()) / 2; double radian = Math.PI / 2 / (count - 2) * i; int childL = (int) (mRadius * Math.sin(radian)); int childT = (int) (mRadius * Math.cos(radian)); int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); switch (mPosition) { case LEFT_TOP: break; case LEFT_BOTTOM: // offsetY = -offsetY; childT = getMeasuredHeight() - childT - childHeight; break; case RIGHT_TOP: // offsetX = -offsetX; childL = getMeasuredWidth() - childL - childWidth; break; case RIGHT_BOTTOM: // offsetX = -offsetX; // offsetY = -offsetY; childT = getMeasuredHeight() - childT - childHeight; childL = getMeasuredWidth() - childL - childWidth; break; } child.layout(offsetX + childL, offsetY + childT, offsetX + childL + childWidth, offsetY + childT + childHeight); } } } /** * 定位主菜单按钮 */ private void layoutCenterButton() { mCenterButton = getChildAt(getChildCount() - 1); mCenterButton.setOnClickListener(this); int l = 0; int t = 0; int width = mCenterButton.getMeasuredWidth(); int height = mCenterButton.getMeasuredHeight(); switch (mPosition) { case LEFT_TOP: l = 0; t = 0; break; case LEFT_BOTTOM: l = 0; t = getMeasuredHeight() - height; break; case RIGHT_TOP: l = getMeasuredWidth() - width; t = 0; break; case RIGHT_BOTTOM: l = getMeasuredWidth() - width; t = getMeasuredHeight() - height; break; } mCenterButton.layout(l, t, l + width, t + height); } @Override public void onClick(View v) { if (!mIsAnim) { rotateCenterButton(v, 0f, 360f, 300); toggleMenu(300); } } /** * 切换菜单展开/收起状态 * * @param duration 子菜单项动画时间 */ private void toggleMenu(int duration) { final int count = getChildCount(); for (int i = 0; i < count - 1; i++) { final View childView = getChildAt(i); int offset = duration / 20; int x = mCenterButton.getLeft() - childView.getLeft(); int y = mCenterButton.getTop() - childView.getTop(); TranslateAnimation translateAnimation; if (mCurrentStatus == Status.CLOSE) { translateAnimation = new TranslateAnimation(x, 0, y, 0); childView.setClickable(true); childView.setFocusable(true); } else { translateAnimation = new TranslateAnimation(0, x, 0, y); childView.setClickable(false); childView.setFocusable(false); } translateAnimation.setDuration(duration); translateAnimation.setStartOffset(i * offset); final int finalI = i; translateAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { mIsAnim = true; childView.setVisibility(VISIBLE); } @Override public void onAnimationEnd(Animation animation) { if (mCurrentStatus == Status.OPEN) {//open to close childView.setVisibility(View.GONE); } if (finalI == count - 2) {//最后一个子菜单的动画结束 mIsAnim = false; changeStatus(); } } @Override public void onAnimationRepeat(Animation animation) { } }); childView.startAnimation(translateAnimation); //设置监听器 childView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mOnMenuItemClickListener != null) { mOnMenuItemClickListener.onClick(childView, finalI); } menuItemAnim(finalI); changeStatus(); } }); } } private void changeStatus() { mCurrentStatus = mCurrentStatus == Status.OPEN ? Status.CLOSE : Status.OPEN; } /** * menuItem的点击动画 * * @param pos menuItem的位置0~count-2 */ private void menuItemAnim(int pos) { for (int i = 0; i < getChildCount() - 1; i++) { View childView = getChildAt(i); if (pos == i) { childView.startAnimation(scaleBigAnim(300)); } else { childView.startAnimation(scaleSmallAnim(300)); } childView.setFocusable(false); childView.setClickable(false); } } private Animation scaleBigAnim(int duration) { AnimationSet animationSet = new AnimationSet(true); ScaleAnimation scaleAnimation = new ScaleAnimation(1, 4, 1, 4, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0); animationSet.addAnimation(scaleAnimation); animationSet.addAnimation(alphaAnimation); animationSet.setDuration(duration); animationSet.setFillAfter(true); animationSet.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { mIsAnim = true; } @Override public void onAnimationEnd(Animation animation) { mIsAnim = false; } @Override public void onAnimationRepeat(Animation animation) { } }); return animationSet; } private Animation scaleSmallAnim(int duration) { AnimationSet animationSet = new AnimationSet(true); ScaleAnimation scaleAnimation = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); AlphaAnimation alphaAnimation = new AlphaAnimation(1, 0); animationSet.addAnimation(scaleAnimation); animationSet.addAnimation(alphaAnimation); animationSet.setDuration(duration); animationSet.setFillAfter(true); animationSet.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { mIsAnim = true; } @Override public void onAnimationEnd(Animation animation) { mIsAnim = false; } @Override public void onAnimationRepeat(Animation animation) { } }); return animationSet; } private void rotateCenterButton(View v, float start, float end, int duration) { RotateAnimation animation = new RotateAnimation(start, end, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animation.setDuration(duration); v.startAnimation(animation); } }