自定义菜单栏---卫星菜单栏

来源:互联网 发布:网络赚钱的门路和技巧 编辑:程序博客网 时间:2024/05/31 19:35

在android开发中卫星菜单栏比较常用,而且不失为一个比较炫的而且简单实现的自定义控件

1.

下面开始实现功能,ok~首先先来看要实现的功能:
1.点击主菜单按钮 按钮旋转360 子菜单按钮菜单展开
2.在展开状态下点击主菜单,然后子菜单合拢
3.子菜单按钮会有旋转,平移,渐变动画
4.在展开状态下,点击子菜单按钮,被点击按钮放大消,其他没有被点击的子菜单消失
5.在demo中添加listView,如果菜单为展开状态,滑动listViewh会自动合拢
6.主菜单和子菜单布局在代码中做了判单,在左上,左下,右上,右下都不会影响效果,有需求 可以检测

2.

    下面来看效果图:

这里写图片描述

3.

功能和效果介绍完之后开始详细解析代码:
3.1布局的设置以及自定义属性的实现

在res->value 下设置自定义属性,然后自定义菜单继承属性:

<?xml version="1.0" encoding="utf-8"?><resources>    <attr name="position">        <enum name="left_top" value="0"/>        <enum name="left_bottom" value="1"/>        <enum name="right_top" value="2"/>        <enum name="right_bottom" value="3"/>    </attr>    <attr name="radius"        format="dimension"/>    <declare-styleable name="ArcMenu">        <attr name="position"/>        <attr name="radius"/>    </declare-styleable></resources>

由以上代码可以看到 设置了一个Position 就是主菜单按钮的位置设定,(有四个值,可以自己定制);然后设置了半径(不是严格意义上的半径),就是自菜单和主菜单的直线长度;然后自定义空间ArcMenu 继承 然后再MainActivity中加入ArcMenu并且在xml中实现自定义属性(即位置和半径)

<?xml version="1.0" encoding="utf-8"?><FrameLayout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:myattr = "http://schemas.android.com/apk/res/com.example.custonmenu"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:padding="10dp">    <!--至少有一个主按钮  若干个菜单按钮-->    <com.example.custonmenu.custom.ArcMenu        android:id="@+id/arc"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Hello World!"        myattr:position = "right_bottom"        myattr:radius = "100dp"        >        <RelativeLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="@drawable/bg_btn">            <ImageView                android:layout_centerInParent="true"                android:id="@+id/button"                android:layout_width="30dp"                android:layout_height="30dp"                android:src="@drawable/icon_close"/>        </RelativeLayout>        <ImageView            android:id="@+id/btn_eat"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:src="@drawable/icon_eat"            android:tag="eat"/>        <ImageView            android:id="@+id/btn_movie"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:src="@drawable/icon_moive"            android:tag="movie"/>        <ImageView            android:id="@+id/btn_play"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:src="@drawable/icon_play"            android:tag="play"/>        <ImageView            android:id="@+id/btn_sleep"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:src="@drawable/icon_sleep"            android:tag="sleep"/>    </com.example.custonmenu.custom.ArcMenu></FrameLayout>

在根布局中生命自定义属性 这里定义为myattr,以便后面使用该自定义属性,仔细看xml代码 这里没有sm好说的

3.2核心自定义代码类ArcMenu的实现

自定义一个菜单的自定义空间ArcMenu,实现其半径的获取和初始position的设置

public class ArcMenu extends ViewGroup implements View.OnClickListener {    /**自定义属性 半径*/    private int mRadius;    /**设置value 用于匹配attrs*/    private final int LEFT_TOP = 0;    private final int LEFT_BOTTOM = 1;    private final int RIGHT_TOP = 2;    private final int RIGHT_BOTTOM = 3;    /**设置初始的位置和按钮状态*/    private Position mPosition = Position.RIGHT_BOTTOM;    private State mState = State.CLOSE;    private View mainMenu;    /**两个自定义的属性 position radius     * @param     *     * */    private enum Position{        LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM    }    /**菜单的打开和关闭状态的枚举类型*/    private enum State{        OPEN,CLOSE    }    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);        /**给半径设置属性 dp*///        mRadius = TypedValue.applyDimension(//                TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());        //获取自定义属性        //获取position的value        TypedArray type = context.getTheme().                obtainStyledAttributes(attrs, R.styleable.ArcMenu, defStyleAttr, 0);        int POS = type.getInt(R.styleable.ArcMenu_position, RIGHT_BOTTOM);        switch (POS){            case LEFT_TOP:                mPosition = Position.LEFT_TOP;                break;            case LEFT_BOTTOM:                mPosition = Position.LEFT_BOTTOM;                break;            case RIGHT_TOP:                mPosition = Position.RIGHT_TOP;                break;            case RIGHT_BOTTOM:                mPosition = Position.RIGHT_BOTTOM;                break;        }        //设置半径        mRadius = (int) type.getDimension(R.styleable.ArcMenu_radius,                TypedValue.applyDimension(                TypedValue.COMPLEX_UNIT_DIP, 150, getResources().                                getDisplayMetrics()));        //Log.i("mRadius = COMPLEX_UNIT_DIP",mPosition + "===" + mRadius);        type.recycle();    }

初始化成员变量,Position和State(开关状态)这里用枚举来记录多种类型,在构造方法中从xml根据自定义属性attr来去除半径设置到mRadius,根据xml中属性position判断来给mPosition赋值,在这里基本初始化完毕;
然后开始设置其measure方法实现ArcMenu中对其字孩子的测量,对每一个子孩子的宽高进行测量:

@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //测量子孩子        int count = getChildCount();        for (int i = 0; i < count; i++) {            View view = getChildAt(i);            measureChild(view,widthMeasureSpec,heightMeasureSpec);        }        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }
    接下来 开始在onLayout最后对空间进行重新布局:
   @Override    protected void onLayout(boolean change, int l, int t, int r, int b) {        if (change){            //设置主menu            layoutMainMenu();            //设置item            layoutItemMenu();        }    }
        接着看主menu的布局设置:
private void layoutMainMenu() {        mainMenu = getChildAt(0);        mainMenu.setOnClickListener(this);        int l = 0;        int t = 0;        int mWidth = mainMenu.getMeasuredWidth();        int mHeight = mainMenu.getMeasuredHeight();        switch (mPosition){            case LEFT_TOP:                l = 0;                t = 0;                break;            case LEFT_BOTTOM:                l = 0;                t = getMeasuredHeight() - mHeight;                break;            case RIGHT_TOP:                l = getMeasuredWidth() - mWidth;                t = 0;                break;            case RIGHT_BOTTOM:                l = getMeasuredWidth() - mWidth;                t = getMeasuredHeight() - mHeight;                break;        }        mainMenu.layout(l,t,l+mWidth,t+mHeight);    }

分别对LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM的位置进行left,top,right,bottom的设置,这样主菜单放在哪里都可以分布;
接着看子菜单的布局:

private void layoutItemMenu() {        int count = getChildCount();        for (int i = 0; i < count - 1; i++) {            //第一个字孩子市主菜单  不在这里布局            View itemMenu = getChildAt(i + 1);            //设置item点击            initItemClick(itemMenu,i + 1);            itemMenu.setVisibility(GONE);            int mItemHeight = itemMenu.getMeasuredHeight();            int mItemWidth = itemMenu.getMeasuredWidth();            int il = (int) (mRadius * Math.sin(Math.PI/2/(count-2) * i));            int it = (int) (mRadius * Math.cos(Math.PI/2/(count - 2) * i));            if (mPosition == Position.RIGHT_BOTTOM || mPosition == Position.RIGHT_TOP){                //高是不变的  l要改变                il = getMeasuredWidth() - il - mItemWidth;            }            if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM){                it = getMeasuredHeight() - it - mItemHeight;            }            itemMenu.layout(il,it,il + mItemWidth,it + mItemHeight);        }    }

这里用到一点三角函数的知识建议画图做了解,首先以LEFT_TOP为基准设置il和it,il = mRadius * sin(PI/2/3*i),it = mRadius * cos(PI/2/3*i);根据不同位置进行变化 最后统一调用layout进行布局:

int il = (int) (mRadius * Math.sin(Math.PI/2/(count-2) * i));            int it = (int) (mRadius * Math.cos(Math.PI/2/(count - 2) * i));            if (mPosition == Position.RIGHT_BOTTOM || mPosition == Position.RIGHT_TOP){                //高是不变的  l要改变                il = getMeasuredWidth() - il - mItemWidth;            }            if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM){                it = getMeasuredHeight() - it - mItemHeight;            }            itemMenu.layout(il,it,il + mItemWidth,it + mItemHeight);

当然需要注意的事 一开始默认菜单为关闭 所以需要把子菜单设置为不可见;接着就需要看主菜单和子菜单的点击事件了 这里需要加入动画;
首先看主菜单的点击事件:

@Override    public void onClick(View view) {        //设置按钮的旋转        setRotaAnim(view);        //切换菜单        setChangeMenu();    }
        首先来看主菜单的自身的旋转动画:
private void setRotaAnim(View view) {        RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);        rotateAnimation.setFillAfter(true);        rotateAnimation.setDuration(500);        view.startAnimation(rotateAnimation);    }

很简单 就是一个旋转动画,那么接下来看点击主菜单,子菜单按钮的飞入飞出动画,这里包含3中动画,平移+旋转+渐变:

public void setChangeMenu() {        int end = 0;        int start = 0;        TranslateAnimation translateAnimation;        int count = getChildCount();        for (int i = 0; i < count - 1; i++) {            final View itemView = getChildAt(i + 1);            itemView.setVisibility(VISIBLE);            int il = (int) (mRadius * Math.sin(Math.PI/2/(count-2) * i));            int it = (int) (mRadius * Math.cos(Math.PI/2/(count - 2) * i));            //对每一个item添加动画            AnimationSet animationSet = new AnimationSet(true);            //添加平移动画            //判断位移的距离            int xFlat = 1;            int yFlat = 1;            //开始判断            if (mPosition == Position.LEFT_TOP || mPosition == Position.LEFT_BOTTOM){                xFlat = -1;            }            if (mPosition == Position.LEFT_TOP || mPosition == Position.RIGHT_TOP){                yFlat = -1;            }            if (mState == State.CLOSE){                translateAnimation = new TranslateAnimation(xFlat * il, 0, yFlat * it, 0);                itemView.setClickable(true);                itemView.setFocusable(true);            }else {                translateAnimation = new TranslateAnimation(0,xFlat * il, 0, yFlat * it);                itemView.setClickable(false);                itemView.setFocusable(false);            }            translateAnimation.setFillAfter(true);            translateAnimation.setDuration(500);            translateAnimation.setStartOffset((i * 100) / count);            translateAnimation.setAnimationListener(new             //设置旋转动画            RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);            rotateAnimation.setFillAfter(true);            rotateAnimation.setDuration(500);            AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0f);            alphaAnimation.setDuration(500);            alphaAnimation.setFillAfter(true);            AlphaAnimation alphaAnimation1 = new AlphaAnimation(0f, 1.0f);            alphaAnimation1.setDuration(500);            alphaAnimation1.setFillAfter(true);            animationSet.addAnimation(rotateAnimation);            animationSet.addAnimation(translateAnimation);            if (mState == State.OPEN){                animationSet.addAnimation(alphaAnimation);            }else {                animationSet.addAnimation(alphaAnimation1);            }            animationSet.setFillAfter(true);            animationSet.setDuration(500);            itemView.startAnimation(animationSet);        }        changeState();    }

首先大体来看,是AnimSet的组合动画,重点要看子菜单按钮的平移动画的位移变化,这里是比较绕的:

  int xFlat = 1;            int yFlat = 1;            //开始判断            if (mPosition == Position.LEFT_TOP || mPosition == Position.LEFT_BOTTOM){                xFlat = -1;            }            if (mPosition == Position.LEFT_TOP || mPosition == Position.RIGHT_TOP){                yFlat = -1;            }            if (mState == State.CLOSE){                translateAnimation = new TranslateAnimation(xFlat * il, 0, yFlat * it, 0);                itemView.setClickable(true);                itemView.setFocusable(true);            }else {                translateAnimation = new TranslateAnimation(0,xFlat * il, 0, yFlat * it);                itemView.setClickable(false);                itemView.setFocusable(false);            }

首先是以子菜单按钮自身来说,x = 0 ,y = 0,他们的位移均为上面代码il和it,就是前面用三角函数获取的偏移量,当左上和左下时候,x方向收缩时候,子菜单x坐标市减少的,在左上和右上,y方向同样坐标是减少的,所以要通过xFlag和yFlag的正负来控制减少和增加,建议画图自己研究一下,很简单;
最后不要忘了在点击菜单完毕后要重新设置menu的state:

private void changeState() {        mState = (mState == State.CLOSE?State.OPEN:State.CLOSE);    }
        最后设置字按钮的点击事件:        首先复制部分代码 说明一下:
for (int i = 0; i < count - 1; i++) {            //第一个字孩子市主菜单  不在这里布局            View itemMenu = getChildAt(i + 1);            //设置item点击            initItemClick(itemMenu,i + 1);

这是遍历子孩子,因为是设置子孩子的点击,需要排除主按钮,而恰好主按钮也是子孩子,所以需要在遍历时候条件,设定为0~count-2,然后传入position时候,传入postion+1;
ok,看子菜单按钮的点击事件:

private void initItemClick(View itemMenu, final int pos) {        itemMenu.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View view) {                int count = getChildCount();                for (int i = 0; i < count - 1 ; i++) {                    View itemView = getChildAt(i + 1);                    if (i + 1 == pos){                        if (mListener != null)                            mListener.onClick(view,pos);                        //放大动画+ 透明                        AnimationSet animationSet = new AnimationSet(true);                        //放大动画                        ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f,                                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);                        //透明动画                        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0f);                        animationSet.setDuration(500);                        animationSet.setFillAfter(true);                        animationSet.addAnimation(scaleAnimation);                        animationSet.addAnimation(alphaAnimation);                        itemView.startAnimation(animationSet);                        mState = State.CLOSE;                    }else{                        //透明动画                        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0f);                        alphaAnimation.setDuration(500);                        alphaAnimation.setFillAfter(true);                        itemView.startAnimation(alphaAnimation);                    }                }            }        });    }

大体思路就是点击时候遍历所有按钮,被点击的放大伴随消失,没有点击的直接消失;当然要严格控制开/闭的状态;
最后需要在ArcMenu中定义一个对调的接口:

/**设置接口的对象*/    private OnItemClickListener mListener;    /**设置会调接口的方法*/    public void setOnItemClickListener(OnItemClickListener listener)    {        this.mListener = listener;    }    /**设置会调接口*/    public interface OnItemClickListener{        void onClick(View view ,int pos);    }

对调接口中抽象方法放在子菜单的点击事件中:(这里仅粘贴部分代码)

rivate void initItemClick(View itemMenu, final int pos) {        itemMenu.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View view) {                int count = getChildCount();                for (int i = 0; i < count - 1 ; i++) {                    View itemView = getChildAt(i + 1);                    if (i + 1 == pos){                        if (mListener != null)                            mListener.onClick(view,pos);                        //放大动画+ 透明                        AnimationSet animationSet = new AnimationSet(true);
        在Arc中设置一个记录状态的方法:
public boolean isOpen(){        if (mState == State.CLOSE){            return false;        }else {            return true;        }    }

接下来就是在MainActivity中调用了,比较简单直接粘贴MainActivity中代码:

public class MainActivity extends AppCompatActivity implements ArcMenu.OnItemClickListener {    private ArcMenu mArc;    private ListView mListView;    private ArrayList<String> mData;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        initData();        initListener();    }    private void initListener() {        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {            @Override            public void onScrollStateChanged(AbsListView absListView, int i) {            }            @Override            public void onScroll(AbsListView absListView, int i, int i1, int i2) {                if (mArc.isOpen()){                    mArc.setChangeMenu();                }            }        });    }    private void initView() {        mArc = (ArcMenu) findViewById(R.id.arc);        mListView = (ListView) findViewById(R.id.listView);    }    private void initData() {        mArc.setOnItemClickListener(this);        setData();        mListView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mData));    }    private void setData() {        mData = new ArrayList<>();        for (int i = 'A'; i < 'Z'; i++) {            mData.add(String.valueOf((char)i));        }    }    @Override    public void onClick(View view, int pos) {        Toast.makeText(this,view.getTag()+ "",Toast.LENGTH_SHORT).show();    }}

就是设置一个简单的listView,然后监听listView的滑动,当滑动过程中,通过Arcmenu的isOpen()返回值判断,当为打开状态时候,滑动自动合闭;调用ArcMenu中setChangeMenu
这样一个卫星样式的可打开,可定制实现功能的菜单栏就实现了

下面看完整代码:
//ArcMenu:

package com.example.custonmenu.custom;import android.content.BroadcastReceiver;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.util.Log;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 android.widget.Switch;import com.example.custonmenu.MainActivity;import com.example.custonmenu.R;/** * Created by houruixiang on 2017/8/7. * 自定义菜单栏 */public class ArcMenu extends ViewGroup implements View.OnClickListener {    /**自定义属性 半径*/    private int mRadius;    /**设置value 用于匹配attrs*/    private final int LEFT_TOP = 0;    private final int LEFT_BOTTOM = 1;    private final int RIGHT_TOP = 2;    private final int RIGHT_BOTTOM = 3;    /**设置初始的位置和按钮状态*/    private Position mPosition = Position.RIGHT_BOTTOM;    private State mState = State.CLOSE;    private View mainMenu;    /**两个自定义的属性 position radius     * @param     *     * */    private enum Position{        LEFT_TOP,LEFT_BOTTOM,RIGHT_TOP,RIGHT_BOTTOM    }    /**菜单的打开和关闭状态的枚举类型*/    private enum State{        OPEN,CLOSE    }    /**设置接口的对象*/    private OnItemClickListener mListener;    /**设置会调接口的方法*/    public void setOnItemClickListener(OnItemClickListener listener)    {        this.mListener = listener;    }    /**设置会调接口*/    public interface OnItemClickListener{        void onClick(View view ,int pos);    }    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);        /**给半径设置属性 dp*///        mRadius = TypedValue.applyDimension(//                TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());        //获取自定义属性        //获取position的value        TypedArray type = context.getTheme().                obtainStyledAttributes(attrs, R.styleable.ArcMenu, defStyleAttr, 0);        int POS = type.getInt(R.styleable.ArcMenu_position, RIGHT_BOTTOM);        switch (POS){            case LEFT_TOP:                mPosition = Position.LEFT_TOP;                break;            case LEFT_BOTTOM:                mPosition = Position.LEFT_BOTTOM;                break;            case RIGHT_TOP:                mPosition = Position.RIGHT_TOP;                break;            case RIGHT_BOTTOM:                mPosition = Position.RIGHT_BOTTOM;                break;        }        //设置半径        mRadius = (int) type.getDimension(R.styleable.ArcMenu_radius,                TypedValue.applyDimension(                TypedValue.COMPLEX_UNIT_DIP, 150, getResources().                                getDisplayMetrics()));        //Log.i("mRadius = COMPLEX_UNIT_DIP",mPosition + "===" + mRadius);        type.recycle();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        //测量子孩子        int count = getChildCount();        for (int i = 0; i < count; i++) {            View view = getChildAt(i);            measureChild(view,widthMeasureSpec,heightMeasureSpec);        }        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onLayout(boolean change, int l, int t, int r, int b) {        if (change){            //设置主menu            layoutMainMenu();            //设置item            layoutItemMenu();        }    }    private void layoutItemMenu() {        int count = getChildCount();        for (int i = 0; i < count - 1; i++) {            //第一个字孩子市主菜单  不在这里布局            View itemMenu = getChildAt(i + 1);            //设置item点击            initItemClick(itemMenu,i + 1);            itemMenu.setVisibility(GONE);            int mItemHeight = itemMenu.getMeasuredHeight();            int mItemWidth = itemMenu.getMeasuredWidth();            int il = (int) (mRadius * Math.sin(Math.PI/2/(count-2) * i));            int it = (int) (mRadius * Math.cos(Math.PI/2/(count - 2) * i));            if (mPosition == Position.RIGHT_BOTTOM || mPosition == Position.RIGHT_TOP){                //高是不变的  l要改变                il = getMeasuredWidth() - il - mItemWidth;            }            if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM){                it = getMeasuredHeight() - it - mItemHeight;            }            itemMenu.layout(il,it,il + mItemWidth,it + mItemHeight);        }    }    private void initItemClick(View itemMenu, final int pos) {        itemMenu.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View view) {                int count = getChildCount();                for (int i = 0; i < count - 1 ; i++) {                    View itemView = getChildAt(i + 1);                    if (i + 1 == pos){                        if (mListener != null)                            mListener.onClick(view,pos);                        //放大动画+ 透明                        AnimationSet animationSet = new AnimationSet(true);                        //放大动画                        ScaleAnimation scaleAnimation = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f,                                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);                        //透明动画                        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0f);                        animationSet.setDuration(500);                        animationSet.setFillAfter(true);                        animationSet.addAnimation(scaleAnimation);                        animationSet.addAnimation(alphaAnimation);                        itemView.startAnimation(animationSet);                        mState = State.CLOSE;                    }else{                        //透明动画                        AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0f);                        alphaAnimation.setDuration(500);                        alphaAnimation.setFillAfter(true);                        itemView.startAnimation(alphaAnimation);                    }                }            }        });    }    private void layoutMainMenu() {        mainMenu = getChildAt(0);        mainMenu.setOnClickListener(this);        int l = 0;        int t = 0;        int mWidth = mainMenu.getMeasuredWidth();        int mHeight = mainMenu.getMeasuredHeight();        switch (mPosition){            case LEFT_TOP:                l = 0;                t = 0;                break;            case LEFT_BOTTOM:                l = 0;                t = getMeasuredHeight() - mHeight;                break;            case RIGHT_TOP:                l = getMeasuredWidth() - mWidth;                t = 0;                break;            case RIGHT_BOTTOM:                l = getMeasuredWidth() - mWidth;                t = getMeasuredHeight() - mHeight;                break;        }        mainMenu.layout(l,t,l+mWidth,t+mHeight);    }    @Override    public void onClick(View view) {        //设置按钮的旋转        setRotaAnim(view);        //切换菜单        setChangeMenu();    }    public void setChangeMenu() {        int end = 0;        int start = 0;        TranslateAnimation translateAnimation;        int count = getChildCount();        for (int i = 0; i < count - 1; i++) {            final View itemView = getChildAt(i + 1);            itemView.setVisibility(VISIBLE);            int il = (int) (mRadius * Math.sin(Math.PI/2/(count-2) * i));            int it = (int) (mRadius * Math.cos(Math.PI/2/(count - 2) * i));            //对每一个item添加动画            AnimationSet animationSet = new AnimationSet(true);            //添加平移动画            //判断位移的距离            int xFlat = 1;            int yFlat = 1;            //开始判断            if (mPosition == Position.LEFT_TOP || mPosition == Position.LEFT_BOTTOM){                xFlat = -1;            }            if (mPosition == Position.LEFT_TOP || mPosition == Position.RIGHT_TOP){                yFlat = -1;            }            if (mState == State.CLOSE){                translateAnimation = new TranslateAnimation(xFlat * il, 0, yFlat * it, 0);                itemView.setClickable(true);                itemView.setFocusable(true);            }else {                translateAnimation = new TranslateAnimation(0,xFlat * il, 0, yFlat * it);                itemView.setClickable(false);                itemView.setFocusable(false);            }            translateAnimation.setFillAfter(true);            translateAnimation.setDuration(500);            translateAnimation.setStartOffset((i * 100) / count);            translateAnimation.setAnimationListener(new Animation.AnimationListener() {                @Override                public void onAnimationStart(Animation animation) {                }                @Override                public void onAnimationEnd(Animation animation) {                    if (mState == State.CLOSE){                        itemView.setVisibility(GONE);                    }                    //Log.i("onAnimationEnd","nihao  i m u");                    //itemView.setVisibility(GONE);                }                @Override                public void onAnimationRepeat(Animation animation) {                }            });            //设置旋转动画            RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);            rotateAnimation.setFillAfter(true);            rotateAnimation.setDuration(500);            AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0f);            alphaAnimation.setDuration(500);            alphaAnimation.setFillAfter(true);            AlphaAnimation alphaAnimation1 = new AlphaAnimation(0f, 1.0f);            alphaAnimation1.setDuration(500);            alphaAnimation1.setFillAfter(true);            animationSet.addAnimation(rotateAnimation);            animationSet.addAnimation(translateAnimation);            if (mState == State.OPEN){                animationSet.addAnimation(alphaAnimation);            }else {                animationSet.addAnimation(alphaAnimation1);            }            animationSet.setFillAfter(true);            animationSet.setDuration(500);            itemView.startAnimation(animationSet);        }        changeState();    }    private void changeState() {        mState = (mState == State.CLOSE?State.OPEN:State.CLOSE);    }    private void setRotaAnim(View view) {        RotateAnimation rotateAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);        rotateAnimation.setFillAfter(true);        rotateAnimation.setDuration(500);        view.startAnimation(rotateAnimation);    }    public boolean isOpen(){        if (mState == State.CLOSE){            return false;        }else {            return true;        }    }}

//MainActivity:

package com.example.custonmenu;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.AbsListView;import android.widget.ArrayAdapter;import android.widget.ListView;import android.widget.Toast;import com.example.custonmenu.custom.ArcMenu;import java.util.ArrayList;public class MainActivity extends AppCompatActivity implements ArcMenu.OnItemClickListener {    private ArcMenu mArc;    private ListView mListView;    private ArrayList<String> mData;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        initData();        initListener();    }    private void initListener() {        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {            @Override            public void onScrollStateChanged(AbsListView absListView, int i) {            }            @Override            public void onScroll(AbsListView absListView, int i, int i1, int i2) {                if (mArc.isOpen()){                    mArc.setChangeMenu();                }            }        });    }    private void initView() {        mArc = (ArcMenu) findViewById(R.id.arc);        mListView = (ListView) findViewById(R.id.listView);    }    private void initData() {        mArc.setOnItemClickListener(this);        setData();        mListView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,mData));    }    private void setData() {        mData = new ArrayList<>();        for (int i = 'A'; i < 'Z'; i++) {            mData.add(String.valueOf((char)i));        }    }    @Override    public void onClick(View view, int pos) {        Toast.makeText(this,view.getTag()+ "",Toast.LENGTH_SHORT).show();    }}

ok~ 就到这里了 期待共同进步;