Android实现自定义的卫星式菜单(弧形菜单)详解_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();
   }
  });
 }
}

三、总结

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

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索android
, 右侧
, 弧形菜单
, 弧形滑动菜单
android卫星菜单
js 自定义 卫星菜单、ios 自定义弧形slider、svg path a自定义弧形、android 弧形菜单、android 弧形滑动菜单,以便于您获取更多的相关知识。

时间: 2024-09-20 09:40:15

Android实现自定义的卫星式菜单(弧形菜单)详解_Android的相关文章

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

一.前言 Android 实现卫星式菜单也叫弧形菜单,主要要做的工作如下: 1.动画的处理 2.自定义ViewGroup来实现卫星式菜单View (1)自定义属性 a. 在attrs.xml中定义属性 b. 在布局中使用自定义属性 c. 在自定义View中读取布局文件中的自定义属性 (2)onMeasure 测量 child 即测量主按钮以及菜单项 (3)onLayout 布局 child 即布局主按钮以及菜单项 (4)设置主按钮的选择动画 a.为菜单项menuItem添加平移动画和旋转动画 b

Android中gson、jsonobject解析JSON的方法详解_Android

JSON的定义: 一种轻量级的数据交换格式,具有良好的可读和便于快速编写的特性.业内主流技术为其提供了完整的解决方案(有点类似于正则表达式 ,获得了当今大部分语言的支持),从而可以在不同平台间进行数据交换.JSON采用兼容性很高的文本格式,同时也具备类似于C语言体系的行为. JSON对象: JSON中对象(Object)以"{"开始, 以"}"结束. 对象中的每一个item都是一个key-value对, 表现为"key:value"的形式, ke

Android编程之软键盘的隐藏显示实例详解_Android

本文实例分析了Android编程之软键盘的隐藏显示方法.分享给大家供大家参考,具体如下: Android是一个针对触摸屏专门设计的操作系统,当点击编辑框,系统自动为用户弹出软键盘,以便用户进行输入. 那么,弹出软键盘后必然会造成原有布局高度的减少,那么系统应该如何来处理布局的减少?我们能否在应用程序中进行自定义的控制?这些是本文要讨论的重点. 一.软键盘显示的原理 软件盘的本质是什么?软键盘其实是一个Dialog! InputMethodService为我们的输入法创建了一个Dialog,并且将

ListView点击Item展开菜单实现代码详解_Android

一.概述 ListView点击item显示菜单是要实现这样的效果: 需要实现的逻辑如下: 1)点击一个普通item,展开当前菜单,同时关闭其他菜单 2)点击一个已展开的菜单,隐藏当前菜单 3)将展开菜单滑到listview之外,再滑动回来,展开菜单状态不变 4)点击菜单中的按钮,能够根据不同item进行不同的处理 二.实现思路 1.UI布局上,对于这种每个listitem都包含动态显示菜单的场景,可以直接在listitem的xml布局里就包含两部分元素:item本身以及展开菜单 点击item的时

Android屏幕锁屏弹窗的正确姿势DEMO详解_Android

在上篇文章给大家介绍了Android程序开发仿新版QQ锁屏下弹窗功能.今天通过本文给大家分享android锁屏弹窗的正确姿势. 最近在做一个关于屏幕锁屏悬浮窗的功能,于是在网上搜索了很多安卓屏幕锁屏的相关资料,鉴于网上的资料比较零碎,所以我在这里进行整理总结.本文将从以下两点对屏幕锁屏进行解析: 1. 如何监听系统屏幕锁屏 2. 如何在锁屏界面弹出悬浮窗 如何监听系统屏幕锁屏 经过总结,监听系统的锁屏可以通过以下两种方式: 1) 代码直接判定 2) 接收广播 1) 代码直接判定 代码判断方式,也

Android 实现夜间模式的快速简单方法实例详解_Android

ChangeMode 项目地址:ChangeMode Implementation of night mode for Android. 用最简单的方式实现夜间模式,支持ListView.RecyclerView. Preview Usage xml android:background="?attr/zzbackground" app:backgroundAttr="zzbackground"//如果当前页面要立即刷新,这里传入属性名称 比如 R.attr.zzb

Android之用PopupWindow实现弹出菜单的方法详解_Android

在使用UC-WebBrowser时,你会发现它的弹出菜单跟系统自带的菜单不一样.它实现更多菜单选项的显示和分栏.其实,它的本身是PopupWindow或者是AlertDialog对话框,在里面添加两个GridView控件,一个是菜单标题栏,一个是菜单选项.菜单选项视图的切换可以通过适配器的变换,轻松地实现.点击下载该实例:一.运行截图:             二.实现要点:(1)屏蔽系统弹出的菜单:1.首先创建至少一个系统的菜单选项 复制代码 代码如下: @Override public bo

Android 开源项目侧边栏菜单(SlidingMenu)使用详解_Android

项目下载地址:https://github.com/jfeinstein10/SlidingMenu 注意: SlidingMenu依赖于另一个开源项目ActionBarSherlock,所以需要将ActionBarSherlock添加作为SlidingMenu的库工程,否则会报资源找不到错误. 然后再将SlidingMenu添加到自己的工程中去. SlidingMenu集成常见错误: Jar mismatch! Fix your dependencies :引用的工程和自身工程以来的jar包版

Android权限管理之Permission权限机制及使用详解_Android

前言: 最近突然喜欢上一句诗:"宠辱不惊,看庭前花开花落:去留无意,望天空云卷云舒." 哈哈~,这个和今天的主题无关,最近只要不学习总觉得生活中少了点什么,所以想着围绕着最近面试过程中讨论比较多的一个知识点Android 6.0 权限适配问题来进行学习,不过我不想直接进入这个主题,所以选择先去了解一下Android的Permission权限机制及使用 Android权限机制: 权限是一种安全机制.Android权限主要用于限制应用程序内部某些具有限制性特性的功能使用以及应用程序之间的组