Android 实现自定义的卫星式菜单

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();
      }
    });
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值