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);
}
}