步骤:一:自定义ViewGroup

1、自定义属性

a、attr.xml

b、在布局文件中使用activity_main.xml

c、在自定义控件中进行读取

2、onMeasure

3、onLayout

4、设置主按钮的旋转动画

为menuItem添加平移动画和旋转动画

实现menuItem的点击事件

MainActivity.java

public class MainActivity extends AppCompatActivity {
private ListView listView;
private List<String> mData;
private ArrayAdapter mAdapter; private ArcMenuActivity mArc; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); listView = (ListView) findViewById(R.id.listview);
mArc = (ArcMenuActivity) findViewById(R.id.view_arc); addData();
mAdapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, mData);
listView.setAdapter(mAdapter); listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) { } @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (mArc.isOpen()) {
mArc.toggleMenu(200);
}
}
}); mArc.setOnMenuItemClickListener(new ArcMenuActivity.onMenuItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(MainActivity.this,position+" : "+view.getTag(),Toast.LENGTH_SHORT).show();
}
});
} private void addData() {
mData = new ArrayList<String>();
for (int i = 'A'; i <= 'z'; i++) {
mData.add((char) i + "");
}
} }

ArcMenuActivity.java

/**
* 自定义的ViewGroup,在activity_main.xml里面调用
*/
public class ArcMenuActivity extends ViewGroup {
private static final int POS_LEFT_TOP = 0;
private static final int POS_RIGHT_TOP = 1;
private static final int POS_LEFT_BOTTOM = 2;
private static final int POS_RIGHT_BOTTOM = 3; private Position mPosition = Position.RIGHT_BOTTOM;
private Status mCurrentStatus = Status.CLOSE;
private onMenuItemClickListener onMenuItemClick;
private View mCButton; //主菜单按钮
private int mRadius; //卫星半径 //卫星菜单位置枚举类
public enum Position {
LEFT_TOP, RIGHT_TOP, LEFT_BOTTOM, RIGHT_BOTTOM
} //主菜单的状态
public enum Status {
OPEN, CLOSE
} //定义一个点击点击子菜单项的回调接口
public interface onMenuItemClickListener {
void onItemClick(View view, int position);
} //自定义的点击方法
public void setOnMenuItemClickListener(onMenuItemClickListener onMenuItemClick) {
this.onMenuItemClick = onMenuItemClick;
} public ArcMenuActivity(Context context) {
// super(context);
this(context, null);
} public ArcMenuActivity(Context context, AttributeSet attrs) {
// super(context, attrs);
this(context, attrs, 0);
} public ArcMenuActivity(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); //获取自定义属性的值
TypedArray ta = context.getTheme().obtainStyledAttributes(
attrs, R.styleable.ArcMenu, defStyleAttr, 0); //getInt()方法
//参数1:所需要赋予给pos的值
// 参数2:如果参数1无值,则取该值,就是custom:position="right_bottom"没有定义时
int pos = ta.getInt(R.styleable.ArcMenu_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) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics()); //getDimension()方法
//参数1:所需要赋予给radius的值
// 参数2:如果参数1无值,则取该值,就是custom:radius="100"没有定义时
mRadius = (int) ta.getDimension(R.styleable.ArcMenu_radius, TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics())); //radius输出的值为px
Log.v("TGA", "position = " + mPosition + ",radius = " + mRadius); ta.recycle();
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取activity_main.xml里面的View控件的个数
// <my.com.example.x550v.view.ArcMenuActivity/>里面的
int count = getChildCount();
//测量child
for (int i = 0; i < count; i++) {
//xml文件里面控件的位置,宽,高
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) {
//主菜单按钮
layoutCButton();
//子菜单按钮
subItemButton();
}
} //定义主菜单按钮
private void layoutCButton() {
//或者使用findViewById()的方法
mCButton = getChildAt(0); //获取第一个xml文件里面的第一个View控件
mCButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//主菜单动画
//这里得使用getContext()来获取context
Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.anim);
//如果使用setAnimation()只会转一次
mCButton.startAnimation(animation); //子菜单动画
toggleMenu(200);
}
}); //l为主菜单距离父布局的左边距离,t为主菜单距离父布局的顶边距离
int l = 0, t = 0;
//获取主按钮的宽和高
int width = mCButton.getMeasuredWidth();
int height = mCButton.getMeasuredHeight(); switch (mPosition) {
case LEFT_TOP:
//0,0表示坐上角的位置
l = 0;
t = 0;
break;
case RIGHT_TOP:
l = getMeasuredWidth() - width; //getMeasuredWidth()取得容器的宽度
t = 0;
break;
case LEFT_BOTTOM:
l = 0;
t = getMeasuredHeight() - height; //getMeasuredHeight()取得容器的高度
break;
case RIGHT_BOTTOM:
l = getMeasuredWidth() - width;
t = getMeasuredHeight() - height;
break;
}
mCButton.layout(l, t, l + width, t + height);
} //定义子菜单按钮
public void subItemButton() {
//获取activity_main.xml里面的View控件的个数
// <my.com.example.x550v.view.ArcMenuActivity/>里面的
int count = getChildCount(); for (int i = 0; i < count - 1; i++) { //去掉主菜单按钮的一个
View child = getChildAt(i + 1); //从第一个子菜单开始获取,而不是主菜单 //开始时设置子菜单为隐藏
child.setVisibility(GONE); //当子菜单为左上角时,cl为子菜单距离父布局的左边距离,ct为子菜单距离父布局的顶边距离
//当子菜单为右上角时,cl为子菜单距离父布局的右边距离,ct为子菜单距离父布局的顶边距离
//当子菜单为左下角时,cl为子菜单距离父布局的左边距离,ct为子菜单距离父布局的底边距离
//当子菜单为右下角时,cl为子菜单距离父布局的右边距离,ct为子菜单距离父布局的底边距离
//Math.PI的值为圆周率pai,角度为180度
//Math.PI / 2 / (count - 2)是取出平均角,
//*i是看子菜单拥有几个平均角
int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i));
// Log.v("TAG","cl = "+cl);
int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i)); //获取子菜单View的宽和高
int cWidth = child.getMeasuredWidth();
int cHeight = child.getMeasuredHeight(); //如果子菜单在左下,右下
if (mPosition == Position.LEFT_BOTTOM || mPosition == Position.RIGHT_BOTTOM) {
ct = getMeasuredHeight() - cHeight - ct;
Log.v("TAG","getMeasuredHeight()"+getMeasuredHeight());
}
//如果子菜单在右上,右下
if (mPosition == Position.RIGHT_TOP || mPosition == Position.RIGHT_BOTTOM) {
cl = getMeasuredWidth() - cWidth - cl;
}
child.layout(cl, ct, cl + cWidth, ct + cHeight);
}
} //定义点击主菜单后子菜单出现动画
public void toggleMenu(int duration) {
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;
} final AnimationSet animationSet = new AnimationSet(true); //位移动画
Animation tranAnim = null;
//如果为关闭状态,点击后会散开
if (mCurrentStatus == Status.CLOSE) {
//子菜单一开始就是扇形排布,其坐标为(0,0)
tranAnim = new TranslateAnimation(xFlag * cl, 0, yFlag * ct, 0);
childView.setClickable(true);
childView.setFocusable(true);
}
//否则为打开状态,点击后会收缩
else {
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 Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) { } @Override
public void onAnimationEnd(Animation animation) {
if (mCurrentStatus == Status.CLOSE) {
childView.setVisibility(GONE);
}
} @Override
public void onAnimationRepeat(Animation animation) { }
}); //旋转动画
RotateAnimation rotateAnim = new RotateAnimation(
0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotateAnim.setFillAfter(true);
rotateAnim.setDuration(duration); //先旋转,再位移
//先位移,再旋转更炫
// animationSet.addAnimation(tranAnim);
animationSet.addAnimation(rotateAnim);
animationSet.addAnimation(tranAnim);
childView.startAnimation(animationSet); final int pos = i + 1;
//子菜单的点击监听
childView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onMenuItemClick != null) {
onMenuItemClick.onItemClick(childView, pos);
}
menuItemAnim(pos - 1); //子菜单点击动画
changeStatus();
}
});
}
//切换菜单状态,在for()循环之外
changeStatus();
} //切换菜单状态
private void changeStatus() {
mCurrentStatus = (mCurrentStatus == Status.CLOSE ? Status.OPEN : Status.CLOSE);
} //添加子菜单点击动画
private void menuItemAnim(int pos) {
for (int i = 0; i < getChildCount() - 1; i++) {
View childView = getChildAt(i + 1);
if (i == pos) {
childView.startAnimation(scaleBigAnimation(300));
} else {
childView.startAnimation(scaleSmallAnimation(300));
}
childView.setClickable(false);
childView.setFocusable(false);
}
} //子菜单变大,变小动画
private Animation scaleBigAnimation(int duration) {
AnimationSet set = new AnimationSet(true);
Animation scaleAnim = new ScaleAnimation(1, 2, 1, 2, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
Animation alphaAnim = new AlphaAnimation(1, 0);
set.setDuration(duration);
set.addAnimation(scaleAnim);
set.addAnimation(alphaAnim);
set.setFillAfter(true);
return set;
} private Animation scaleSmallAnimation(int duration) {
AnimationSet set = new AnimationSet(true);
Animation scaleAnim = new ScaleAnimation(1, 0, 1, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
Animation alphaAnim = new AlphaAnimation(1, 0);
set.setDuration(duration);
set.addAnimation(scaleAnim);
set.addAnimation(alphaAnim);
set.setFillAfter(true);
return set;
} public boolean isOpen(){
return mCurrentStatus == Status.OPEN;
} }

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"> <ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"/> <my.com.example.x550v.view.ArcMenuActivity
android:id="@+id/view.arc"
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:position="right_bottom"
custom:radius="200dp"> <RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/composer_button"> <ImageView
android:id="@+id/iv_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:src="@drawable/composer_icn_plus" />
</RelativeLayout> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_camera"
android:tag="Camera" /> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_music"
android:tag="Music" /> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_place"
android:tag="Place" /> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_sleep"
android:tag="Sleep" /> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_thought"
android:tag="Thought" /> <ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/composer_with"
android:tag="With" /> </my.com.example.x550v.view.ArcMenuActivity> </RelativeLayout>

attr.xml

<resources>
<attr name="position">
<enum name="left_top" value="0" />
<enum name="right_top" value="1" />
<enum name="left_bottom" value="2" />
<enum name="right_bottom" value="3" />
</attr>
<attr name="radius" format="dimension" /> <declare-styleable name="ArcMenu">
<attr name="position" />
<attr name="radius" />
</declare-styleable> </resources>

anim.xml

<set
xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="500"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="true"/>
</set>

运行效果:

Android 实现卫星菜单的更多相关文章

  1. Android之卫星菜单的实现

    卫星菜单是现在一个非常受欢迎的“控件”,很多Android程序员都趋之若鹜,预览如下图.传统的卫星菜单是用Animation实现的,需要大量的代码,而且算法极多,一不小心就要通宵Debug.本帖贴出用 ...

  2. Android 实现卫星菜单(精简版)

    MainActivity.java public class MainActivity extends AppCompatActivity { private ArcDemo mArc; privat ...

  3. android 实现自定义卫星菜单

    看了hyman老师的视频,听起来有点迷糊,所以就想把实现卫星菜单的实现总结一下.长话短说,下面总结一下: 一.自定义ViewGroup1).自定义属性文件 属性的定义: <attr name=& ...

  4. Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9671609 记得在很早之前,我写了一篇关于Android滑动菜单的文章,其中有一个 ...

  5. Android 的上下文菜单: Context Menu,registerForContextMenu(getListView())

    概述: Android 的上下文菜单类似于 PC 上的右键菜单.当为一个视图注册了上下文菜单之后,长按(2 秒左右)这个视图对象就会弹出一个浮动菜单,即上下文菜单.任何视图都可以注册上下文菜单,不过, ...

  6. android中常用菜单(menu)的基本知识

    (一)选项菜单 1.简单的创建菜单: @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMe ...

  7. Android SlidingMenu侧滑菜单使用

    把下载的侧滑菜单压缩包打开,会有一个library文件夹,在eclipse中import existing android code into workspace,导入library文件夹,并且选择作 ...

  8. 【转】Android双向滑动菜单完全解析,教你如何一分钟实现双向滑动特效

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9671609 记得在很早之前,我写了一篇关于Android滑动菜单的文章,其中有一个 ...

  9. Android:有关菜单的学习(供自己参考)

    Android:有关==菜单==的学习 上下文菜单 上下文菜单就是手机中对某一项进行==点击一定时间==后弹出的针对该项处理的菜单. context_menu.xml: <?xml versio ...

随机推荐

  1. MyEclipse无法启动调试:Cannot connect to VM

    MyEclipse无法启动调试:Cannot connect to VM 问题描述:Eclipse普通的Run模式没有问题,Debug模式却启动不了.换了Eclipse,MyEclipse,JDK都不 ...

  2. ILMerge 简单应用

    ILMerge是合并.net的assembly的工具,最新版的支持.net 4.0的ILmerge下载: http://www.microsoft.com/downloads/details.aspx ...

  3. ASP.NET错误处理的方式(总结)

    转载至: http://www.cnblogs.com/chinhr/archive/2007/06/26/795947.html ASP.NET错误处理的方式(整理&总结)英文文章研究:ht ...

  4. js控制只能输入数字和小数点

    非常好用,代码示例如下: <input  onkeypress = "return event.keyCode>=48&&event.keyCode<=57 ...

  5. WPF Command Binding

    <Window x:Class="WpfTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/200 ...

  6. Android中的异步网络请求

    本篇文章我们来一起写一个最基本的Android异步网络请求框架,借此来了解下Android中网络请求的相关姿势.由于个人水平有限,文中难免存在疏忽和谬误,希望大家可以指出,谢谢大家:) 1. 同步网络 ...

  7. node 学习笔记 - fs 文件操作

    本文同步自我的个人博客:http://www.52cik.com/2015/12/03/learn-node-fs.html 最近看到群里不少大神都开始玩 node 了,我感觉跟他们步伐越来越大了, ...

  8. 开源搜索引擎Iveely 0.8.0发布,终见天日

    这是一篇博客,不是,这是一篇开源人的心酸和喜悦,没有人可以理解我们的心情,一路的辛酸一路的艰辛,不过还好,在大家的支持下,总算是终见天日,谢谢那些给予我们无私帮助的朋友.您的支持,依然是我们无限的动力 ...

  9. I belonged to you

    小葫芦,你就像山间清爽的风,犹如古城温暖的光,在我的旅途中陪伴着我. 我想牵着你的手,踏遍万水千山,赏遍美景风光,春观夜樱,夏望繁星,秋赏满月,冬会初雪. 直到两鬓斑白,一起坐在火炉旁,给孩子们讲故事 ...

  10. 20步打造最安全的NGINX WEB服务器

    Nginx 是一个轻量级的,高性能的Web服务器以及反向代理和邮箱(IMAP/POP3)代理服务器.它运行在UNIX,GNU /linux,BSD 各种版本,Mac OS X,Solaris和Wind ...