步骤:一:自定义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. [Unity3D] 和Unity3D的一些事

    Before 一开始知道Unty3D引擎,貌似还是大一或者大二,那时Unity3D还是收费软件,不像现在Personal Version免费. XNA|MonoGame 再后来,在Coursera上发 ...

  2. matlab中textread

    今天打算跑下程序,突然发现,真的很烂,不会读入数据,简单的Iris.txt一上午都没读进去,在此对matlab中的textread函数做下总结,textscan函数待续. 本文主要内容引自http:/ ...

  3. LINUX第五次实验报告

    北京电子科技学院(BESTI) 实     验    报     告 课程:信息安全系统设计基础                     班级: 201353 姓名:刘世鹏 郝爽 学号:2013530 ...

  4. 对于JAVA课程的期望

    对于JAVA课程的期望 我对于JAVA这门课程最初的了解可能来自于学长学姐的描述,或者是选课指南上简单的课程名称,那个时候的JAVA,对我来说遥远而又陌生,显得那么高大上,但是一转眼自己马上就要结束大 ...

  5. 安装包制作工具 SetupFactory使用2 API清单

    2014-11-19 SetupFactory中可以通过其API控制很复杂的业务需求. 下图中展示了其内置的API种类与具体分类函数.   序号 API名称 API说明 1 Application.E ...

  6. Java:静态代理 and 动态代理

    代理模式是常用的设计模式,其特征是代理类与委托类具有相同的接口,在具体实现上,有静态代理和动态代理之分.代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并 ...

  7. 服务发现:Zookeeper vs etcd vs Consul

    [编者的话]本文对比了Zookeeper.etcd和Consul三种服务发现工具,探讨了最佳的服务发现解决方案,仅供参考. 如果使用预定义的端口,服务越多,发生冲突的可能性越大,毕竟,不可能有两个服务 ...

  8. webSocket实现web及时聊天的例子

    概述 websocket目前虽然无法普及应用,未来是什么样子,我们不得而知,但现在开始学习应用它,只有好处没有坏处,本随笔的WebSocket是版本13(RFC6455)协议的实现,也是目前webso ...

  9. nth-child() 选择器

    给tabel tb1 td添加样式 #tb1 tr td:nth-child(2n+1) {/*奇数行样式*/ } #tb1 tr td:nth-child(2n) {/*偶数行样式*/ }

  10. [USACO2003][poj2138]Travel Games(dp/最长路)

    http://poj.org/problem?id=2138 题意:给你一些单词和初始单词,在初始单词的任意位置你可以加任意一个字母,使得这个新单词在给的单词中有所出现,然后在这样不断迭代下去,让你求 ...