Android 实现卫星菜单(精简版)

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private ArcDemo mArc;
    private ListView mListView;
    private List<String> mData;
    private ArrayAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mArc = (ArcDemo) findViewById(R.id.view_arc);
        mArc.setOnSubItemClickListener(new ArcDemo.onSubItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Toast.makeText(MainActivity.this, "position" + position, Toast.LENGTH_SHORT).show();
            }
        });

        initListView();
    }

    private void initListView() {
        mListView = (ListView) findViewById(R.id.listview);
        mData = new ArrayList<String>();
        for (int i = 'A'; i <= 'z'; i++) {
            mData.add((char) i + "");
        }
        mAdapter = new ArrayAdapter<String>(
                MainActivity.this, android.R.layout.simple_list_item_1, mData);
        mListView.setAdapter(mAdapter);

        mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {

            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (mArc.isOpen()) {
                    mArc.subItemAnim();
                }
            }
        });
    }
}

ArcDemo.java

public class ArcDemo extends ViewGroup {
    private View mButton;
    private BStatus mBStatus = BStatus.STATUS_CLOSE;
    private onSubItemClickListener onListener;

    public enum BStatus {
        STATUS_OPEN, STATUS_CLOSE
    }

    //子菜单点击接口
    public interface onSubItemClickListener {
        void onItemClick(View view, int position);
    }

    public void setOnSubItemClickListener(onSubItemClickListener mListener) {
        this.onListener = mListener;
    }

    public ArcDemo(Context context) {
        super(context);
//        this(context, null);
    }

    public ArcDemo(Context context, AttributeSet attrs) {
        super(context, attrs);
//        this(context, attrs, 0);
    }

    public ArcDemo(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int count = getChildCount();
        for (int i = 0; i < count; i++) {
            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    //添加布局,就是所要显示的控件View
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {
            //主菜单按钮
            onMainButton();
            //子菜单按钮
            onSubItemButton();
        }
    }

    //获取主菜单按钮
    private void onMainButton() {
        mButton = getChildAt(0);
        mButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //主菜单动画
                Animation rotateAnim = AnimationUtils.loadAnimation(getContext(), R.anim.anim);
                mButton.startAnimation(rotateAnim);

                //子菜单动画
                subItemAnim();
            }
        });
        int l, t, r = 0, b = 0;
        int mWidth = mButton.getMeasuredWidth();
        int mHeight = mButton.getMeasuredHeight();

        l = getMeasuredWidth() - mWidth;
        t = getMeasuredHeight() - mHeight;

        mButton.layout(l, t, getMeasuredWidth(), getMeasuredHeight());
    }

    //获取子菜单按钮
    private void onSubItemButton() {
        int count = getChildCount();
        for (int i = 0; i < count - 1; i++) {
            View childView = getChildAt(i + 1);

            //开始时不呈现子菜单
            childView.setVisibility(View.GONE);

            int radius = 350;
            int cl, ct, cr, cb;

            cr = (int) (radius * Math.sin(Math.PI / 2 / (count - 2) * i));
            cb = (int) (radius * Math.cos(Math.PI / 2 / (count - 2) * i));

            int cWidth = childView.getMeasuredWidth();
            int cHeight = childView.getMeasuredHeight();

            cl = getMeasuredWidth() - cWidth - cr;
            ct = getMeasuredHeight() - cHeight - cb;

            //layout(l,t,r,b);前两参数决定位置,后两参数决定大小
            //参数(1,t)为View控件的左上角坐标
            // (r-l,b-t)为View控件大小,r-l为控件宽度,b-t为控件高度
            childView.layout(cl, ct, getMeasuredWidth() - cr, getMeasuredHeight() - cb);
        }
    }

    //子菜单散开回笼动画
    public void subItemAnim() {
        int count = getChildCount();
        for (int i = 0; i < count - 1; i++) {
            final View cView = getChildAt(i + 1);

            //点击主菜单后,子菜单就立刻呈现,否则后面的动画无法完成
            cView.setVisibility(VISIBLE);

            int radius = 350;
            int l, t, r, d;

            r = (int) (radius * Math.sin(Math.PI / 2 / (count - 2) * i));
            d = (int) (radius * Math.cos(Math.PI / 2 / (count - 2) * i));

//            int cWidth = cView.getMeasuredWidth();
//            int cHeight = cView.getMeasuredHeight();
//
//            l = getMeasuredWidth() - cWidth - r;
//            t = getMeasuredHeight() - cHeight - d;

            AnimationSet set = new AnimationSet(true);
            Animation tranAnim = null;
            if (mBStatus == BStatus.STATUS_CLOSE) {
                //散开动画
                tranAnim = new TranslateAnimation(r, 0, d, 0);
                cView.setClickable(true);
                cView.setFocusable(true);
            } else {
                //回笼动画
                tranAnim = new TranslateAnimation(0, r, 0, d);
                cView.setClickable(false);
                cView.setFocusable(false);
            }
            tranAnim.setDuration(300);
//            tranAnim.setFillAfter(true);  //让最后一帧的动画不消失
            tranAnim.setStartOffset(100 * i / count);
            tranAnim.setAnimationListener(new Animation.AnimationListener() {
                @Override
                public void onAnimationStart(Animation animation) {

                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    if (mBStatus == BStatus.STATUS_CLOSE) {
                        cView.setVisibility(GONE);
                    }
                }

                @Override
                public void onAnimationRepeat(Animation animation) {

                }
            });


            Animation rotateAnim = new RotateAnimation(
                    0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            rotateAnim.setDuration(300);
//            rotateAnim.setFillAfter(false);

            set.addAnimation(rotateAnim);
            set.addAnimation(tranAnim);
            cView.startAnimation(set);

            //散开后子菜单的点击监听事件
            final int pos = i + 1;
            cView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (onListener != null) {
                        onListener.onItemClick(cView, pos);
                    }
                    //散开后点击子菜单动画
                    subItemClickAnim(pos - 1);
                    changStatus();
                }
            });
        }
        changStatus();
    }

    //监听子菜单状态改变
    private void changStatus() {
        mBStatus = (mBStatus == BStatus.STATUS_CLOSE ? BStatus.STATUS_OPEN : BStatus.STATUS_CLOSE);
    }

    //散开后点击子菜单动画
    private void subItemClickAnim(int pos) {
        int count = getChildCount();
        for (int i = 0;i<count-1;i++) {
            View cView = getChildAt(i+1);
            if(i == pos) {
                //变大,变透明
                cView.startAnimation(toBig());
            }
            else {
                //变小,变透明
                cView.startAnimation(toSmall());
            }
            cView.setClickable(false);
            cView.setFocusable(false);
        }
    }

    //变大,变透明
    private Animation toBig(){
        Animation big = AnimationUtils.loadAnimation(getContext(), R.anim.bigalpha);
        return big;
    }

    //变小,变透明
    private Animation toSmall(){
        Animation small = AnimationUtils.loadAnimation(getContext(),R.anim.smallalpha);
        return small;
    }

    //给ListView调用
    public boolean isOpen() {
        return mBStatus == BStatus.STATUS_OPEN;
    }

}

activity_main.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"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <my.com.example.x550v.view.ArcDemo
        android:id="@+id/view_arc"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/composer_button">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:src="@drawable/composer_icn_plus" />
        </RelativeLayout>

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_camera" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_with" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_thought" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_music" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_place" />

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/composer_sleep" />
    </my.com.example.x550v.view.ArcDemo>
</RelativeLayout>

anim.xml

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:duration="300"
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"/>
</set>

bigalpha.xml

<!--android:fillAfter="true"得加,取动画结束后的最后一帧-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <alpha
        android:duration="200"
        android:fromAlpha="1"
        android:toAlpha="0"/>
    <scale
        android:duration="200"
        android:fromXScale="1"
        android:fromYScale="1"
        android:toXScale="3"
        android:toYScale="3"
        android:pivotX="50%"
        android:pivotY="50%" />
</set>

smallalpha.xml

<!--android:fillAfter="true"得加,取动画结束后的最后一帧-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <alpha
        android:duration="200"
        android:fromAlpha="1"
        android:toAlpha="0"/>
    <scale
        android:duration="200"
        android:fromXScale="1"
        android:fromYScale="1"
        android:toXScale="0"
        android:toYScale="0"
        android:pivotX="50%"
        android:pivotY="50%" />
</set>

运行效果:

原文地址:https://www.cnblogs.com/tianhengblogs/p/5265195.html