• Android 实现卫星菜单


    步骤:一:自定义ViewGroup

            1、自定义属性

                   a、attr.xml

                   b、在布局文件中使用activity_main.xml

                   c、在自定义控件中进行读取

            2、onMeasure

            3、onLayout

            4、设置主按钮的旋转动画

                 为menuItem添加平移动画和旋转动画

                 实现menuItem的点击事件

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
        private ListView listView;
        private List<String> mData;
        private ArrayAdapter mAdapter;
    
        private ArcMenuActivity mArc;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            listView = (ListView) findViewById(R.id.listview);
            mArc = (ArcMenuActivity) findViewById(R.id.view_arc);
    
            addData();
            mAdapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, mData);
            listView.setAdapter(mAdapter);
    
            listView.setOnScrollListener(new AbsListView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(AbsListView view, int scrollState) {
    
                }
    
                @Override
                public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                    if (mArc.isOpen()) {
                        mArc.toggleMenu(200);
                    }
                }
            });
    
            mArc.setOnMenuItemClickListener(new ArcMenuActivity.onMenuItemClickListener() {
                @Override
                public void onItemClick(View view, int position) {
                    Toast.makeText(MainActivity.this,position+" : "+view.getTag(),Toast.LENGTH_SHORT).show();
                }
            });
        }
    
        private void addData() {
            mData = new ArrayList<String>();
            for (int i = 'A'; i <= 'z'; i++) {
                mData.add((char) i + "");
            }
        }
        
    }

    ArcMenuActivity.java

    /**
     * 自定义的ViewGroup,在activity_main.xml里面调用
     */
    public class ArcMenuActivity extends ViewGroup {
        private static final int POS_LEFT_TOP = 0;
        private static final int POS_RIGHT_TOP = 1;
        private static final int POS_LEFT_BOTTOM = 2;
        private static final int POS_RIGHT_BOTTOM = 3;
    
        private Position mPosition = Position.RIGHT_BOTTOM;
        private Status mCurrentStatus = Status.CLOSE;
        private onMenuItemClickListener onMenuItemClick;
        private View mCButton;       //主菜单按钮
        private int mRadius;         //卫星半径
    
        //卫星菜单位置枚举类
        public enum Position {
            LEFT_TOP, RIGHT_TOP, LEFT_BOTTOM, RIGHT_BOTTOM
        }
    
        //主菜单的状态
        public enum Status {
            OPEN, CLOSE
        }
    
        //定义一个点击点击子菜单项的回调接口
        public interface onMenuItemClickListener {
            void onItemClick(View view, int position);
        }
    
        //自定义的点击方法
        public void setOnMenuItemClickListener(onMenuItemClickListener onMenuItemClick) {
            this.onMenuItemClick = onMenuItemClick;
        }
    
        public ArcMenuActivity(Context context) {
    //        super(context);
            this(context, null);
        }
    
        public ArcMenuActivity(Context context, AttributeSet attrs) {
    //        super(context, attrs);
            this(context, attrs, 0);
        }
    
        public ArcMenuActivity(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            //获取自定义属性的值
            TypedArray ta = context.getTheme().obtainStyledAttributes(
                    attrs, R.styleable.ArcMenu, defStyleAttr, 0);
    
            //getInt()方法
            //参数1:所需要赋予给pos的值
            // 参数2:如果参数1无值,则取该值,就是custom:position="right_bottom"没有定义时
            int pos = ta.getInt(R.styleable.ArcMenu_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) TypedValue.applyDimension(
                    TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics());
    
            //getDimension()方法
            //参数1:所需要赋予给radius的值
            // 参数2:如果参数1无值,则取该值,就是custom:radius="100"没有定义时
            mRadius = (int) ta.getDimension(R.styleable.ArcMenu_radius, TypedValue.applyDimension(
                    TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics()));
    
            //radius输出的值为px
            Log.v("TGA", "position = " + mPosition + ",radius = " + mRadius);
    
            ta.recycle();
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            //获取activity_main.xml里面的View控件的个数
            // <my.com.example.x550v.view.ArcMenuActivity/>里面的
            int count = getChildCount();
            //测量child
            for (int i = 0; i < count; i++) {
                //xml文件里面控件的位置,宽,高
                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) {
                //主菜单按钮
                layoutCButton();
                //子菜单按钮
                subItemButton();
            }
        }
    
        //定义主菜单按钮
        private void layoutCButton() {
            //或者使用findViewById()的方法
            mCButton = getChildAt(0); //获取第一个xml文件里面的第一个View控件
            mCButton.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    //主菜单动画
                    //这里得使用getContext()来获取context
                    Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.anim);
                    //如果使用setAnimation()只会转一次
                    mCButton.startAnimation(animation);
    
                    //子菜单动画
                    toggleMenu(200);
                }
            });
    
            //l为主菜单距离父布局的左边距离,t为主菜单距离父布局的顶边距离
            int l = 0, t = 0;
            //获取主按钮的宽和高
            int width = mCButton.getMeasuredWidth();
            int height = mCButton.getMeasuredHeight();
    
            switch (mPosition) {
                case LEFT_TOP:
                    //0,0表示坐上角的位置
                    l = 0;
                    t = 0;
                    break;
                case RIGHT_TOP:
                    l = getMeasuredWidth() - width;   //getMeasuredWidth()取得容器的宽度
                    t = 0;
                    break;
                case LEFT_BOTTOM:
                    l = 0;
                    t = getMeasuredHeight() - height; //getMeasuredHeight()取得容器的高度
                    break;
                case RIGHT_BOTTOM:
                    l = getMeasuredWidth() - width;
                    t = getMeasuredHeight() - height;
                    break;
            }
            mCButton.layout(l, t, l + width, t + height);
        }
    
        //定义子菜单按钮
        public void subItemButton() {
            //获取activity_main.xml里面的View控件的个数
            // <my.com.example.x550v.view.ArcMenuActivity/>里面的
            int count = getChildCount();
    
            for (int i = 0; i < count - 1; i++) { //去掉主菜单按钮的一个
                View child = getChildAt(i + 1);   //从第一个子菜单开始获取,而不是主菜单
    
                //开始时设置子菜单为隐藏
                child.setVisibility(GONE);
    
                //当子菜单为左上角时,cl为子菜单距离父布局的左边距离,ct为子菜单距离父布局的顶边距离
                //当子菜单为右上角时,cl为子菜单距离父布局的右边距离,ct为子菜单距离父布局的顶边距离
                //当子菜单为左下角时,cl为子菜单距离父布局的左边距离,ct为子菜单距离父布局的底边距离
                //当子菜单为右下角时,cl为子菜单距离父布局的右边距离,ct为子菜单距离父布局的底边距离
                //Math.PI的值为圆周率pai,角度为180度
                //Math.PI / 2 / (count - 2)是取出平均角,
                //*i是看子菜单拥有几个平均角
                int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));
    //            Log.v("TAG","cl = "+cl);
                int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i));
    
                //获取子菜单View的宽和高
                int cWidth = child.getMeasuredWidth();
                int cHeight = child.getMeasuredHeight();
    
                //如果子菜单在左下,右下
                if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM) {
                    ct = getMeasuredHeight() - cHeight - ct;
                    Log.v("TAG","getMeasuredHeight()"+getMeasuredHeight());
                }
                //如果子菜单在右上,右下
                if (mPosition == Position.RIGHT_TOP || mPosition == Position.RIGHT_BOTTOM) {
                    cl = getMeasuredWidth() - cWidth - cl;
                }
                child.layout(cl, ct, cl + cWidth, ct + cHeight);
            }
        }
    
        //定义点击主菜单后子菜单出现动画
        public void toggleMenu(int duration) {
            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;
                }
    
                final AnimationSet animationSet = new AnimationSet(true);
    
                //位移动画
                Animation tranAnim = null;
                //如果为关闭状态,点击后会散开
                if (mCurrentStatus == Status.CLOSE) {
                    //子菜单一开始就是扇形排布,其坐标为(0,0)
                    tranAnim = new TranslateAnimation(xFlag * cl, 0, yFlag * ct, 0);
                    childView.setClickable(true);
                    childView.setFocusable(true);
                }
                //否则为打开状态,点击后会收缩
                else {
                    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 Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
    
                    }
    
                    @Override
                    public void onAnimationEnd(Animation animation) {
                        if (mCurrentStatus == Status.CLOSE) {
                            childView.setVisibility(GONE);
                        }
                    }
    
                    @Override
                    public void onAnimationRepeat(Animation animation) {
    
                    }
                });
    
                //旋转动画
                RotateAnimation rotateAnim = new RotateAnimation(
                        0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                rotateAnim.setFillAfter(true);
                rotateAnim.setDuration(duration);
    
                //先旋转,再位移
                //先位移,再旋转更炫
    //            animationSet.addAnimation(tranAnim);
                animationSet.addAnimation(rotateAnim);
                animationSet.addAnimation(tranAnim);
                childView.startAnimation(animationSet);
    
                final int pos = i + 1;
                //子菜单的点击监听
                childView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (onMenuItemClick != null) {
                            onMenuItemClick.onItemClick(childView, pos);
                        }
                        menuItemAnim(pos - 1);  //子菜单点击动画
                        changeStatus();
                    }
                });
            }
            //切换菜单状态,在for()循环之外
            changeStatus();
        }
    
        //切换菜单状态
        private void changeStatus() {
            mCurrentStatus = (mCurrentStatus == Status.CLOSE ? Status.OPEN : Status.CLOSE);
        }
    
        //添加子菜单点击动画
        private void menuItemAnim(int pos) {
            for (int i = 0; i < getChildCount() - 1; i++) {
                View childView = getChildAt(i + 1);
                if (i == pos) {
                    childView.startAnimation(scaleBigAnimation(300));
                } else {
                    childView.startAnimation(scaleSmallAnimation(300));
                }
                childView.setClickable(false);
                childView.setFocusable(false);
            }
        }
    
        //子菜单变大,变小动画
        private Animation scaleBigAnimation(int duration) {
            AnimationSet set = new AnimationSet(true);
            Animation scaleAnim = new ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            Animation alphaAnim = new AlphaAnimation(1, 0);
            set.setDuration(duration);
            set.addAnimation(scaleAnim);
            set.addAnimation(alphaAnim);
            set.setFillAfter(true);
            return set;
        }
    
        private Animation scaleSmallAnimation(int duration) {
            AnimationSet set = new AnimationSet(true);
            Animation scaleAnim = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
            Animation alphaAnim = new AlphaAnimation(1, 0);
            set.setDuration(duration);
            set.addAnimation(scaleAnim);
            set.addAnimation(alphaAnim);
            set.setFillAfter(true);
            return set;
        }
    
        public boolean isOpen(){
            return mCurrentStatus == Status.OPEN;
        }
    
    }

    activity_main.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:custom="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <ListView
            android:id="@+id/listview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
        <my.com.example.x550v.view.ArcMenuActivity
            android:id="@+id/view.arc"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            custom:position="right_bottom"
            custom:radius="200dp">
    
            <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/composer_button">
    
                <ImageView
                    android:id="@+id/iv_button"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:src="@drawable/composer_icn_plus" />
            </RelativeLayout>
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/composer_camera"
                android:tag="Camera" />
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/composer_music"
                android:tag="Music" />
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/composer_place"
                android:tag="Place" />
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/composer_sleep"
                android:tag="Sleep" />
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/composer_thought"
                android:tag="Thought" />
    
            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/composer_with"
                android:tag="With" />
    
        </my.com.example.x550v.view.ArcMenuActivity>
    
    </RelativeLayout>

    attr.xml

    <resources>
        <attr name="position">
            <enum name="left_top" value="0" />
            <enum name="right_top" value="1" />
            <enum name="left_bottom" value="2" />
            <enum name="right_bottom" value="3" />
        </attr>
        <attr name="radius" format="dimension" />
    
    
        <declare-styleable name="ArcMenu">
            <attr name="position" />
            <attr name="radius" />
        </declare-styleable>
    
    </resources>

    anim.xml

    <set
        xmlns:android="http://schemas.android.com/apk/res/android">
        <rotate
            android:duration="500"
            android:fromDegrees="0"
            android:toDegrees="360"
            android:pivotX="50%"
            android:pivotY="50%"
            android:fillAfter="true"/>
    </set>

    运行效果:

  • 相关阅读:
    模板、皮肤、主题的定义
    table的thead/tbody/tfoot/tr/th/td
    JavaScript实现图片拖动功能 SpryMap
    js调用.net后台事件,和后台调用前台等方法总结
    图片轮播效果
    关于json的一点总结
    jQuery插件编写
    JavaScript中的数组
    HTML中Form属性Name和ID的区别
    DetailsView和FormView
  • 原文地址:https://www.cnblogs.com/tianhengblogs/p/5263186.html
Copyright © 2020-2023  润新知