自定义卫星菜单CustomArcMenu
2015-11-18 16:13
295 查看
现在这种效果的开源项目,确实蛮多的。SatelliteMenu,ARCMenu都是。
下面是我自己仿照他们写的:
先交代一下具体步骤:
自定义卫星菜单:
1,自定义属性
a,自定义attr属性
b,xml布局文件中引用
c,CustomArcMenu中获取自定义属性值
2,onMesure测量自控件的大小
3,onLayout 布置自控件。
4,添加点击事件,添加主按钮与子菜单的按钮动画效果
a,主按钮的旋转动画
b,子菜单的旋转和平移动画
5,添加子菜单的点击事件
展示效果图:点击主按钮,子按钮弹射出去并且呈弧形展示,再次点击主按钮,子菜单按钮返回主按钮位置并消失,点击子菜单按钮,被点击子菜单按钮放大并消失
其他子菜单按钮缩小消失。下图是点击展开后的效果。
下面是具体的代码实现:
1,自定义属性
a,自定义attr属性
b,xml布局文件中引用
c,CustomArcMenu中获取自定义属性值
2,onMesure测量自控件的大小
3,onLayout 布置自控件。
4,添加点击事件,添加主按钮与子菜单的按钮动画效果
a,主按钮的旋转动画
b,子菜单的旋转和平移动画
5,添加子菜单的点击事件
类的下载
下面是我自己仿照他们写的:
先交代一下具体步骤:
自定义卫星菜单:
1,自定义属性
a,自定义attr属性
b,xml布局文件中引用
c,CustomArcMenu中获取自定义属性值
2,onMesure测量自控件的大小
3,onLayout 布置自控件。
4,添加点击事件,添加主按钮与子菜单的按钮动画效果
a,主按钮的旋转动画
b,子菜单的旋转和平移动画
5,添加子菜单的点击事件
展示效果图:点击主按钮,子按钮弹射出去并且呈弧形展示,再次点击主按钮,子菜单按钮返回主按钮位置并消失,点击子菜单按钮,被点击子菜单按钮放大并消失
其他子菜单按钮缩小消失。下图是点击展开后的效果。
下面是具体的代码实现:
1,自定义属性
a,自定义attr属性
<?xml version="1.0" encoding="utf-8"?> <resources> <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 name="CustomArcMenu"> <attr name="position"/> <attr name="radius"/> </declare-styleable> </resources>
b,xml布局文件中引用
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:wang="http://schemas.android.com/apk/res/com.wang.demo_android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/lv_content" android:layout_width="fill_parent" android:layout_height="fill_parent"></ListView> <com.wang.demo_android.view.CustomArcMenu android:id="@+id/custom_arcmenu" android:layout_width="match_parent" android:layout_height="match_parent" wang:radius="140dp" wang:position="left_bottom"> <ImageView android:id="@+id/arc_main" android:layout_width="48dp" android:layout_height="48dp" android:padding="5dp" android:src="@drawable/main"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:tag="search" android:src="@drawable/ic_search"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:tag="camera" android:src="@drawable/ic_camera"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:tag="notifaction" android:src="@drawable/ic_notifaction"/> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:tag="music" android:src="@drawable/ic_music"/> </com.wang.demo_android.view.CustomArcMenu> </FrameLayout>
c,CustomArcMenu中获取自定义属性值
public CustomArcMenu(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomArcMenu, 0, defStyleRes); int position = array.getInt(R.styleable.CustomArcMenu_position, POS_RIGHTT_BOTTOM); switch (position) { 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_RIGHTT_BOTTOM: mPosition = POSITION.RIGHT_BOTTOM; break; } // 设置半径的默认值:100dp mRadius = (int) array.getDimension(R.styleable.CustomArcMenu_radius, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources() .getDisplayMetrics())); Log.i(tag, "positon:" + mPosition + " radius:" + mRadius); array.recycle();// 回收 }
2,onMesure测量自控件的大小
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); for (int i = 0; i < count; i++) { // 测量child measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec); } super.onMeasure(widthMeasureSpec, heightMeasureSpec); }
3,onLayout 布置自控件。
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (changed) { // 定位主菜单按钮 layoutMainButton(); // 定位子菜单按钮 layoutMenuItem(); } } /** * 定位子菜单按钮 */ private void layoutMenuItem() { int count = getChildCount(); for (int i = 0; i < count - 1; i++) { View child = getChildAt(i + 1); int cl = (int) (mRadius * Math.sin(Math.PI / 2 / (count - 2) * i)); int ct = (int) (mRadius * Math.cos(Math.PI / 2 / (count - 2) * i)); // 右上和右下位置 if (mPosition == POSITION.RIGHT_TOP || mPosition == POSITION.RIGHT_BOTTOM) { cl = getMeasuredWidth() - cl - child.getMeasuredWidth(); } // 左下和右下位置 if (mPosition == POSITION.LEFT_BOTTOM || mPosition == POSITION.RIGHT_BOTTOM) { ct = getMeasuredHeight() - ct - child.getMeasuredHeight(); } child.layout(cl, ct, cl + child.getMeasuredWidth(), ct + child.getMeasuredHeight()); child.setVisibility(View.GONE); } } /** * 定位主菜单按钮 * * @param left * @param top * @param right * @param bottom */ private void layoutMainButton() { mMainButton = getChildAt(0); if (mMainButton == null) { return; } mMainButton.setOnClickListener(this); int l = 0; int t = 0; int width = mMainButton.getMeasuredWidth(); int height = mMainButton.getMeasuredHeight(); if (mPosition == POSITION.LEFT_BOTTOM || mPosition == POSITION.RIGHT_BOTTOM) { t = getMeasuredHeight() - height; } if (mPosition == POSITION.RIGHT_TOP || mPosition == POSITION.RIGHT_BOTTOM) { l = getMeasuredWidth() - width; } mMainButton.layout(l, t, l + width, t + height); }
4,添加点击事件,添加主按钮与子菜单的按钮动画效果
a,主按钮的旋转动画
@Override public void onClick(View v) { rotateMainButton(v, 0f, 360f, 300); toggleMenu(300); }
/** * 启动主按钮的动画效果 */ private void rotateMainButton(View v, float start, float end, int duration) { RotateAnimation rotateAnim = new RotateAnimation(start, end, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateAnim.setDuration(duration); rotateAnim.setFillAfter(true); v.startAnimation(rotateAnim); }
b,子菜单的旋转和平移动画
/** * 启动子菜单的动画 */ public void toggleMenu(int duration) { int count = getChildCount(); for (int i = 0; i < count - 1; i++) { final View child = getChildAt(i + 1); child.setVisibility(View.VISIBLE); 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); // 平移动画 TranslateAnimation translateAnim = null; if (mCurrentStatus == STATUS.CLOSE) { translateAnim = new TranslateAnimation(xFlag * cl, 0, yFlag * ct, 0); child.setClickable(true); child.setFocusable(true); } else { translateAnim = new TranslateAnimation(0, xFlag * cl, 0, yFlag * ct); child.setClickable(false); child.setFocusable(false); } translateAnim.setDuration(duration); translateAnim.setFillAfter(true); // 使得动画具有较明显的先后循序 translateAnim.setStartOffset(i * 100 / count); translateAnim.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { if (mCurrentStatus == STATUS.CLOSE) { child.setVisibility(View.GONE); } } }); // 旋转动画 RotateAnimation rotateAnim = new RotateAnimation(0, 720f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); rotateAnim.setDuration(duration); rotateAnim.setFillAfter(true); animSet.addAnimation(rotateAnim); animSet.addAnimation(translateAnim); // 开始动画 child.startAnimation(animSet); final int pos = i + 1; child.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOnMenuItemClickListener != null) { mOnMenuItemClickListener.OnClick(v, pos); } // 子菜单按钮的点击事件 menuItemAnim(pos); // 改变状态 changeStatus(); } }); } changeStatus(); }
5,添加子菜单的点击事件
/** * 添加item的点击动画 * * @param pos */ private void menuItemAnim(int pos) { int count = getChildCount(); for (int i = 0; i < count-1; i++) { View child = getChildAt(i + 1); if ((i + 1) == pos) { child.startAnimation(getScaleBig(300)); } else { child.startAnimation(getScaleSmall(300)); } child.setClickable(false); child.setFocusable(false); } } /** * 返回一个缩小的动画 * * @return */ private Animation getScaleSmall(int duration) { AnimationSet animSet = new AnimationSet(true); ScaleAnimation scaleAnim = new ScaleAnimation(1.0f, 0f, 1.0f, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); scaleAnim.setDuration(duration); scaleAnim.setFillAfter(true); AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0f); alphaAnim.setDuration(duration); alphaAnim.setFillAfter(true); animSet.addAnimation(alphaAnim); animSet.addAnimation(scaleAnim); animSet.setFillAfter(true); return animSet; } private Animation getScaleBig(int duration) { AnimationSet animSet = 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); scaleAnim.setDuration(duration); //scaleAnim.setFillAfter(true); AlphaAnimation alphaAnim = new AlphaAnimation(1.0f, 0f); alphaAnim.setDuration(duration); //alphaAnim.setFillAfter(true); animSet.addAnimation(alphaAnim); animSet.addAnimation(scaleAnim); animSet.setFillAfter(true); return animSet; } /** * 改变当前的状态 */ private void changeStatus() { mCurrentStatus = mCurrentStatus == STATUS.CLOSE ? STATUS.OPEN : STATUS.CLOSE; }注意item个数一定要大于等于三个,否则会有异常
类的下载
相关文章推荐
- html网页框架
- 对指定文件生成数字摘要的MD5工具类
- 将文件内容隐藏在bmp位图中
- 【GZOJ】1372——阶乘
- 黑马程序员——Java继承——多线程(二)
- FlyCapture2相机的连接和图像采集
- could not initialize proxy - no Session
- Object-c之id ,instancetype,SEL,_cmd;self,super几种类型的区别及用法
- 在网页上显示一个时时刷新的时钟,含有毫秒
- node.js学习笔记13—express视图
- ASP.NET Session丢失的情况
- oracle 基础命令整理
- obj文件格式与mtl文件格式
- Phalcon 调试组件(Debug Component)
- 基于etcd加saltstack的自动化扩容
- 哲学生活中必背的哲学原理
- tracef 安装 跟踪 函数调用图
- php连接oracle数据库转载
- 解决不同类型手机系统照相机拍照无法确定返回问题
- 《犯错的孩子值得表扬》