卫星菜单
来源:互联网 发布:手机知乎怎么回答问题 编辑:程序博客网 时间:2024/04/29 11:18
转载请注明出处:http://blog.csdn.net/forwardyzk/article/details/44306855
下面就介绍一种菜单,卫星菜单(旋转菜单).按照下面的步骤来介绍其实现的方法。
卫星菜单
1.自定义类继承ViewGroup
2.自定义属性(在res/value/attr.xml中添加属性位置和半径 )
3.在View中获取这些自定义的属性
4.onMeasure测量子孩子
5.设置菜单主按钮的位置
6.设置子孩子的位置
7.给Item设置平移动画和旋转动画
8.点击Item设置点击事件并且设置缩放动画
9.测试使用
步骤1
-自定义类(MoonMenu)继承ViewGroup
public class MoonMenu extends ViewGroup {添加两个构造方法,其中一个必须含有设置属性的构造方法
public MoonMenu(Context context) { this(context, null); } public MoonMenu(Context context, AttributeSet attrs) { super(context, attrs);
--自定义属性(在res/value/attr.xml中添加属性位置和半径 )
定义属性,显示位置,使用枚举,有四个位置供选择,左上,左下,右上,右下。
半径大小,是一个旋转的弧形,设置一个半径的大小
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="MoonMenu"> <attr name="position"> <enum name="LEFT_TOP" value="0"></enum> <enum name="LEFT_BOTTOM" value="1"></enum> <enum name="RIGHT_TOP" value="2"></enum> <enum name="RIGHT_BOTTOM" value="3"></enum> </attr> <attr name="radius" format="dimension"></attr> </declare-styleable></resources>
步骤3.
--在View中获取这些自定义的属性
在调用出,要添加一个命名空间,xmlns:moon="http://schemas.android.com/apk/res-auto"
<com.example.menu.view.MoonMenu android:layout_width="wrap_content" android:layout_height="wrap_content" moon:position="LEFT_TOP" moon:radius="130dp"> </com.example.menu.view.MoonMenu>
在构造方法中获取设置的自定义的位置和半径属性
定义一个枚举位置
enum Position { LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM }
public MoonMenu(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; //获取自定义的属性 TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MoonMenu); int p = typedArray.getInt(R.styleable.MoonMenu_position, 3); if (p == 0) { position = Position.LEFT_TOP; } else if (p == 1) { position = Position.LEFT_BOTTOM; } else if (p == 2) { position = Position.RIGHT_TOP; } else if (p == 3) { position = Position.RIGHT_BOTTOM; } radius = (int) typedArray.getDimension(R.styleable.MoonMenu_radius, 100); }半径默认为100dp,位置默认右下角
步骤4
--测量子孩子的长和宽,供在onLayout方法中显示
菜单的主按钮和菜单Item都作为其MoonMenu菜单子孩子显示
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); for (int i = 0; i < count; i++) { measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
步骤5
--设置菜单主按钮的位置
现在控件中添加菜单主按钮到MoonMenu,作为第一个子孩子
<com.example.menu.view.MoonMenu android:layout_width="match_parent" android:layout_height="match_parent" moon:position="LEFT_TOP" moon:radius="130dp"> <!--菜单主按钮--> <RelativeLayout android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/main" /> </RelativeLayout> </com.example.menu.view.MoonMenu>
/** * 设置菜单的主按钮 */ private void layoutMainView() { mainView = getChildAt(0); //设置菜单主按钮的点击事件 mainView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { switchStaOffOn(v); } }); int sWidth = getMeasuredWidth(); int sHeight = getMeasuredHeight(); int l = 0; int t = 0; int width = mainView.getMeasuredWidth(); int height = mainView.getMeasuredHeight(); switch (position) { case LEFT_TOP: mainView.layout(l, t, width, height); break; case LEFT_BOTTOM: mainView.layout(l, sHeight - height, width, sHeight); break; case RIGHT_TOP: mainView.layout(sWidth - width, t, sWidth, height); break; case RIGHT_BOTTOM: mainView.layout(sWidth - width, sHeight - height, sWidth, sHeight); break; } }
通过获取菜单的长和宽,子孩子的长和宽,确定View显示的坐标,使用layout显示的界面上。
以左上角的位置作为例子。
l=0:childView左边相对于父View的坐标
t=0:childView顶部边相对于父View的坐标
r=childView的宽度:childView右边相对于父View的坐标
b=childView的高度:childView底部边相对于父View的坐标
mainView.layout(l, t, r, b);
步骤6
---设置子孩子MenuItem的位置
先一下一张图
MenuItem可以有半径(R),角度(θ)确定两个距离,这样在获取childView的宽和高,Menu的长和宽,就可以确定MenItem相对父View的坐标
R:是设置Menu时,传进来的。
θ:由MenuItem 的个数确定,例如有三个MenuItem,单位的角度就是:α=Math.PI / 2 / (3 - 2),为什么-2呢,第一去掉主按钮,第二去掉MenuItem-1.那三个MenuItem的计算的角度分别为:0*α,1*α,2*α
那么设置显示位置也是用layout(l,t,r,b)
/** * 设置子菜单Item位置 */ private void layoutChildsItem() { int childCount = getChildCount(); double angleUnit = Math.PI / 2 / (childCount - 2); for (int i = 0; i < childCount - 1; i++) { View childView = getChildAt(i + 1); final int clickPosition = i + 1; //默认的是显示 int xl = (int) (radius * Math.sin(angleUnit * i)); int yl = (int) (radius * Math.cos(angleUnit * i)); int childHeight = childView.getMeasuredHeight(); int childWidth = childView.getMeasuredWidth(); int screenWidth = getMeasuredWidth(); int screenHeight = getMeasuredHeight(); switch (position) { case LEFT_TOP: childView.layout(xl, yl, xl + childWidth, yl + childHeight); break; case LEFT_BOTTOM: childView.layout(xl, screenHeight - yl - childHeight, xl + childWidth, screenHeight - yl); break; case RIGHT_TOP: childView.layout(screenWidth - xl - childWidth, yl, screenWidth - xl, yl + childHeight); break; case RIGHT_BOTTOM: childView.layout(screenWidth - xl - childWidth, screenHeight - yl - childHeight, screenWidth - xl, screenHeight - yl); break; } childView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (onMenuItemClickListener != null) { onMenuItemClickListener.onClickMenuItem(v, clickPosition); } onMenuItemClickAnimation(v); } }); } }
步骤7
---给MainMenuItem设置点击事件和给Item设置平移动画和旋转动画
主菜单按钮旋转动画,其他的ChildItem平移动画和旋转动画
切换菜单的开关状态
在layoutMainView()方法中,给第一个ChildItem(菜单主按钮)设置点击事件
mainView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { switchStaOffOn(v); } });
/** * 主菜单点击事件执行的操作 * * @param mainView */ private void switchStaOffOn(View mainView) { mainAnim(mainView, DURATION); childItemAnim(DURATION); changeStatus(); }
/** * 菜单主按钮设置旋转动画 * * @param mainView */ private void mainAnim(View mainView, int duration) { RotateAnimation roateAn = new RotateAnimation(0, 360, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); roateAn.setFillAfter(true); roateAn.setDuration(duration); mainView.setAnimation(roateAn); mainView.startAnimation(roateAn); }当点击了主按钮时,主菜单按钮旋转360度。
给其他的ChildItem要设置平移动画和旋转动画
其坐标都是相对与父View作为参考物
当菜单展开时:
对于LEFT_BOTTOM,LEFT_TOP位置的菜单,当平移的时候,向左移动,X坐标变小(与移动前X坐标比较)
对于RIGHT_TOP,LEFT_TOP位置的菜单, 当平移的时候,向上移动,Y坐标变小(与移动前Y坐标比较)
为X坐标和Y坐标大小变化增加标记,xFlag和yFlag,默认为1,表示变大,-1表示变小
当菜单打开的时候,X和Y坐标都是(0,0)--->>(xFlag * xl,yFlag * yl)
当菜单关闭的时候,X和Y的坐标是(xFlag * xl,yFlag * yl)-->>0,0)--->>
给Item增加旋转动画
相对于自己中心位置旋转,0-->720,旋转两圈
ChildMenuItem设置了动画的开始执行的间隔时间,这样的效果更好,感觉就是按顺序展开是关闭。setStartOffset(开始延迟时间执行)
/** * 给Item设置展开与收回的动画 * * @param duration */ private void childItemAnim(long duration) { int xFlag = 1; int yFlag = 1; if (position == Position.LEFT_BOTTOM || position == Position.LEFT_TOP) { xFlag = -1; } if (position == Position.RIGHT_TOP || position == Position.LEFT_TOP) { yFlag = -1; } int childCount = getChildCount(); double angleUnit = Math.PI / 2 / (childCount - 2); for (int i = 0; i < childCount - 1; i++) { final View childView = getChildAt(i + 1); childView.setVisibility(View.VISIBLE); int xl = (int) (radius * Math.sin(angleUnit * i)); int yl = (int) (radius * Math.cos(angleUnit * i)); AnimationSet anSet = new AnimationSet(true); //平移动画 TranslateAnimation translateAnim = null; //旋转动画 RotateAnimation rotateAni = null; if (switchStatus == Status.OPEN) { translateAnim = new TranslateAnimation(0, xFlag * xl, 0, yFlag * yl); childView.setClickable(false); childView.setFocusable(false); } else if (switchStatus == Status.CLOSE) { translateAnim = new TranslateAnimation(xFlag * xl, 0, yFlag * yl, 0); childView.setClickable(true); childView.setFocusable(true); } translateAnim.setFillAfter(true); translateAnim.setDuration(duration); rotateAni = new RotateAnimation(0, 720, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); rotateAni.setFillAfter(true); rotateAni.setDuration(duration); //添加动画到集合 translateAnim.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { if (switchStatus == Status.CLOSE) { childView.setVisibility(View.GONE); } } @Override public void onAnimationRepeat(Animation animation) { } }); anSet.addAnimation(rotateAni); anSet.addAnimation(translateAnim); anSet.setStartOffset(i * 100 / childCount); childView.startAnimation(anSet); } }
当动画结束的时候,如果此时菜单的状态为关闭状态,那么ChildMenuItem,设置为不可显示,
最后改变菜单开和关状态
/** * 改变开关的状态 */ private void changeStatus() { switchStatus = (switchStatus == Status.OPEN ? Status.CLOSE : Status.OPEN); }
/** * 判断当前的Menu是否是开的状态 * * @return */ public boolean isOpen() { return switchStatus == Status.OPEN; } /** * Menu从开---关的ChildMenuItem动画效果,并且要改变Menu的状态 */ public void toClose() { childItemAnim(DURATION); changeStatus(); }
步骤8
---点击Item设置点击事件并且设置缩放动画
当点击Item的时候,点击的ChildMenuItem扩大,其他的ChildMenuItem缩小,同时透明度变小
在layoutChildsItem()中给ChildMenuItem设置点击事件
childView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (onMenuItemClickListener != null) { onMenuItemClickListener.onClickMenuItem(v, clickPosition); } onMenuItemClickAnimation(v); } });
/** * 点击菜单Item的动画,点击的Item放大,其他的缩小,并且透明度变低 * * @param v */ private void onMenuItemClickAnimation(View v) { int childCount = getChildCount(); for (int i = 0; i < childCount - 1; i++) { View childView = getChildAt(i + 1); if (childView == v) { childView.startAnimation(menuItemBigAnimation()); } else { childView.startAnimation(menuItemSmallAnimation()); } childView.setClickable(false); childView.setFocusable(false); if (isOpen()) { changeStatus(); } } }
/** * 点击Item后,Item变大和透明度变大的动画 * * @return */ private Animation menuItemBigAnimation() { AlphaAnimation alphaAnima = new AlphaAnimation(1.0f, 0.0f); //点击的item动画 ScaleAnimation scaleAnimaClick = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f); AnimationSet clickSet = new AnimationSet(true); clickSet.addAnimation(scaleAnimaClick); clickSet.addAnimation(alphaAnima); clickSet.setFillAfter(true); clickSet.setDuration(300); return clickSet; } /** * 点击Item后,Item变小和透明度变小的动画 * * @return */ private Animation menuItemSmallAnimation() { AlphaAnimation alphaAnima = new AlphaAnimation(1.0f, 0.0f); alphaAnima.setDuration(300); ScaleAnimation scaleAnima = new ScaleAnimation(1.0f, 0.0f, 0.0f, 0.0f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f, ScaleAnimation.RELATIVE_TO_SELF, 0.5f); scaleAnima.setDuration(300); AnimationSet set = new AnimationSet(true); set.addAnimation(scaleAnima); set.addAnimation(alphaAnima); set.setFillAfter(true); set.setDuration(300); return set; }
定义点击事件的回调接口,
/** * 设置点击Item的事件监听 * * @param onMenuItemClickListener */ public void setOnMenuItemClickListener(OnMenuItemClickListener onMenuItemClickListener) { this.onMenuItemClickListener = onMenuItemClickListener; } /** * 点击Item的监听事件 */ public interface OnMenuItemClickListener { void onClickMenuItem(View itemView, int position); } private OnMenuItemClickListener onMenuItemClickListener;
当然对于动画效果,是写在了当前的空间中,不需要在回调接口中使用,当点击了ChildMenuItem后,执行的操作在回调接口中操作。
参数:点击的当前的ChildMenuView和当前ChildMenuView位置。
步骤9
---测试使用
在一个Activity中,有一个菜单和ListView展示,默认的都展示出来
test_activity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:moon="http://schemas.android.com/apk/res-auto" 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="wrap_content"></ListView> <!--右下角--> <com.example.menu.view.MoonMenu android:id="@+id/moonmenu" android:layout_width="match_parent" android:layout_height="match_parent" moon:position="RIGHT_BOTTOM" moon:radius="150dp"> <RelativeLayout android:id="@+id/main_menu" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:layout_centerInParent="true" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/main" /> </RelativeLayout> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_1" android:tag="1" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_2" android:tag="2" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_3" android:tag="3" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_4" android:tag="4" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_5" android:tag="5" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_6" android:tag="6" /> </com.example.menu.view.MoonMenu></RelativeLayout>
TestAcitivity.java
public class TestActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_activity); initView(); } private void initView() { final MoonMenu moonMenu = (MoonMenu) findViewById(R.id.moonmenu); moonMenu.setOnMenuItemClickListener(new MoonMenu.OnMenuItemClickListener() { @Override public void onClickMenuItem(View itemView, int position) { //可以使用标记来判断点击的Viwew,当然必须给ChildViewItem设置了Tag if ("1".equals(itemView.getTag())) { Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); } else if ("2".equals(itemView.getTag())) { Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); } else if ("3".equals(itemView.getTag())) { Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); } else if ("4".equals(itemView.getTag())) { Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); } else if ("5".equals(itemView.getTag())) { Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); } else if ("6".equals(itemView.getTag())) { Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); } //可以使用position来判断 switch (position) { case 1: Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); break; case 2: Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); break; case 3: Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); break; case 4: Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); break; case 5: Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); break; case 6: Toast.makeText(getApplicationContext(), "点击了" + position, Toast.LENGTH_SHORT).show(); break; } } }); //展示ListView,并且设置适配器和Item点击事件,滚动监听 ListView listview = (ListView) findViewById(R.id.listview); String[] items = {"item0", "item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9", "item10", "item11", "item12", "item13", "item14", "item15", "item16", "item17", "item18", "item19", "item20"}; ArrayAdapter<String> adapter = new ArrayAdapter<String>(TestActivity.this, android.R.layout.simple_list_item_activated_1, items); listview.setAdapter(adapter); listview.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (moonMenu.isOpen()) { moonMenu.toClose(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { } }); listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (moonMenu.isOpen()) { moonMenu.toClose(); } } }); }}
当ListView滚动的时候,如果当前的Menu是开的状态,那么就进行关闭的动画。
判断点击的ChildMenuItem,可以使用Tag(必须在xml文件中设置了Tag),或者使用position来判断。
效果图:
源码下载: http://download.csdn.net/detail/forwardyzk/8506243
- 卫星菜单
- 卫星菜单
- 自定义旋转卫星菜单
- Andriod实现卫星菜单
- 卫星式菜单
- 自定义卫星菜单CustomArcMenu
- 仿卫星菜单
- 卫星菜单效果实现
- 动画之卫星菜单
- Android卫星菜单
- 卫星式菜单的总结。
- 传统动画实现卫星菜单
- 属性动画实现卫星菜单
- Android自动义卫星菜单
- 自定义ViewGroup之卫星菜单
- 属性动画实现卫星菜单
- Android卫星菜单的实现
- Android自定义卫星弧度菜单
- flume log4j日志接收
- 第二周项目1
- android WheelView自定义
- Maven Pom.xml文件详细解释
- 香港鉴定胎儿性别流程
- 卫星菜单
- Disruptor 详解
- 二叉树的创建及其遍历
- 安装cloudera
- ios 收藏app内容或网页到手机桌面
- 查看mysql访问记录
- Qt课程表软件
- 关于TP框架中的jQuery ajax POST使用U方法
- phoenix on cloudera