手记

如何做一个在底部沿着一个圆环动画弹出多选菜单的例子


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

代码在:https://github.com/nickgao1986/StepSport

0人推荐
随时随地看视频
慕课网APP