xml文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffffffff" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Rotate Example" android:textSize="20sp" /> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:id="@+id/rcBottomTabBar" android:layout_centerVertical="true" android:layout_margin="1dp" android:background="#ffffff" android:paddingLeft="10dp" > <com.ringcentral.android.utils.ui.menu.PlusButton android:id="@+id/rcMenuButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:src="@drawable/launch_bar_btn_all_normal" /> </RelativeLayout> <RelativeLayout android:id="@+id/rcActionMenuBg" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_above="@id/rcBottomTabBar" android:layout_alignParentTop="true" android:visibility="gone" android:background="#f0ffffff"> <com.ringcentral.android.utils.ui.menu.ActionMenu android:id="@+id/rcActionMenu" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginLeft="14dp" android:layout_marginRight="14dp" android:visibility="gone"/> </RelativeLayout> </RelativeLayout>
ActionMenu这个控件就是展示菜单用的
public class ActionMenu extends ViewGroup implements IAction, View.OnClickListener { public ActionMenu(Context context, AttributeSet attrs) { super(context, attrs); initData(context); } private final int DEFAULT_ANI_DURATION = 200; private int mAnimationDuration = DEFAULT_ANI_DURATION; private MenuStyle mMenuStyle = MenuStyle.Cycle; private IStyle mStyle; private IAnimation mAnimation; private ArrayList<View> mArrayMenus; private boolean mIsInit = false; private OnMenuItemSelect mOnMenuItemSelect; private IAnimationDelegate mAniDelegate; public static enum MenuStyle { Cycle, Collapse } public interface OnMenuItemSelect { /** * event will be triggered when one of the item has been selected. * * @param itemId -1 dismiss menu */ public void onMenuItemSelect(int itemId); } public ArrayList<View> getMenuItems() { return mArrayMenus; } public int getItemSize() { return mArrayMenus.size(); } private void initData(Context context) { if (!mIsInit) { //this.setOnClickListener(this); MenuStyle mMenuStyle = MenuStyle.Cycle; setMenuStyle(mMenuStyle); mArrayMenus = new ArrayList<View>(); mIsInit = true; } } public void setMenuItems(List<MenuItemInfo> itemList, MenuStyle menuStyle) { setMenuStyle(menuStyle); mArrayMenus.clear(); removeAllViews(); for (MenuItemInfo item : itemList) { addItem(mStyle.generateMenuItem(getContext(), item)); } } public void setOnMenuItemSelectListener(OnMenuItemSelect listener) { mOnMenuItemSelect = listener; } private void addItem(View item) { item.setOnClickListener(this); mArrayMenus.add(item); this.addView(item); } public void setAnimationDelegate(IAnimationDelegate aniDelegate) { mAniDelegate = aniDelegate; mAnimation.setAnimationDelegate(aniDelegate); } private void setMenuStyle(MenuStyle menuStyle) { mMenuStyle = menuStyle; if (menuStyle == MenuStyle.Cycle) { mStyle = new CycleStyle(this); mAnimation = new MenuRotateAnimation(); mAnimation.setAnimationDelegate(mAniDelegate); } } @Override public void onClick(View v) { } @Override public void openWithAnimation() { mAnimation.open(this, mAnimationDuration); } @Override public void closeWithAnimation() { mAnimation.close(this, mAnimationDuration); } @Override public void closeWithoutAnimation() { mAnimation.close(this, 0); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mStyle.onSizeChanged(w, h, oldw, oldh); super.onSizeChanged(w, h, oldw, oldh); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { mStyle.onLayout(changed, l, t, r, b); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ViewGroup.LayoutParams layoutParams = this.getLayoutParams(); setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec)); for (int i = 0; i < mArrayMenus.size(); ++i) { View item = mArrayMenus.get(i); this.measureChild(item, widthMeasureSpec, heightMeasureSpec); } int height = mStyle.onMeasure(widthMeasureSpec, heightMeasureSpec); layoutParams.height = height; } }
public interface IStyle { public View generateMenuItem(Context context, MenuItemInfo itemInfo); public void onLayout(boolean changed, int l, int t, int r, int b); public int onMeasure(int widthMeasureSpec, int heightMeasureSpec); public void onSizeChanged(int w, int h, int oldw, int oldh); }
使用了interface Istyle很好的实现了代码复用:
我们这次使用的是圆弧型的:
public class CycleStyle implements IStyle{ private ActionMenu mActionMenu; private int mItemPadding = 20; private ArrayList<Position> mPositions = new ArrayList<Position>(); private float mRadian = 3.14f / 180.0f; private float mCenterX = 0.0f; private float mCenterY = 0.0f; private float mRadius = 0.0f; public CycleStyle(ActionMenu menu) { mActionMenu = menu; mItemPadding = mActionMenu.getResources().getDimensionPixelSize(R.dimen.tab_item_padding); } @Override public View generateMenuItem(Context context, MenuItemInfo itemInfo) { TextView item = new TextView(context); item.setId(itemInfo.getItemId()); item.setText(itemInfo.getItemText()); item.setGravity(Gravity.CENTER); item.setCompoundDrawablesWithIntrinsicBounds(0, itemInfo.getItemIcon(), 0, 0); return item; } @Override public void onLayout(boolean changed, int l, int t, int r, int b) { ArrayList<View> menus = mActionMenu.getMenuItems(); int width = mActionMenu.getMeasuredWidth(); int height = mActionMenu.getMeasuredHeight(); if (menus.size() != mPositions.size()) { reCalculate(width, height); } for (int i = 0; i < menus.size(); ++i) { View item = menus.get(i); Position position = mPositions.get(i); int x = (int) position.getX(); int y = (int) position.getY(); int halfItemWidth = item.getMeasuredWidth() / 2; int halfItemHeight = item.getMeasuredHeight() / 2; item.layout(x - halfItemWidth, height - (y + halfItemHeight), x + halfItemWidth, height - (y - halfItemHeight)); } } @Override public int onMeasure(int widthMeasureSpec, int heightMeasureSpec) { return mActionMenu.getMeasuredWidth(); } @Override public void onSizeChanged(int w, int h, int oldw, int oldh) { reCalculate(w, h); } protected void reCalculate(int w, int h) { mPositions.clear(); mPositions = getPositions(w, h, mActionMenu.getItemSize()); } public ArrayList<Position> getPositions(int width, int height, int itemCount) { mCenterX = width / 2; mRadius = mCenterX - mItemPadding; mCenterY = 0.0f; ArrayList<Position> arrayPositions = new ArrayList<Position>(); float averageAngle = 180.0f / (itemCount + 1); float currentAngle = averageAngle; for (int i = 0; i < itemCount; ++i) { arrayPositions.add(toPosition(currentAngle)); currentAngle += averageAngle; } return arrayPositions; } private float angleToRadian(float angle) { return angle * mRadian; } private Position toPosition(float angle) { float radian = angleToRadian(angle); float x = mCenterX + (float) (Math.cos(radian) * mRadius); float y = mCenterY + (float) (Math.sin(radian) * mRadius); return new Position(x, y); } }
在onlayout排列好那几个选项
最后我们来看一下点击菜单的动画实现:
public class PlusMenuAnimation implements IAnimation { @Override public void setAnimationDelegate(IAnimationDelegate aniDelegate) { } @Override public void open(View view, int duration) { Animation animation = getAnimation(true, duration); view.startAnimation(animation); } @Override public void close(View view, int duration) { Animation animation = getAnimation(false, duration); view.startAnimation(animation); } private Animation getAnimation(boolean expanded, int duration) { Animation animation = new RotateAnimation(expanded ? 0.0f : 45.0f, expanded ? 45.0f : 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animation.setStartOffset(0); animation.setDuration(duration); animation.setInterpolator(new DecelerateInterpolator()); animation.setFillAfter(true); return animation; } }
还有弹出菜单选项的动画效果:
public class MenuRotateAnimation extends ViewStateManager implements IAnimation { private float mRotateDegrees = -180.0f; private IAnimationDelegate mAnimationDelegate; @Override public void setAnimationDelegate(IAnimationDelegate aniDelegate) { mAnimationDelegate = aniDelegate; } @Override public void open(final View view, int duration) { if (mAnimationDelegate != null) { mAnimationDelegate.onAnimationStart(); } setViewState(view, View.VISIBLE, true); Animation animation; animation = new android.view.animation.RotateAnimation(mRotateDegrees, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 1.0f); animation.setFillAfter(true); animation.setDuration(duration); view.startAnimation(animation); } @Override public void close(final View view, int duration) { Animation animation; animation = new android.view.animation.RotateAnimation(0, mRotateDegrees, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 1.0f); animation.setFillAfter(true); animation.setDuration(duration); animation.setStartOffset(0); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation arg0) { } @Override public void onAnimationRepeat(Animation arg0) { } @Override public void onAnimationEnd(Animation arg0) { setViewState(view, View.GONE, false); if (mAnimationDelegate != null) { mAnimationDelegate.onAnimationEnd(); } } }); view.startAnimation(animation); } }