Android 自定义ViewGroup实现弧形菜单

1、直接继承ViewGroup类,onMeasure测量子控件,onLayout第一个子控件放在边角,其他的子控件根据弧度计算其位置来进行layout。

2、动画效果:第一个按钮的旋转动画 RotateAnimation,其他的控件TranslateAnimation和RotateAnimation两个动画,点击ScaleAnimation动画和AlphaAnimation动画 ,设置好延迟的时间间隔就可以看到效果了。

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.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.AlphaAnimation;
import android.view.animation.AnimationSet;
import android.view.animation.OvershootInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;

/**
 * @author zhy
 */
public class ArcMenu extends ViewGroup implements OnClickListener
{

    private static final String TAG = "ArcMenu";
    /**
     * 菜单的显示位置
     */
    private Position mPosition = Position.LEFT_TOP;

    /**
     * 菜单显示的半径,默认100dp
     */
    private int mRadius = 100;
    /**
     * 用户点击的按钮
     */
    private View mButton;
    /**
     * 当前ArcMenu的状态
     */
    private Status mCurrentStatus = Status.CLOSE;
    /**
     * 回调接口
     */
    private OnMenuItemClickListener onMenuItemClickListener;

    /**
     * 状态的枚举类
     *
     * @author zhy
     *
     */
    public enum Status
    {
        OPEN, CLOSE
    }

    /**
     * 设置菜单现实的位置,四选1,默认右下
     *
     * @author zhy
     */
    public enum Position
    {
        LEFT_TOP, RIGHT_TOP, RIGHT_BOTTOM, LEFT_BOTTOM;
    }

    public interface OnMenuItemClickListener
    {
        void onClick(View view, int pos);
    }

    public ArcMenu(Context context)
    {
        this(context, null);
    }

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

    }

    /**
     * 初始化属性
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public ArcMenu(Context context, AttributeSet attrs, int defStyle)
    {

        super(context, attrs, defStyle);
        // dp convert to px
        mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                mRadius, getResources().getDisplayMetrics());
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.ArcMenu, defStyle, 0);

        int n = a.getIndexCount();
        for (int i = 0; i < n; i++)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
            case R.styleable.ArcMenu_position:
                int val = a.getInt(attr, 0);
                switch (val)
                {
                case 0:
                    mPosition = Position.LEFT_TOP;
                    break;
                case 1:
                    mPosition = Position.RIGHT_TOP;
                    break;
                case 2:
                    mPosition = Position.RIGHT_BOTTOM;
                    break;
                case 3:
                    mPosition = Position.LEFT_BOTTOM;
                    break;
                }
                break;
            case R.styleable.ArcMenu_radius:
                // dp convert to px
                mRadius = a.getDimensionPixelSize(attr, (int) TypedValue
                        .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100f,
                                getResources().getDisplayMetrics()));
                break;

            }
        }
        a.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        int count = getChildCount();
        for (int i = 0; i < count; i++)
        {
            // mesure child
            getChildAt(i).measure(MeasureSpec.UNSPECIFIED,
                    MeasureSpec.UNSPECIFIED);
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        if (changed)
        {

            layoutButton();
            int count = getChildCount();
            /**
             * 设置所有孩子的位置 例如(第一个为按钮): 左上时,从左到右 ] 第2个:mRadius(sin0 , cos0)
             * 第3个:mRadius(sina ,cosa) 注:[a = Math.PI / 2 * (cCount - 1)]
             * 第4个:mRadius(sin2a ,cos2a) 第5个:mRadius(sin3a , cos3a) ...
             */
            for (int i = 0; i < count - 1; i++)
            {
                View child = getChildAt(i + 1);
                child.setVisibility(View.GONE);

                int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2)
                        * i));
                int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2)
                        * i));
                // childview width
                int cWidth = child.getMeasuredWidth();
                // childview height
                int cHeight = child.getMeasuredHeight();

                // 右上,右下
                if (mPosition == Position.LEFT_BOTTOM
                        || mPosition == Position.RIGHT_BOTTOM)
                {
                    ct = getMeasuredHeight() - cHeight - ct;
                }
                // 右上,右下
                if (mPosition == Position.RIGHT_TOP
                        || mPosition == Position.RIGHT_BOTTOM)
                {
                    cl = getMeasuredWidth() - cWidth - cl;
                }

                Log.e(TAG, cl + " , " + ct);
                child.layout(cl, ct, cl + cWidth, ct + cHeight);

            }
        }
    }

    /**
     * 第一个子元素为按钮,为按钮布局且初始化点击事件
     */
    private void layoutButton()
    {
        View cButton = getChildAt(0);

        cButton.setOnClickListener(this);

        int l = 0;
        int t = 0;
        int width = cButton.getMeasuredWidth();
        int height = cButton.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;

        }
        Log.e(TAG, l + " , " + t + " , " + (l + width) + " , " + (t + height));
        cButton.layout(l, t, l + width, t + height);

    }

    /**
     * 为按钮添加点击事件
     */
    @Override
    public void onClick(View v)
    {
        mButton = findViewById(R.id.id_button);
        if (mButton == null)
        {
            mButton = getChildAt(0);
        }
        rotateView(mButton, 0f, 270f, 300);
        toggleMenu(300);
    }

    /**
     * 按钮的旋转动画
     *
     * @param view
     * @param fromDegrees
     * @param toDegrees
     * @param durationMillis
     */
    public static void rotateView(View view, float fromDegrees,
            float toDegrees, int durationMillis)
    {
        RotateAnimation rotate = new RotateAnimation(fromDegrees, toDegrees,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        rotate.setDuration(durationMillis);
        rotate.setFillAfter(true);
        view.startAnimation(rotate);
    }

    public void toggleMenu(int durationMillis)
    {
        int count = getChildCount();
        for (int i = 0; i < count - 1; i++)
        {
            final View childView = getChildAt(i + 1);
            childView.setVisibility(View.VISIBLE);

            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;

            // child left
            int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));
            // child top
            int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i));

            AnimationSet animset = new AnimationSet(true);
            Animation animation = null;
            if (mCurrentStatus == Status.CLOSE)
            {// to open
                animset.setInterpolator(new OvershootInterpolator(2F));
                animation = new TranslateAnimation(xflag * cl, 0, yflag * ct, 0);
                childView.setClickable(true);
                childView.setFocusable(true);
            } else
            {// to close
                animation = new TranslateAnimation(0f, xflag * cl, 0f, yflag
                        * ct);
                childView.setClickable(false);
                childView.setFocusable(false);
            }
            animation.setAnimationListener(new AnimationListener()
            {
                public void onAnimationStart(Animation animation)
                {
                }

                public void onAnimationRepeat(Animation animation)
                {
                }

                public void onAnimationEnd(Animation animation)
                {
                    if (mCurrentStatus == Status.CLOSE)
                        childView.setVisibility(View.GONE);

                }
            });

            animation.setFillAfter(true);
            animation.setDuration(durationMillis);
            // 为动画设置一个开始延迟时间,纯属好看,可以不设
            animation.setStartOffset((i * 100) / (count - 1));
            RotateAnimation rotate = new RotateAnimation(0, 720,
                    Animation.RELATIVE_TO_SELF, 0.5f,
                    Animation.RELATIVE_TO_SELF, 0.5f);
            rotate.setDuration(durationMillis);
            rotate.setFillAfter(true);
            animset.addAnimation(rotate);
            animset.addAnimation(animation);
            childView.startAnimation(animset);
            final int index = i + 1;
            childView.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    if (onMenuItemClickListener != null)
                        onMenuItemClickListener.onClick(childView, index - 1);
                    menuItemAnin(index - 1);
                    changeStatus();

                }
            });

        }
        changeStatus();
        Log.e(TAG, mCurrentStatus.name() +"");
    }

    private void changeStatus()
    {
        mCurrentStatus = (mCurrentStatus == Status.CLOSE ? Status.OPEN
                : Status.CLOSE);
    }
    /**
     * 开始菜单动画,点击的MenuItem放大消失,其他的缩小消失
     * @param item
     */
    private void menuItemAnin(int item)
    {
        for (int i = 0; i < getChildCount() - 1; i++)
        {
            View childView = getChildAt(i + 1);
            if (i == item)
            {
                childView.startAnimation(scaleBigAnim(300));
            } else
            {
                childView.startAnimation(scaleSmallAnim(300));
            }
            childView.setClickable(false);
            childView.setFocusable(false);

        }

    }

    /**
     * 缩小消失
     * @param durationMillis
     * @return
     */
    private Animation scaleSmallAnim(int durationMillis)
    {
        Animation anim = new ScaleAnimation(1.0f, 0f, 1.0f, 0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        anim.setDuration(durationMillis);
        anim.setFillAfter(true);
        return anim;
    }
    /**
     * 放大,透明度降低
     * @param durationMillis
     * @return
     */
    private Animation scaleBigAnim(int durationMillis)
    {
        AnimationSet animationset = new AnimationSet(true);

        Animation anim = new ScaleAnimation(1.0f, 4.0f, 1.0f, 4.0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        Animation alphaAnimation = new AlphaAnimation(1, 0);
        animationset.addAnimation(anim);
        animationset.addAnimation(alphaAnimation);
        animationset.setDuration(durationMillis);
        animationset.setFillAfter(true);
        return animationset;
    }

    public Position getmPosition()
    {
        return mPosition;
    }

    public void setmPosition(Position mPosition)
    {
        this.mPosition = mPosition;
    }

    public int getmRadius()
    {
        return mRadius;
    }

    public void setmRadius(int mRadius)
    {
        this.mRadius = mRadius;
    }

    public Status getmCurrentStatus()
    {
        return mCurrentStatus;
    }

    public void setmCurrentStatus(Status mCurrentStatus)
    {
        this.mCurrentStatus = mCurrentStatus;
    }

    public OnMenuItemClickListener getOnMenuItemClickListener()
    {
        return onMenuItemClickListener;
    }

    public void setOnMenuItemClickListener(
            OnMenuItemClickListener onMenuItemClickListener)
    {
        this.onMenuItemClickListener = onMenuItemClickListener;
    }

}
时间: 2024-09-29 10:09:08

Android 自定义ViewGroup实现弧形菜单的相关文章

Android 自定义ViewGroup手把手教你实现ArcMenu

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/37567907 逛eoe发现这样的UI效果,感觉很不错,后来知道github上有这么个开源项目~~~~当然本篇不是教你如何使用这个开源项目,而是教你如何自己通过自定义ViewGroup写这样的效果,自定义ViewGroup也是我的痛楚,嘿嘿,希望以此可以抛砖引玉~~ 效果图: 1.实现思路 通过效果图,会有几个问题: a.动画效果如何实现 可以看出动画是从顶点外外发射的,可能有人

Android 自定义ViewGroup之实现FlowLayout-标签流容器

本篇文章讲的是Android 自定义ViewGroup之实现标签流式布局-FlowLayout,开发中我们会经常需要实现类似于热门标签等自动换行的流式布局的功能,网上也有很多这样的FlowLayout,但不影响我对其的学习.和往常一样,主要还是想总结一下自定义ViewGroup的开发过程以及一些需要注意的地方. 按照惯例,我们先来看看效果图 一.写代码之前,有几个是问题是我们先要弄清楚的: 1.什么是ViewGroup:从名字上来看,它可以被翻译为控件组,言外之意是ViewGroup内部包含了许

Android 自定义ViewGroup 实战篇 -&gt; 实现FlowLayout

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38352503 ,本文出自[张鸿洋的博客] 1.概述 上一篇已经基本给大家介绍了如何自定义ViewGroup,如果你还不了解,请查看:Android 手把手教您自定ViewGroup ,本篇将使用上篇介绍的方法,给大家带来一个实例:实现FlowLayout,何为FlowLayout,如果对Java的Swing比较熟悉的话一定不会陌生,就是控件根据ViewGroup的宽,自动的往右

android自定义viewgroup实现等分格子布局

先上效果图: 实现这样的效果: 一般的思路就是,直接写布局文件,用LinearLayout 嵌套多层子LinearLayout,然后根据权重layout_weight可以达到上面的效果 还有就是利用gridview了,但是这里的需求就是不能上下滑动,使用gridview的时候还要计算布局的高度,否则内容超出下滑: 开始我是用的第一种,直接在布局文件实现了,但是后来发现代码太多太恶心哦,所以我继承viewGroup,重写两个关键的方法:onLayout(),onMeasure() 我的大致思路:

Android自定义ViewGroup (选择照片或者拍照)

教你搞定Android自定义ViewGroup http://www.jianshu.com/p/138b98095778 字数1794 阅读7030 评论8 喜欢37 上一篇我们介绍了Android中自定义View的知识,并实现了一个类似Google彩虹进度条的自定义View,今天我们将进一步学习如何去自定义一个ViewGroup. ViewGroup 我们知道ViewGroup就是View的容器类,我们经常用的LinearLayout,RelativeLayout等都是ViewGroup的子

Android 自定义ViewGroup,实现侧方位滑动菜单

侧方位滑动菜单 1.现在adnroid流行的应用当中很多都是用的侧方位滑动菜单如图: 将菜单显示在左边,内容页面显示在右边,通过滑动或则按钮点击来隐藏和显示菜单. 2.首先对ViewGroup进行个了解: View是ViewGroup的父类,ViewGroup具有View的所有特性,ViewGroup主要用用来充当View的容器,将其中的View作为自己孩子, 并对其进行管理,当然孩子也是可以是ViewGroup类型. View类一般用于绘图操作,重写他的onDraw方法,但它不可以包含其他组件

Android自定义ViewGroup(一)——带箭头的圆角矩形菜单

今天要做一个带箭头的圆角矩形菜单,大概长下面这个样子: 要求顶上的箭头要对准菜单锚点,菜单项按压反色,菜单背景色和按压色可配置. 最简单的做法就是让UX给个三角形的图片往上一贴,但是转念一想这样是不是太low了点,而且不同分辨率也不太好适配,干脆自定义一个ViewGroup吧! 自定义ViewGroup其实很简单,基本都是按一定的套路来的. 一.定义一个attrs.xml 就是声明一下你的这个自定义View有哪些可配置的属性,将来使用的时候可以自由配置.这里声明了7个属性,分别是:箭头宽度.箭头

【Android自定义ViewGroup】不一样的轮子,巧用类变量解决冲突,像IOS那样简单的使用侧滑删除,一个控件搞定Android item侧滑删除菜单。

================================================================================== [1 序言] 侧滑删除的轮子网上有很多,最初在github上看过一个,还是ListView时代,那是一个自定义ListView 实现侧滑删除的,当初就觉得这种做法不是最佳,万一我项目里又同时有自定义ListView的需求,会增加复杂度. 写这篇文章之前又通过毒度搜了一下,排名前几的CSDN文章,都是通过自定义ListVIew和Vie

android自定义viewgroup初步之一----抽屉菜单

几天前在慕课网上看到鸿洋老师的 自定义卫星菜单,感觉很有意思,于是看完视频以后,自己也尝试写了一遍,并且添加了可拖拽效果(光看视频是不管用的,一定要自己动手做!切记不要照着抄代码). 有兴趣的同学可以去慕课网看看(并非广告):http://www.imooc.com/learn/300 自定义控件这个玩意呢,就得考多练,于是又写了一个抽屉效果的菜单,也是比较简单的. 老规矩,先上效果图: 那么中间的圆圈就是卫星菜单拉,而左下角的呢,是抽屉菜单. 下面进入正题: 自定义Viewgroup的一般步骤