一、总述

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

三、源码下载

源码下载:http://download.csdn.net/detail/jczmdeveloper/8561749

真题园网:http://www.zhentiyuan.com

Android 自定义View修炼-Android 实现自定义的卫星式菜单(弧形菜单)View的更多相关文章

  1. Android 自定义View修炼-Android中常见的热门标签的流式布局的实现

    一.概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出哈) 类似的 ...

  2. Android 自定义View修炼-Android实现圆形、圆角和椭圆自定义图片View(使用BitmapShader图形渲染方法)

    一.概述 Android实现圆角矩形,圆形或者椭圆等图形,一般主要是个自定义View加上使用Xfermode实现的.实现圆角图片的方法其实不少,常见的就是利用Xfermode,Shader.本文直接继 ...

  3. Android 自定义View修炼-Android开发之自定义View开发及实例详解

    在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难. 其主要原理就是继承View,重写构造方法.onDraw,(onMeasure)等 ...

  4. Android 自定义View修炼-打造完美的自定义侧滑菜单/侧滑View控件

    一.概述 在App中,经常会出现侧滑菜单,侧滑滑出View等效果,虽然说Android有很多第三方开源库,但是实际上 咱们可以自己也写一个自定义的侧滑View控件,其实不难,主要涉及到以下几个要点: ...

  5. Android 自定义View修炼-如何打造Android自定义的下拉列表框控件

    一.概述 Android中的有个原生的下拉列表控件Spinner,但是这个控件有时候不符合我们自己的要求, 比如有时候我们需要类似windows 或者web网页中常见的那种下拉列表控件,类似下图这样的 ...

  6. Android 自定义View修炼-【2014年最后的分享啦】Android实现自定义刮刮卡效果View

    一.简介: 今天是2014年最后一天啦,首先在这里,我祝福大家在新的2015年都一个个的新健康,新收入,新顺利,新如意!!! 上一偏,我介绍了用Xfermode实现自定义圆角和椭圆图片view的博文& ...

  7. Android 自定义View修炼-实现自定义圆形、圆角和椭圆ImageView(使用Xfermode图形渲染方法)

    一:简介: 在上一篇<Android实现圆形.圆角和椭圆自定义图片View(使用BitmapShader图形渲染方法)>博文中,采用BitmapShader方法实现自定义的圆形.圆角等自定 ...

  8. Android 自定义View修炼-仿360手机卫士波浪球进度的实现

    像360卫士的波浪球进度的效果,一般最常用的方法就是 画线的方式,先绘sin线或贝塞尔曲线,然后从左到右绘制竖线,然后再裁剪圆区域. 今天我这用图片bitmap的方式,大概的方法原理是: (1)首先用 ...

  9. Android 自定义View修炼-仿QQ5.0 的侧滑菜单效果的实现

    有一段时间没有写博客了,最近比较忙,没什么时间写,刚好今天有点时间, 我就分享下,侧滑菜单的实现原理,一般android侧滑的实现原理和步骤如下:(源码下载在下面最后给出哈) 1.使用ViewGrou ...

随机推荐

  1. cto

    CTO(首席技术官)英文Chief Technology Officer,即企业内负责技术的最高负责人.这个名称在1980年代从美国开始时兴.起于做很多研究的大公司,如General Electric ...

  2. Oracle 常用符号CHR

    select  chr(92)||chr(102) from dual; \f select  chr(92)||chr(110) from dual; \n select  chr(92)||chr ...

  3. scaleform mobile sdk for android 多点触摸 修正

    修正 scaleform 的多点触控 (随手一记 给后来的人做个参考) scaleform 版本号 4.2.24 (估计这就是最后一个 移动版的版本了,万年没有更新了) 开始 一直以为 scalefo ...

  4. hadoop namenode启动过程详细剖析及瓶颈分析

    NameNode中几个关键的数据结构 FSImage Namenode 会将HDFS的文件和目录元数据存储在一个叫fsimage的二进制文件中,每次保存fsimage之后到下次保存之间的所有hdfs操 ...

  5. [HDU 1430] 魔板

    魔板 Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submiss ...

  6. MongoDB之二(增删查改)

    一: Insert操作 上一篇也说过,文档是采用“K-V”格式存储的,如果大家对JSON比较熟悉的话,我相信学mongodb是手到擒来,我们知道JSON里面Value 可能是“字符串”,可能是“数组” ...

  7. 【原】Windows中使用Redis基本入门教程

    Redis是c编写基于Unix平台开发的一种内存KV数据库,官网上并没有给出Window的安装包,但MS基于redis发布了Windows版本. 下载链接: https://github.com/MS ...

  8. 洛谷P1294 高手去散步

    洛谷1294 高手去散步 题目背景 高手最近谈恋爱了.不过是单相思.“即使是单相思,也是完整的爱情”,高手从未放弃对它的追求.今天,这个阳光明媚的早晨,太阳从西边缓缓升起.于是它找到高手,希望在晨读开 ...

  9. Windows Azure 的虚拟硬盘和文件的相关概念

    虚拟硬盘和文件 在 Windows Azure 外部,虚拟硬盘可使用 VHD 或 VHDX 格式.它们还可以是固定的.动态扩展或差异的.Windows Azure 支持 VHD 格式的固定磁盘.固定格 ...

  10. javascript原型和原型继承

    每一个javascript对象(null除外)都和原型对象相关联,每一个对象都从原型对象继承属性. 所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过javascript代码Object.p ...