首页 > 系统 > Android > 正文

Android实现自定义的卫星式菜单(弧形菜单)详解

2019-12-12 05:33:38
字体:
来源:转载
供稿:网友

一、前言

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

三、总结

Android实现自定义的卫星式菜单(弧形菜单)的内容到这就基本结束了,感兴趣的朋友们可以动手操作起来,只有自己实践了才能更深的理解,希望本文对大家能有所帮助。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表