博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 自定义ViewGroup实现弧形菜单
阅读量:5282 次
发布时间:2019-06-14

本文共 12697 字,大约阅读时间需要 42 分钟。

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; }}

 

转载于:https://www.cnblogs.com/xbx2015/p/4702667.html

你可能感兴趣的文章
2012-01-12 16:01 hibernate注解以及简单实例
查看>>
iOS8统一的系统提示控件——UIAlertController
查看>>
PAT甲级——1101 Quick Sort (快速排序)
查看>>
python创建进程的两种方式
查看>>
1.2 基础知识——关于猪皮(GP,Generic Practice)
查看>>
迭代器Iterator
查看>>
java易错题----静态方法的调用
查看>>
php建立MySQL数据表
查看>>
最简单的线程同步的例子
查看>>
旅途上看的电影和观后感
查看>>
Ztree异步树加载
查看>>
关于IE和火狐,谷歌,Safari对Html标签Object和Embed的支持问题
查看>>
poj3320 Jessica's Reading Problem(尺取思路+STL)
查看>>
分布式计算开源框架Hadoop介绍
查看>>
安卓平台接口剖析
查看>>
坏的事情不都会带来坏的结果
查看>>
RPC的基础:调研EOS插件http_plugin
查看>>
第二次团队冲刺第二天
查看>>
bzoj 2257 (JSOI 2009) 瓶子与燃料
查看>>
11)Java abstract class 和 interface
查看>>