一、总述

Android 实现卫星式菜单也叫弧形菜单的主要要做的工作如下:
1.动画的处理
2.自定义ViewGroup来实现卫星式菜单View
(1)自定义属性
a. 在attrs.xml中定义属性
b. 在布局中使用自定义属性
c. 在自定义View中读取布局文件中的自定义属性
(2)onMeasure 测量 child 即测量主按钮以及菜单项
(3)onLayout 布局 child 即布局主按钮以及菜单项
(4)设置主按钮的选择动画
a.为菜单项menuItem添加平移动画和旋转动画
b.实现菜单项menuItem的点击动画

卫星式菜单效果截图:

二、实现

上面介绍了原理和效果图,下面来看看卫星菜单类的实现:

1.布局文件的实现

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:xcskin="http://schemas.android.com/apk/res/com.xc.xcskin"    android:id="@+id/container"    android:layout_width="match_parent"    android:layout_height="match_parent" >        <com.xc.xcskin.view.XCArcMenuView        android:id="@+id/arcmenu"        android:layout_width="150dp"        android:layout_height="150dp"        android:layout_alignParentBottom="true"        android:layout_alignParentLeft="true"        xcskin:position="left_bottom"        xcskin:radius="120dp" >        <RelativeLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="@drawable/composer_button" >            <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_centerInParent="true"                android:src="@drawable/composer_icn_plus" />        </RelativeLayout>        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_camera"                android:tag="camera" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_music"                android:tag="music" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_place"                android:tag="place" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_sleep"                android:tag="sleep" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_thought"                android:tag="thought" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_with"                android:tag="with" />    </com.xc.xcskin.view.XCArcMenuView>        <com.xc.xcskin.view.XCArcMenuView        android:id="@+id/arcmenu2"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_alignParentRight="true"        xcskin:position="right_bottom"        xcskin:radius="150dp" >        <RelativeLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:background="@drawable/composer_button" >            <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_centerInParent="true"                android:src="@drawable/composer_icn_plus" />        </RelativeLayout>        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_camera"                android:tag="camera" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_music"                android:tag="music" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_place"                android:tag="place" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_sleep"                android:tag="sleep" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_thought"                android:tag="thought" />        <ImageView                android:id="@+id/id_button"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:src="@drawable/composer_with"                android:tag="with" />    </com.xc.xcskin.view.XCArcMenuView>        </RelativeLayout>

2.卫星菜单类的实现

package com.xc.xcskin.view;import com.xc.xcskin.R;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.View.OnClickListener;import android.view.ViewGroup;import android.view.animation.AlphaAnimation;import android.view.animation.Animation;import android.view.animation.Animation.AnimationListener;import android.view.animation.AnimationSet;import android.view.animation.RotateAnimation;import android.view.animation.ScaleAnimation;import android.view.animation.TranslateAnimation;/** * 卫星式菜单View * @author caizhiming * */public class XCArcMenuView extends ViewGroup implements OnClickListener{    private static final int POS_LEFT_TOP = 0;    private static final int POS_LEFT_BOTTOM = 1;    private static final int POS_RIGHT_TOP = 2;    private static final int POS_RIGHT_BOTTOM = 3;        private Position mPosition = Position.RIGHT_BOTTOM;    private int mRadius;    private Status mStatus = Status.CLOSE;    //主菜的单按钮    private View mCButton;    private OnMenuItemClickListener mOnMenuItemClickListener;    /**     * 菜单的状态枚举类     * @author caizhiming     *     */    public enum Status{        OPEN,CLOSE    }    /**     * 菜单的位置枚举类     * @author caizhiming     *     */    public enum Position{        LEFT_TOP,LEFT_BOTTOM,        RIGHT_TOP,RIGHT_BOTTOM    }    /**     * 点击子菜单项的回调接口     * @author caizhiming     *     */    public interface OnMenuItemClickListener {        void onClick(View view, int pos);    }    public void setOnMenuItemClickListener(            OnMenuItemClickListener onMenuItemClickListener) {        this.mOnMenuItemClickListener = onMenuItemClickListener;    }        public XCArcMenuView(Context context) {        this(context, null);        // TODO Auto-generated constructor stub    }    public XCArcMenuView(Context context, AttributeSet attrs) {        this(context, attrs, 0);        // TODO Auto-generated constructor stub    }    public XCArcMenuView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        // TODO Auto-generated constructor stub        //获取自定义属性        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,                R.styleable.XCArcMenuView,defStyle,0);        int pos = a.getInt(R.styleable.XCArcMenuView_position  , POS_RIGHT_BOTTOM);        switch (pos) {            case POS_LEFT_TOP:                mPosition = Position.LEFT_TOP;                break;            case POS_LEFT_BOTTOM:                mPosition = Position.LEFT_BOTTOM;                break;            case POS_RIGHT_TOP:                mPosition = Position.RIGHT_TOP;                break;            case POS_RIGHT_BOTTOM:                mPosition = Position.RIGHT_BOTTOM;                break;        }        mRadius = (int) a.getDimension(R.styleable.XCArcMenuView_radius,                 (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150,                 getResources().getDisplayMetrics()));        Log.v("czm", "mPosition = " + mPosition + ",mRadius = "+mRadius);        a.recycle();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // TODO Auto-generated method stub        int count = getChildCount();        for(int i = 0; i < count; i ++){            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);        }        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        // TODO Auto-generated method stub        if(changed){            layoutCButton();            layoutMenuItems();        }    }       /**     * 布局主菜单项     */    private void layoutCButton() {        // TODO Auto-generated method stub        mCButton = getChildAt(0);        mCButton.setOnClickListener(this);        int l = 0;        int t = 0;        int width = mCButton.getMeasuredWidth();        int height = mCButton.getMeasuredHeight();        switch (mPosition) {            case LEFT_TOP:                l = 0;                t = 0;                break;             case LEFT_BOTTOM:                l = 0;                t = getMeasuredHeight() - height;                break;            case RIGHT_TOP:                l = getMeasuredWidth() - width;                t = 0;                break;            case RIGHT_BOTTOM:                l = getMeasuredWidth() - width;                t = getMeasuredHeight() - height;                break;            default:                break;        }        mCButton.layout(l, t, l + width, t + height);    }    /**     * 布局菜单项     */    private void layoutMenuItems() {        // TODO Auto-generated method stub        int count = getChildCount();        for (int i = 0; i < count - 1; i++) {            View child = getChildAt(i + 1);            int l = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));            int t = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i));            int width = child.getMeasuredWidth();            int height = child.getMeasuredHeight();            // 如果菜单位置在底部 左下,右下            if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM) {                t = getMeasuredHeight() - height - t;            }            // 右上,右下            if (mPosition == Position.RIGHT_TOP || mPosition == Position.RIGHT_BOTTOM) {                l = getMeasuredWidth() - width - l;            }            child.layout(l, t, l + width, t + height);            child.setVisibility(View.GONE);        }    }    @Override    public void onClick(View v) {        // TODO Auto-generated method stub        mCButton = findViewById(R.id.id_button);        rotateCButton(v,0,360,300);        toggleMenu(300);    }    /**     * 切换菜单     */    public void toggleMenu(int duration) {        // TODO Auto-generated method stub     // 为menuItem添加平移动画和旋转动画        int count = getChildCount();        for (int i = 0; i < count - 1; i++)        {            final View childView = getChildAt(i + 1);            childView.setVisibility(View.VISIBLE);            // end 0 , 0            // start            int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));            int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i));            int xflag = 1;            int yflag = 1;            if (mPosition == Position.LEFT_TOP                    || mPosition == Position.LEFT_BOTTOM)            {                xflag = -1;            }            if (mPosition == Position.LEFT_TOP                    || mPosition == Position.RIGHT_TOP)            {                yflag = -1;            }            AnimationSet animset = new AnimationSet(true);            Animation tranAnim = null;            // to open            if (mStatus == Status.CLOSE)            {                tranAnim = new TranslateAnimation(xflag * cl, 0, yflag * ct, 0);                childView.setClickable(true);                childView.setFocusable(true);            } else            // to close            {                tranAnim = new TranslateAnimation(0, xflag * cl, 0, yflag * ct);                childView.setClickable(false);                childView.setFocusable(false);            }            tranAnim.setFillAfter(true);            tranAnim.setDuration(duration);            tranAnim.setStartOffset((i * 100) / count);            tranAnim.setAnimationListener(new AnimationListener()            {                @Override                public void onAnimationStart(Animation animation)                {                }                @Override                public void onAnimationRepeat(Animation animation)                {                }                @Override                public void onAnimationEnd(Animation animation)                {                    if (mStatus == Status.CLOSE)                    {                        childView.setVisibility(View.GONE);                    }                }            });            // 旋转动画            RotateAnimation rotateAnim = new RotateAnimation(0, 720,                    Animation.RELATIVE_TO_SELF, 0.5f,                    Animation.RELATIVE_TO_SELF, 0.5f);            rotateAnim.setDuration(duration);            rotateAnim.setFillAfter(true);            animset.addAnimation(rotateAnim);            animset.addAnimation(tranAnim);            childView.startAnimation(animset);            final int pos = i + 1;            childView.setOnClickListener(new OnClickListener()            {                @Override                public void onClick(View v)                {                    if (mOnMenuItemClickListener != null)                        mOnMenuItemClickListener.onClick(childView, pos);                    menuItemAnim(pos - 1);                    changeStatus();                }            });        }        // 切换菜单状态        changeStatus();            }    /**     * 选择主菜单按钮     *      */    private void rotateCButton(View v, float start, float end, int duration) {        // TODO Auto-generated method stub        RotateAnimation anim = new RotateAnimation(start, end,                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,                0.5f);        anim.setDuration(duration);        anim.setFillAfter(true);        v.startAnimation(anim);    }    /**     * 添加menuItem的点击动画     *      */    private void menuItemAnim(int pos)    {        for (int i = 0; i < getChildCount() - 1; i++)        {            View childView = getChildAt(i + 1);            if (i == pos)            {                childView.startAnimation(scaleBigAnim(300));            } else            {                childView.startAnimation(scaleSmallAnim(300));            }            childView.setClickable(false);            childView.setFocusable(false);        }    }    /**     * 为当前点击的Item设置变小和透明度增大的动画     * @param duration     * @return     */    private Animation scaleSmallAnim(int duration)    {        AnimationSet animationSet = new AnimationSet(true);        ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f,                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,                0.5f);        AlphaAnimation alphaAnim = new AlphaAnimation(1f, 0.0f);        animationSet.addAnimation(scaleAnim);        animationSet.addAnimation(alphaAnim);        animationSet.setDuration(duration);        animationSet.setFillAfter(true);        return animationSet;    }    /**     * 为当前点击的Item设置变大和透明度降低的动画     */    private Animation scaleBigAnim(int duration)    {        AnimationSet animationSet = new AnimationSet(true);        ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f,                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,                0.5f);        AlphaAnimation alphaAnim = new AlphaAnimation(1f, 0.0f);        animationSet.addAnimation(scaleAnim);        animationSet.addAnimation(alphaAnim);        animationSet.setDuration(duration);        animationSet.setFillAfter(true);        return animationSet;    }    /**     * 切换菜单状态     */    private void changeStatus()    {        mStatus = (mStatus == Status.CLOSE ? Status.OPEN                : Status.CLOSE);    }    /**     * 是否处于展开状态     * @return     */    public boolean isOpen()    {        return mStatus == Status.OPEN;    }        }

3.使用卫星式菜单类

package com.xc.xcskin;import android.app.Activity;import android.os.Bundle;import android.view.View;import android.widget.Toast;import com.xc.xcskin.view.XCArcMenuView;import com.xc.xcskin.view.XCArcMenuView.OnMenuItemClickListener;import com.xc.xcskin.view.XCGuaguakaView;import com.xc.xcskin.view.XCGuaguakaView.OnCompleteListener;/** * 使用并测试自定义卫星式菜单View * @author caizhiming * */public class XCArcMenuViewDemo extends Activity{    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.xc_arcmenu_view_demo);        XCArcMenuView view = (XCArcMenuView) findViewById(R.id.arcmenu);        view.setOnMenuItemClickListener(new OnMenuItemClickListener() {                        @Override            public void onClick(View view, int pos) {                // TODO Auto-generated method stub                String tag = (String) view.getTag();                Toast.makeText(XCArcMenuViewDemo.this, tag, Toast.LENGTH_SHORT).show();            }        });    }}

三、源码下载

源码下载:http://download.csdn.net/detail/jczmdeveloper/8561749

真题园网:http://www.zhentiyuan.com

更多相关文章

  1. Android(安卓)动画各种实现,包括帧动画、补间动画和属性动画的总
  2. Android(安卓)Layout 布局
  3. android中-使用2D动画 — 图形处理(Canvas基础)
  4. Android(安卓)属性动画(一)
  5. Android(Xamarin)之旅(三)
  6. Android属性动画上手实现各种动画效果,自定义动画,抛物线等
  7. 2013年01月06日
  8. Android(安卓)主流屏幕以及适配
  9. Android(安卓)实例剖析之 notepad

随机推荐

  1. Android开发实战使用(VR技术实现360°全景
  2. DEX 方法超过64K限制和gradle编译OOM问题
  3. Android(安卓)SDK no swt-pi-gtk-3550 or
  4. 菜鸟Android(1)
  5. Java(Android)数据结构汇总(二)-- Set(下)
  6. 学习笔记:Android开源库使用方法总结
  7. android opengl 渲染的3D色子
  8. Android引路蜂地图开发示例:本地查询
  9. android ant打包注意
  10. Android实现瀑布流,圆角矩形,点按效果