关闭

自定义卫星菜单CustomArcMenu

标签: ArcMenu
407人阅读 评论(0) 收藏 举报
分类:

现在这种效果的开源项目,确实蛮多的。SatelliteMenu,ARCMenu都是。

下面是我自己仿照他们写的:

先交代一下具体步骤:

自定义卫星菜单:

1,自定义属性 

    a,自定义attr属性 

    b,xml布局文件中引用

    c,CustomArcMenu中获取自定义属性值

2,onMesure测量自控件的大小 

3,onLayout 布置自控件。

4,添加点击事件,添加主按钮与子菜单的按钮动画效果 

    a,主按钮的旋转动画

    b,子菜单的旋转和平移动画 

 5,添加子菜单的点击事件

展示效果图:点击主按钮,子按钮弹射出去并且呈弧形展示,再次点击主按钮,子菜单按钮返回主按钮位置并消失,点击子菜单按钮,被点击子菜单按钮放大并消失

其他子菜单按钮缩小消失。下图是点击展开后的效果。



下面是具体的代码实现:

1,自定义属性 

    a,自定义attr属性 

 

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

    b,xml布局文件中引用

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:wang="http://schemas.android.com/apk/res/com.wang.demo_android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <ListView 
        android:id="@+id/lv_content"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"></ListView>
    
    <com.wang.demo_android.view.CustomArcMenu
        android:id="@+id/custom_arcmenu"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        wang:radius="140dp"
        wang:position="left_bottom">
        
        <ImageView 
            android:id="@+id/arc_main"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:padding="5dp"
            android:src="@drawable/main"/>
        
        <ImageView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:tag="search"
            android:src="@drawable/ic_search"/>
                
        <ImageView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:tag="camera"
            android:src="@drawable/ic_camera"/>
                        
        <ImageView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:tag="notifaction"
            android:src="@drawable/ic_notifaction"/>
                                
        <ImageView 
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:tag="music"
            android:src="@drawable/ic_music"/>
    </com.wang.demo_android.view.CustomArcMenu> 
    
</FrameLayout>

    c,CustomArcMenu中获取自定义属性值

    public CustomArcMenu(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        TypedArray array = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.CustomArcMenu, 0, defStyleRes);
        int position = array.getInt(R.styleable.CustomArcMenu_position, POS_RIGHTT_BOTTOM);
        switch (position) {
            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_RIGHTT_BOTTOM:
                mPosition = POSITION.RIGHT_BOTTOM;
                break;
        }
        // 设置半径的默认值:100dp
        mRadius = (int) array.getDimension(R.styleable.CustomArcMenu_radius, (int) TypedValue
                .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources()
                        .getDisplayMetrics()));

        Log.i(tag, "positon:" + mPosition + " radius:" + mRadius);

        array.recycle();// 回收

    }

2,onMesure测量自控件的大小 

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

3,onLayout 布置自控件。

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        if (changed) {
            // 定位主菜单按钮
            layoutMainButton();
            // 定位子菜单按钮
            layoutMenuItem();

        }

    }

    /**
     * 定位子菜单按钮
     */
    private void layoutMenuItem() {
        int count = getChildCount();
        for (int i = 0; i < count - 1; i++) {
            View child = getChildAt(i + 1);
            int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));
            int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i));
            // 右上和右下位置
            if (mPosition == POSITION.RIGHT_TOP || mPosition == POSITION.RIGHT_BOTTOM) {
                cl = getMeasuredWidth() - cl - child.getMeasuredWidth();
            }
            // 左下和右下位置
            if (mPosition == POSITION.LEFT_BOTTOM || mPosition == POSITION.RIGHT_BOTTOM) {
                ct = getMeasuredHeight() - ct - child.getMeasuredHeight();
            }

            child.layout(cl, ct, cl + child.getMeasuredWidth(), ct + child.getMeasuredHeight());
            child.setVisibility(View.GONE);
        }

    }

    /**
     * 定位主菜单按钮
     * 
     * @param left
     * @param top
     * @param right
     * @param bottom
     */
    private void layoutMainButton() {
        mMainButton = getChildAt(0);
        if (mMainButton == null) {
            return;
        }
        mMainButton.setOnClickListener(this);
        int l = 0;
        int t = 0;
        int width = mMainButton.getMeasuredWidth();
        int height = mMainButton.getMeasuredHeight();
        if (mPosition == POSITION.LEFT_BOTTOM || mPosition == POSITION.RIGHT_BOTTOM) {
            t = getMeasuredHeight() - height;
        }
        if (mPosition == POSITION.RIGHT_TOP || mPosition == POSITION.RIGHT_BOTTOM) {
            l = getMeasuredWidth() - width;
        }
        mMainButton.layout(l, t, l + width, t + height);
    }

4,添加点击事件,添加主按钮与子菜单的按钮动画效果 

    a,主按钮的旋转动画

    @Override
    public void onClick(View v) {
        rotateMainButton(v, 0f, 360f, 300);
        toggleMenu(300);
    }

    /**
     * 启动主按钮的动画效果
     */
    private void rotateMainButton(View v, float start, float end, int duration) {
        RotateAnimation rotateAnim = new RotateAnimation(start, end, Animation.RELATIVE_TO_SELF,
                0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotateAnim.setDuration(duration);
        rotateAnim.setFillAfter(true);
        v.startAnimation(rotateAnim);
    }

    b,子菜单的旋转和平移动画 

    /**
     * 启动子菜单的动画
     */
    public void toggleMenu(int duration) {
        int count = getChildCount();
        for (int i = 0; i < count - 1; i++) {
            final View child = getChildAt(i + 1);
            child.setVisibility(View.VISIBLE);

            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);
            // 平移动画
            TranslateAnimation translateAnim = null;
            if (mCurrentStatus == STATUS.CLOSE) {
                translateAnim = new TranslateAnimation(xFlag * cl, 0, yFlag * ct, 0);
                child.setClickable(true);
                child.setFocusable(true);
            } else {
                translateAnim = new TranslateAnimation(0, xFlag * cl, 0, yFlag * ct);
                child.setClickable(false);
                child.setFocusable(false);
            }
            translateAnim.setDuration(duration);
            translateAnim.setFillAfter(true);
            // 使得动画具有较明显的先后循序
            translateAnim.setStartOffset(i * 100 / count);
            translateAnim.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) {
                        child.setVisibility(View.GONE);
                    }
                }
            });
            // 旋转动画
            RotateAnimation rotateAnim = new RotateAnimation(0, 720f, Animation.RELATIVE_TO_SELF,
                    0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            rotateAnim.setDuration(duration);
            rotateAnim.setFillAfter(true);

            animSet.addAnimation(rotateAnim);
            animSet.addAnimation(translateAnim);
            // 开始动画
            child.startAnimation(animSet);

            final int pos = i + 1;
            child.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {
                    if (mOnMenuItemClickListener != null) {
                        mOnMenuItemClickListener.OnClick(v, pos);
                    }
                    // 子菜单按钮的点击事件
                    menuItemAnim(pos);
                    // 改变状态
                    changeStatus();
                }
            });
        }
        changeStatus();
    }

 5,添加子菜单的点击事件

    /**
     * 添加item的点击动画
     * 
     * @param pos
     */
    private void menuItemAnim(int pos) {
        int count = getChildCount();
        for (int i = 0; i < count-1; i++) {
            View child = getChildAt(i + 1);
            if ((i + 1) == pos) {
                child.startAnimation(getScaleBig(300));
            } else {
                child.startAnimation(getScaleSmall(300));
            }
            
            child.setClickable(false);
            child.setFocusable(false);
        }
    }

    /**
     * 返回一个缩小的动画
     * 
     * @return
     */
    private Animation getScaleSmall(int duration) {
        AnimationSet animSet = new AnimationSet(true);
        ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 0f, 1.0f, 0, Animation.RELATIVE_TO_SELF,
                0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        scaleAnim.setDuration(duration);
        scaleAnim.setFillAfter(true);
        
        AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0f);
        alphaAnim.setDuration(duration);
        alphaAnim.setFillAfter(true);
        
        animSet.addAnimation(alphaAnim);
        animSet.addAnimation(scaleAnim);
        animSet.setFillAfter(true);
        return animSet;
    }

    private Animation getScaleBig(int duration) {
        AnimationSet animSet = 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);
        scaleAnim.setDuration(duration);
        //scaleAnim.setFillAfter(true);
        
        AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0f);
        alphaAnim.setDuration(duration);
        //alphaAnim.setFillAfter(true);
        
        animSet.addAnimation(alphaAnim);
        animSet.addAnimation(scaleAnim);
        animSet.setFillAfter(true);
        return animSet;
    }

    /**
     * 改变当前的状态
     */
    private void changeStatus() {
        mCurrentStatus = mCurrentStatus == STATUS.CLOSE ? STATUS.OPEN : STATUS.CLOSE;
    }
注意item个数一定要大于等于三个,否则会有异常

类的下载






0
0
查看评论

安卓自定义组件之------->卫星菜单

直接步入正题:卫星菜单 ArcMenu 相信大家接触安卓,从新手到入门的过渡,就应该会了解到卫星菜单、抽屉、Xutils、Coolmenu、一些大神封装好的一些组件。这些组件在 Github 上面很容易搜得到,但是有时候打开会发现看不懂里面的代码,包括一些方法和函数 。。。。。 ...
  • l540675759
  • l540675759
  • 2016-03-26 16:37
  • 1080

Android 实现自定义的卫星式菜单

Android 实现卫星式菜单也叫弧形菜单的主要要做的工作如下:1.动画的处理2.自定义ViewGroup来实现卫星式菜单View(1)自定义属性a. 在attrs.xml中定义属性b. 在布局中使用自定义属性c. 在自定义View中读取布局文件中的自定义属性(2)onMeasure 测量 chil...
  • Daisy_ruo
  • Daisy_ruo
  • 2016-04-11 19:05
  • 758

Android动画之属性动画Animator详解(卫星菜单)

今天详细看了一下Android属性动画,然后做了一个网上比较火的卫星菜单的demo,下面介绍一下卫星菜单的制作方法
  • jl_stone
  • jl_stone
  • 2015-09-29 14:27
  • 684

android之类似卫星菜单,来自定义ViewGroup。。。。。

这也是我学自定义控件以来,自己写的唯一一个继承ViewGroup的自定义view,对于高手来说比较简单,但对于我们这样的小白,确实是一个不错的demo。定义继承ViewGroup的自定义View  ,需要实现它的两个基本方法, onMeasure和onLayout,我们在onMeasure...
  • qq_21840193
  • qq_21840193
  • 2015-08-08 15:47
  • 439

android卫星菜单的简单实现

效果图: 1、准备图片 2、添加属性动画原理介绍: 刚开始是8张图片重叠放在一起,点击最上面的图片之后,其它7张图片移动到对应的位置。这个位置的计算是通过每个子菜单对应的弧度计算的,通过它的弧度计算出每个子菜单移动后的x坐标和y坐标。 如图: 布局文件:<?xml version=&...
  • baidu_31093133
  • baidu_31093133
  • 2016-05-02 22:38
  • 2070

利用FloatingActionButton+ValueAnimator 完成卫星菜单效果

卫星菜单其实已经很常见了,网上也有很多教程甚至都有开源的控件了。本篇文章是讲利用FloatingActionButton+ValueAnimator 完成卫星菜单效果,仅为交流
  • github_37847975
  • github_37847975
  • 2017-07-31 16:34
  • 473

自定义旋转卫星菜单

经常在应用中看到卫星菜单,自己也学着写了一个继承自ViewGroup的CustomMenu的卫星菜单,不同之处是带了旋转,由于使用了属性动画,所以只支持3.0以上,还有就是界面变的难看了,囧~,上图(gif录制不流畅,见谅啊): 1. 自定义属性:为了偷懒,只定义两了两个属性,分别表示子菜单的大小...
  • yifei1989
  • yifei1989
  • 2015-04-04 22:11
  • 1070

基于Android的卫星菜单效果实现

备注说明:本文主要是笔者通过之前“卫星菜单实现”的学习,特意将实现的思路给理出来、记录下来,作为自己的学习总结。文中存在不对之处,请大家踊跃指出,转载请标明出处。卫星菜单 实现思路: 1、自定义ViewGroup (1)自定义属性:a、创建attr.xml文件 b、在布局中使用 c、...
  • w493549442
  • w493549442
  • 2015-10-06 20:02
  • 1374

自定义ViewGroup之卫星菜单

知识点 一、动画二、自定义ViewGroup 1、自定义属性 a、attrs.xml b、在布局文件中使用 c、在自定义控件种进行读取 2、onMeasure() 3、onLayout() 4、设置主Button旋转动画 5、为menuItem添加平移动画和旋转动画效果 ArcM...
  • u010542146
  • u010542146
  • 2016-02-28 16:07
  • 213

浅谈属性动画简单使用之实现卫星菜单(二)

大家对于卫星菜单应该都不陌生了,但是很多人
  • u013064109
  • u013064109
  • 2016-03-28 01:31
  • 1674
    个人资料
    • 访问:36146次
    • 积分:1310
    • 等级:
    • 排名:千里之外
    • 原创:98篇
    • 转载:5篇
    • 译文:0篇
    • 评论:4条
    最新评论