效果:
实现:
1.设置属性:在values文件夹下创建arr.xml,内容:
<?xml version="1.0" encoding="utf-8"?> <resources> <!--半径属性--> <attr name="radius" format="dimension" /> <!--自定义控件属性--> <declare-styleable name="RotateMenu"> <!--<attr name="position" />--> <attr name="radius" /> </declare-styleable> </resources>
自定义View:
public class RotateMenuView extends ViewGroup { /**定义屏幕的宽度*/ private int pm_width; /**定义屏幕的高度*/ private int pm_height; /**定义菜单半径*/ private int mRadius; /** * 枚举类 菜单状态 */ public enum Status { OPEN, CLOSE } /**默认为关闭状态*/ private Status mCurrentStatus = Status.CLOSE; /**触发菜单的按钮*/ private View mButton; /** * 单击子菜单的回调接口 */ public interface OnRotateMenuItemClickListener { void onClick(View view, int pos); } /**子菜单单击事件*/ private OnRotateMenuItemClickListener mMenuItemClickListener; //构造方法 public RotateMenuView(Context context) { this(context,null); } //构造方法 public RotateMenuView(Context context, AttributeSet attrs) { this(context, attrs,0); } //构造方法 public RotateMenuView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //获取屏幕宽高 WindowManager windowManager= (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); pm_width=windowManager.getDefaultDisplay().getWidth(); pm_height=windowManager.getDefaultDisplay().getHeight(); // 获取自定义属性的值 TypedArray typedArray=context.getTheme().obtainStyledAttributes(attrs, R.styleable.RotateMenu,defStyleAttr,0); //获取按钮半径属性值 mRadius= (int) typedArray.getDimension(R.styleable.RotateMenu_radius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100,getResources().getDisplayMetrics())); //回收资源 typedArray.recycle(); } /** *测量自定义控件中所有子控件的尺寸 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //获取所有的按钮 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) { //如果按钮布局发生改变 if (changed) { //调用主按钮布局位置的方法 buttonLayout(); childLayout(); } } /** * 设置主按钮在布局中的位置 */ private void buttonLayout() { mButton = getChildAt(0); //获取菜单主按钮 //设置按钮的单击事件 mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //调用主按钮旋转的方法 rotatemButton(v, 0f, 360f, 500); //调用收缩子按钮动画方法 shrinkMenu(500); } }); //初始化按钮左边距与上边距 int l = 0; int t = 0; //获取主按钮的宽度与高度 int width = mButton.getMeasuredWidth(); int height = mButton.getMeasuredHeight(); //判断主按钮中间按钮的位置 l = pm_width/2-width/2; t = getMeasuredHeight() - height; //设置主按钮在父容器的位置 mButton.layout(l, t, l + width, t + width); } private void childLayout() { //获取所有的子按钮 int count = getChildCount(); //遍历所有按钮,第一个是主按钮,所以其他子按钮的总数是count - 1 for (int i = 0; i < count - 1; i++) { //获取子控件下标,因为子按钮是从1开始的,所以i + 1 View child = getChildAt(i + 1); //隐藏子按钮 child.setVisibility(View.GONE); //获取子按钮的宽高尺寸 int childWidth = child.getMeasuredWidth(); int childHeight = child.getMeasuredHeight(); //中心坐标(pm_width/2-childWidth/2,getMeasuredHeight() - height) int c_x = pm_width / 2 - childWidth / 2; int c_y = getMeasuredHeight() - childHeight/2; //子按钮与父容器左边距 int childLeft = (int) (mRadius * Math.sin(Math.PI / (count - 2) * i)); int childTop = (int) (mRadius * Math.sin(Math.PI / (count - 2) * i)); childTop = getMeasuredHeight() - childHeight - childTop; if (i == 0) { childLeft = c_x - mRadius; childTop = getMeasuredHeight() - childHeight; } else if (i == 1) { childLeft = c_x - childLeft; } else if (i == 2) { childLeft = c_x; childTop=getMeasuredHeight() - childHeight-mRadius; } else if (i == 3) { childLeft = c_x + childLeft; } else if (i == 4) { childLeft = c_x + mRadius; childTop = getMeasuredHeight() - childHeight; } //设置子按钮的位置 child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); } } /** *收缩子按钮动画 */ public void shrinkMenu(int time) { //获取所有的子按钮 int count = getChildCount(); //遍历所有按钮 for (int i = 0; i < count - 1; i++){ //获取子按钮下标 final View childView = getChildAt(i + 1); //显示子按钮 childView.setVisibility(View.VISIBLE); //动画开始与结束的位置x,y int clx = (int) (mRadius * Math.sin(Math.PI / (count - 2) * i)); int cty = (int) (mRadius * Math.cos(Math.PI / (count - 2) * i)); if (i == 0) { //第一个子按钮 clx = mRadius; cty = 0; } else if (i == 1) { //第二个子按钮 } else if (i == 2) { //第三个子按钮 clx = 0; cty = mRadius; } else if (i == 3) { //第四个子按钮 clx=(int) (mRadius * Math.cos(Math.PI / (count - 2) * i)); cty=-cty; } else if (i == 4) { //第五个子按钮 clx = - mRadius; cty = 0; } //设置动画集合 AnimationSet animset = new AnimationSet(true); Animation tranAnim = null; //如果当前按钮菜单为关闭状态 if (mCurrentStatus == Status.CLOSE){ //设置打开按钮菜单动画 tranAnim = new TranslateAnimation(clx,0 , cty, 0); //子按钮可以单击 childView.setClickable(true); //子按钮可调焦 childView.setFocusable(true); } else{ //如果当前按钮菜单为开启状态,设置关闭按钮菜单动画 tranAnim = new TranslateAnimation(0, clx, 0, cty); //子按钮不可点击 childView.setClickable(false); //子按钮不可调焦 childView.setFocusable(false); } // 动画显示的时间 tranAnim.setDuration(time); //停止最后一帧的位置 tranAnim.setFillAfter(true); tranAnim.setStartOffset((i * 100) / count); //设置动画监听器 tranAnim.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } /** *动画结束后隐藏子按钮 */ @Override public void onAnimationEnd(Animation animation) { if (mCurrentStatus == 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(time); //停止最后一帧的位置 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 (mMenuItemClickListener != null) mMenuItemClickListener.onClick(childView, pos); //调用字按钮单击动画方法 childButtonClickAnim(pos - 1); //调用菜单状态方法 menuStatus(); } }); } // 切换菜单状态 menuStatus(); } /** * 子按钮单击动画的方法 */ private void childButtonClickAnim(int pos) { for (int i = 0; i < getChildCount() - 1; i++) { View childView = getChildAt(i + 1); //当前单击的子按钮 if (i == pos) { //启动子按钮变大并消失动画 childView.startAnimation(scaleBigAnim(200)); } else { //启动其它没有被单击的按钮变小并消失动画 childView.startAnimation(scaleSmallAnim(200)); } //子按钮不可点击 childView.setClickable(false); //子按钮不可调焦 childView.setFocusable(false); } } /** *没有被单击的子按钮,变小并消失的动画方法 */ private Animation scaleSmallAnim(int time) { //设置动画集合 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(time); //停止最后一帧的位置 animationSet.setFillAfter(true); return animationSet; } /** * 单击子按钮,当前子按钮变大并消失动画方法 */ private Animation scaleBigAnim(int time) { //设置动画集合 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(time); //停止最后一帧的位置 animationSet.setFillAfter(true); return animationSet; } /** * 切换菜单状态 */ private void menuStatus() { mCurrentStatus = (mCurrentStatus == Status.CLOSE ? Status.OPEN : Status.CLOSE); } /** *设置主按钮旋转动画 */ private void rotatemButton(View v, float start, float end, int time) { //中心旋转动画 RotateAnimation anim = new RotateAnimation(start, end, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); //设置旋转的时间 anim.setDuration(time); //停止最后一帧的位置 anim.setFillAfter(true); //开始动画 v.startAnimation(anim); } }
3.main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:menu="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimary" tools:context=".MainActivity"> <com.example.shanshan.statemenu.RotateMenuView android:id="@+id/satelliteMenu" android:layout_width="match_parent" android:layout_height="match_parent" menu:radius="100dp"> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@mipmap/button_bg" > <ImageView android:id="@+id/id_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@mipmap/button_in" /> </RelativeLayout> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/img5" android:tag="@string/text_btn1" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/img4" android:tag="@string/text_btn2" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/img3" android:tag="@string/text_btn3" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/img2" android:tag="@string/text_btn4" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@mipmap/img1" android:tag="@string/text_btn5" /> </com.example.shanshan.statemenu.RotateMenuView> </RelativeLayout>