• Android 自定义View修炼-Android 实现自定义的卫星式菜单(弧形菜单)View


    一、总述

    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

  • 相关阅读:
    [博客美化]给你博客添加一个萌萌的看板娘吧
    精简Command版SqlHelper
    使用VS2017创建EF框架实例
    Ueditor1.4.3.3 asp UTF-8版文件缺失修改方法
    android studio编译包真机安装失败解决方案记录
    ANDROID开发之问题积累及解决方案(四)
    ANDROID开发之问题积累及解决方案(三)
    ANDROID调用webservice带soapheader验证
    ANDROID开发之问题积累及解决方案(二)
    ANDROID开发之问题积累及解决方案(一)
  • 原文地址:https://www.cnblogs.com/JczmDeveloper/p/4390625.html
Copyright © 2020-2023  润新知