第4例:卫星菜单

核心思想知识点:
1)、自定义view的流程

效果图如下

GIF.gif

功能实现过程

1、strings.xml

<resources>
    <string name="app_name">卫星菜单</string>
    <string name="text_btn1">子按钮1</string>
    <string name="text_btn2">子按钮2</string>
    <string name="text_btn3">子按钮3</string>
    <string name="text_btn4">子按钮4</string>
    <string name="text_btn5">子按钮5</string>
</resources>

2、attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!--半径属性-->
    <attr name="radius" format="dimension" />
    <!--自定义控件属性-->
    <declare-styleable name="ArcMenu">
        <!--<attr name="position" />-->
        <attr name="radius" />
    </declare-styleable>

</resources>

3、rotatembutton.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:fromDegrees="0"
        android:toDegrees="359"
        android:duration="500"
        android:repeatCount="-1"
        android:pivotX="50%"
        android:pivotY="50%" />
</set>

4、SatelliteMenu .java(自定义控件的核心部分)

public class SatelliteMenu extends ViewGroup {

    private static final String TAG = SatelliteMenu.class.getSimpleName();

    private int pm_width;       //定义屏幕的宽度
    private int pm_height;      //定义屏幕的高度

    //定义菜单半径
    private int mRadius;
    //默认为关闭状态
    private Status mCurrentStatus = Status.CLOSE;
    //触发菜单的按钮
    private View mButton;
    //子菜单单击事件
    private OnSatelliteMenuItemClickListener mMenuItemClickListener;
    //枚举类 菜单状态
    public enum Status
    {
        OPEN, CLOSE
    }
    /**
     * 单击子菜单的回调接口
     */
    public interface OnSatelliteMenuItemClickListener
    {
        void onClick(View view, int pos);
    }

    //构造方法
    public SatelliteMenu(Context context) {
        this(context,null);
    }

    public SatelliteMenu(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public SatelliteMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //        获取屏幕宽高
        WindowManager wm = (WindowManager) getContext()
                .getSystemService(Context.WINDOW_SERVICE);
        pm_width = wm.getDefaultDisplay().getWidth();
        pm_height = wm.getDefaultDisplay().getHeight();

        // 获取自定义属性的值
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.ArcMenu, defStyleAttr, 0);
        //获取按钮半径属性值
        mRadius = (int) a.getDimension(R.styleable.ArcMenu_radius, TypedValue
                .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100,
                        getResources().getDisplayMetrics()));
        Log.i(TAG,"mRadius============="+mRadius);

        a.recycle();        //回收资源
    }
    /**
     *测量自定义控件中所有子控件的尺寸
     */
    @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);
    }

    /**
     *设置按钮的位置
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (changed) {                                    //如果按钮布局发生改变
            buttonLayout();                               //调用主按钮布局位置的方法
            childLayout();

            }
        }

    private void childLayout() {
        int count = getChildCount();                  //获取所有的子按钮
        for (int i = 0; i < count - 1; i++) {         //便利所有按钮
            View child = getChildAt(i + 1);            //获取子控件下标
            child.setVisibility(View.GONE);           //隐藏子按钮

            //获取子按钮的宽高尺寸
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            //==================中=================
            //           中心坐标(pm_width/2-childWidth/2,getMeasuredHeight() - height)
            int c_x = pm_width / 2 - childWidth / 2;
            int c_y = getMeasuredHeight() - childHeight/2;

            //子按钮与父容器左边距
//            Math.sin:返回指定double类型参数的正弦值
            int childLeft = (int) (mRadius * Math.sin(Math.PI / (count - 2)
                    * i));
            int childTop = (int) (mRadius * Math.sin(Math.PI / (count - 2)
                    * i));
            childTop = getMeasuredHeight() - childHeight - childTop;
            if (i == 0) {
                childLeft = c_x - mRadius;
                childTop = getMeasuredHeight() - childHeight;
            } else if (i == 1) {
                childLeft = c_x  - childLeft;
            } else if (i == 2) {
                childLeft = c_x;
                childTop=getMeasuredHeight() - childHeight-mRadius;
            } else if (i == 3) {
                childLeft = c_x  + childLeft;

            } else if (i == 4) {
                childLeft = c_x + mRadius;
                childTop = getMeasuredHeight() - childHeight;
            }
            //设置子按钮的位置
            child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
        }
    }


    /**
     *收缩子按钮动画
     */
    public void shrinkMenu(int time) {
        int count = getChildCount();                    //获取所有的子按钮
        for (int i = 0; i < count - 1; i++)             //便利所有按钮
        {
            final View childView = getChildAt(i + 1);   //获取子按钮下标
            childView.setVisibility(View.VISIBLE);      //显示子按钮
            //==================中=================
                //动画开始与结束的位置x,y
             int clx = (int) (mRadius * Math.sin(Math.PI / (count - 2) * i));
             int cty = (int) (mRadius * Math.cos(Math.PI  / (count - 2) * i));
                if (i == 0) {       //第一个子按钮
                    clx = mRadius;
                    cty = 0;
                } else if (i == 1) {    //第二个子按钮

                } else if (i == 2) {    //第三个子按钮
                    clx = 0;
                    cty = mRadius;
                } else if (i == 3) {    //第四个子按钮
                    clx=(int) (mRadius * Math.cos(Math.PI  / (count - 2) * i));
                    cty=-cty;
                } else if (i == 4) {    //第五个子按钮
                    clx = - mRadius;
                    cty = 0;
                }

            //设置动画集合
            AnimationSet animset = new AnimationSet(true);
            Animation tranAnim = null;

            //如果当前按钮菜单为关闭状态
            if (mCurrentStatus == Status.CLOSE)
            {   //设置打开按钮菜单动画
                tranAnim = new TranslateAnimation(clx,0 , cty, 0);
                childView.setClickable(true);       //子按钮可以单击
                childView.setFocusable(true);       //子按钮可调焦
            } else//如果当前按钮菜单为开启状态
            {   //设置关闭按钮菜单动画
                tranAnim = new TranslateAnimation(0, clx, 0, cty);
                childView.setClickable(false);      //子按钮不可点击
                childView.setFocusable(false);      //子按钮不可调焦
            }
            tranAnim.setDuration(time);             //  动画显示的时间
            tranAnim.setFillAfter(true);            //停止最后一帧的位置
            tranAnim.setStartOffset((i * 100) / count);
            //设置动画监听器
            tranAnim.setAnimationListener(new Animation.AnimationListener()
            {

                @Override
                public void onAnimationStart(Animation animation)
                {

                }

                @Override
                public void onAnimationRepeat(Animation animation)
                {

                }

                /**
                 *动画结束后隐藏子按钮
                 */
                @Override
                public void onAnimationEnd(Animation animation)
                {
                    if (mCurrentStatus == 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(time);       //设置旋转的时间
            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 (mMenuItemClickListener != null)
                        mMenuItemClickListener.onClick(childView, pos);
                    childButtonClickAnim(pos - 1);      //调用字按钮单击动画方法
                    menuStatus();                       //调用菜单状态方法
                }
            });
        }
        // 切换菜单状态
        menuStatus();

    }

    /**
     * 子按钮单击动画的方法
     */
    private void childButtonClickAnim(int pos) {
        for (int i = 0; i < getChildCount() - 1; i++)
        {

            View childView = getChildAt(i + 1);
            if (i == pos)       //当前单击的子按钮
            {   //启动子按钮变大并消失动画
                childView.startAnimation(scaleBigAnim(200));
            } else
            {
                //启动其它没有被单击的按钮变小并消失动画
                childView.startAnimation(scaleSmallAnim(200));
            }

            childView.setClickable(false);  //子按钮不可点击
            childView.setFocusable(false);  //子按钮不可调焦

        }
    }

    /**
     *没有被单击的子按钮,变小并消失的动画方法
     */
    private Animation scaleSmallAnim(int time)
    {   //设置动画集合
        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(time);         //设置动画时间
        animationSet.setFillAfter(true);        //停止最后一帧的位置
        return animationSet;

    }

    /**
     * 单击子按钮,当前子按钮变大并消失动画方法
     */
    private Animation scaleBigAnim(int time)
    {   //设置动画集合
        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(time);         //设置动画时间
        animationSet.setFillAfter(true);        //停止最后一帧的位置
        return animationSet;

    }

    /**
     * 切换菜单状态
     */
    private void menuStatus()
    {
        mCurrentStatus = (mCurrentStatus == Status.CLOSE ? Status.OPEN
                : Status.CLOSE);
    }
    /**
     *设置主按钮旋转动画
     */
    private void rotatemButton(View v, float start, float end, int time) {
        //中心旋转动画
        RotateAnimation anim = new RotateAnimation(start, end,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        anim.setDuration(time);         //设置旋转的时间
        anim.setFillAfter(true);        //停止最后一帧的位置
        v.startAnimation(anim);         //开始动画
    }

    /**
     * 设置主按钮在布局中的位置
     */
    private void buttonLayout() {
        mButton = getChildAt(0);       //获取菜单主按钮
        //设置按钮的单击事件
        mButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                rotatemButton(v, 0f, 360f, 500);    //调用主按钮旋转的方法
                shrinkMenu(500);                    //调用收缩子按钮动画方法
            }
        });
        //初始化按钮左边距与上边距
        int l = 0;
        int t = 0;
        //获取主按钮的宽度与高度
        int width = mButton.getMeasuredWidth();
        int height = mButton.getMeasuredHeight();
        //判断主按钮位置
            //==================中=================
                l = pm_width/2-width/2;
                t = getMeasuredHeight() - height;
        mButton.layout(l, t, l + width, t + width);  //设置主按钮在父容器的位置
    }
}

5、activity_main.xml(引入自定义控件)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:menu="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/bg"
   >
<com.example.satellitemenu.SatelliteMenu
    android:id="@+id/satelliteMenu"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    menu:radius="100dp">
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/button_bg" >

        <ImageView
            android:id="@+id/id_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:src="@drawable/button_in" />

    </RelativeLayout>

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/img5"
        android:tag="@string/text_btn1" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/img4"
        android:tag="@string/text_btn2" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/img3"
        android:tag="@string/text_btn3" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/img2"
        android:tag="@string/text_btn4" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/img1"
        android:tag="@string/text_btn5" />
</com.example.satellitemenu.SatelliteMenu>
</RelativeLayout>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,070评论 25 707
  • 转载自:http://blog.csdn.net/vipzjyno1/article/details/252480...
    HEXG_阅读 5,830评论 0 2
  • 001.表达简洁 说话要简洁。经常有意识的去听自己所说的话语,有没有复杂的,不必要的词语。多不是详细,反而是累赘,...
    羽上惊鸿阅读 325评论 4 6
  • 01 最近喜欢看老师们写的情感故事,但结局都是不圆满的,看了心里特不爽。 所以就跟老师说,后学也写,请一定要看。 ...
    袁袁_45fc阅读 542评论 2 1
  • 看了勺子写的『酥油』读后感,各种感触。为江觉迟的执着与守护,也为勺子单纯的分享和昭昭大爱。感动。 刚才看了...
    笑尘005阅读 206评论 0 0