XCArcMenuView 控件的使用

2019-08-01  本文已影响0人  geaosu

本文涉及内容如下:

  1. XCArcMenuView 控件显示截图展示;
  2. XCArcMenuView 控件源码+自定义属性内容+xml布局+activity/fragment中的使用代码;
  3. XCArcMenuView 控件在使用过程中可能会遇到的问题总结;

废话不多上, 上图:


XCArcMenuView 控件显示截图

动图: 加载中...

XCArcMenuView.java 类源码
我顺便新增了打开和关闭的方法, 方法忘记复制过来了, 后期会补齐

package com.geaosu.app.widget;

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;

import com.geaosu.testapplication.R;

/**
 * 卫星式菜单View
 */
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;
    //主icon到item的icon的距离
    private int mRadius;
    //状态
    private Status mStatus = Status.CLOSE;
    //主菜的单按钮
    private View mCButton;
    //事件监听器
    private OnItemClickListener mOnItemClickListener;

    /**
     * 菜单的状态枚举类
     */
    public enum Status {
        OPEN,
        CLOSE
    }

    /**
     * 菜单的位置枚举类
     */
    public enum Position {
        LEFT_TOP,
        LEFT_BOTTOM,
        RIGHT_TOP,
        RIGHT_BOTTOM
    }


    /**
     * 从代码中new对象
     *
     * @param context
     */
    public XCArcMenuView(Context context) {
        this(context, null);
        // TODO Auto-generated constructor stub
    }

    /**
     * 从代码中new对象, 带有主题的
     *
     * @param context
     * @param attrs
     */
    public XCArcMenuView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        // TODO Auto-generated constructor stub
    }

    /**
     * 从布局中加载对象
     *
     * @param context
     * @param attrs
     * @param defStyle
     */
    public XCArcMenuView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        // TODO Auto-generated constructor stub
        //获取自定义属性
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.XCArcMenuView, defStyle, 0);
        //获取布局中设置的位置值(positio的值)
        int pos = typedArray.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) typedArray.getDimension(R.styleable.XCArcMenuView_radius, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150, getResources().getDisplayMetrics()));
        Log.v("czm", "mPosition = " + mPosition + ",mRadius = " + mRadius);
        typedArray.recycle();
    }

    /**
     * 设置监听器
     */
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.mOnItemClickListener = onItemClickListener;
    }


    /**
     * 绘制
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @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.btn1);
        rotateCButton(v, 0, 360, 300);
        toggleMenu(300);
    }

    /**
     * 切换菜单(展开/隐藏 item)
     */
    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;

            if (mStatus == Status.CLOSE) {
                // to open
                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 (mOnItemClickListener != null) {
                        mOnItemClickListener.onItemClick(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);
    }

    /**
     * Item的点击隐藏动画
     */
    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));
            }

            //item不可点击
            childView.setClickable(false);
            childView.setFocusable(false);
            //解决无法显示并且无法点击问题
            childView.setVisibility(View.GONE);
        }
    }

    /**
     * 为当前点击的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;
    }

    /**
     * 判断是否处于展开状态
     *
     * @return
     */
    public boolean isShow() {
        return mStatus == Status.OPEN;
    }

    /**
     * 打开item
     */
    public void open() {
        if (!isShow()) {
            menuItemAnim(getChildCount());
            changeStatus();
        }
    }

    /**
     * 关闭item
     */
    public void close() {
        if (isShow()) {
            menuItemAnim(getChildCount());
            changeStatus();
        }
    }

    /**
     * 显示item
     */
    public void show() {
        if (!isShow()) {
            menuItemAnim(getChildCount());
            changeStatus();
        }
    }

    /**
     * 隐藏item
     */
    public void hide() {
        if (isShow()) {
            menuItemAnim(getChildCount());
            changeStatus();
        }
    }

    /**
     * 点击子菜单项的回调接口
     */
    public interface OnItemClickListener {
        void onItemClick(View view, int pos);
    }
}

在activity或者fragment中使用

 XCArcMenuView view = (XCArcMenuView) findViewById(R.id.arcmenu);
        view.setOnItemClickListener(new XCArcMenuView.OnItemClickListener() {

            @Override
            public void onItemClick(View view, int pos) {
                // TODO Auto-generated method stub
                String tag = (String) view.getTag();
                Toast.makeText(MainActivity.this, tag, Toast.LENGTH_SHORT).show();
            }
        });

自定义属性文件: attrs.xml

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

xml布局代码

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.geaosu.testapplication.widget.XCArcMenuView
        android:id="@+id/arcmenu"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentBottom="true"
        app:position="left_bottom"
        app:radius="100dp">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#000000">

            <ImageView
                android:id="@+id/btn1"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_centerInParent="true"
                android:src="@mipmap/bg" />
        </RelativeLayout>

        <ImageView
            android:id="@+id/btn2"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@mipmap/bg"
            android:tag="camera" />

        <ImageView
            android:id="@+id/btn3"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@mipmap/bg"
            android:tag="music" />

        <ImageView
            android:id="@+id/btn4"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@mipmap/bg"
            android:tag="place" />

    </com.geaosu.testapplication.widget.XCArcMenuView>

    <com.geaosu.testapplication.widget.XCArcMenuView
        android:id="@+id/arcmenu2"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentBottom="true"
        app:position="right_bottom"
        app:radius="100dp">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#000000">

            <ImageView
                android:id="@+id/btn11"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:layout_centerInParent="true"
                android:src="@mipmap/bg" />
        </RelativeLayout>

        <ImageView
            android:id="@+id/btn12"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@mipmap/bg"
            android:tag="camera" />

        <ImageView
            android:id="@+id/btn13"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@mipmap/bg"
            android:tag="music" />

        <ImageView
            android:id="@+id/btn14"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@mipmap/bg"
            android:tag="place" />

    </com.geaosu.testapplication.widget.XCArcMenuView>
</RelativeLayout>

在使用过程中可能会遇到的问题总结:

问题1: 在fragment中使用时无法隐藏和点击问题

缘由: 前几天发现了一个控件显示问题, 最近任务被安排的很满, 一直没时间去改, 直到昨天, 这个任务分给了我, 我昨天任务太多, 所有今天才处理这个问题.

问题描述: 我们的项目里使用了一个开源控件, 该控件博客介绍地址(https://www.jb51.net/article/91158.htm), 这个控件展开没有任何问题, 但是隐藏时会出现问题, 是这样的, 我们在fragment中使用该控件时, 当该控件隐藏后, 来回切换fragment时发现, 该控件显示了出来, 并且item是无法点击的.

解决方案: 我看了源码后发现, 可能是作者的疏忽了, 忘记了一行代码, 就是在执行完隐藏动画后, 设置了animationSet.setFillAfter(true)属性, 让view保持动画结束的状态, 当我们切换fragment的时候, view重新绘制了, 这是这个属性就不起做作用了, 导致所有的item显示了, 并且无法点击是因为作者添加了childView.setClickable(false)这行代码, 禁止掉了view的点击事件, 到这里问题已经可以解决了, 这里只需要加上childView.setVisibility(View.GONE)这行代码即可;

可能有人不知道在哪里加这行代码, 我把整个方法贴出来, 后面还会把这个类以及这个类的使用和布局xml都会贴出来供大家学习借鉴: 

/**
 * Item的点击隐藏动画
 */
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));
        }
        //item不可点击
        childView.setClickable(false);
        childView.setFocusable(false);
        //解决无法显示并且无法点击问题
        childView.setVisibility(View.GONE);
    }
}
上一篇 下一篇

猜你喜欢

热点阅读