Android圆形旋转菜单开发实例
作者:ywl5320 发布时间:2023-09-06 11:42:52
最近帮朋友做了一个动画菜单,感觉有一定的实用价值,就在此给大家分享一下,先看看效果:
实现思路:
从图中可以看出,这三个(或更多,需要自己再实现)菜单是围绕着中心点旋转的,旋转分为2层,背景旋转和菜单旋转,背景旋转可以直接用旋转动画来实现;菜单的旋转是在以中心点为圆心的圆环上,所以这里用了根据旋转角度求此点在直角坐标系中的坐标点的函数(x = r * cos(rotation* 3.14 / 180) 和y = r * sin(rotation* 3.14 / 180) ),然后根据获取到的点的位置来设置菜单的位置就能实现这种效果。由此可见 数学是很重要的 哈哈~~
有了思路我们就能用代码来实现了:
1、首先自定义View继承相对布局并重写构造函数
/**
* Created by ywl on 2016/8/7.
*/
public class CircleMenuLayout extends RelativeLayout {
public CircleMenuLayout(Context context) {
this(context, null);
}
public CircleMenuLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 初始化布局 把旋转背景和中心点添加进去
* @param context
* @param attrs
* @param defStyleAttr
*/
public CircleMenuLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
layoutInflater = LayoutInflater.from(context);
menuitems = new ArrayList<View>();
centerview = new View(context);//中心点
centerview.setId(ID_CENTER_VIEW);
LayoutParams lp = new LayoutParams(0, 0);
lp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
addView(centerview, lp); //添加中心的 用于旋转定位
progressBar = new ProgressBar(context);//旋转的背景
LayoutParams lp2 = new LayoutParams(dip2px(context, 90), dip2px(context, 90));
lp2.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
addView(progressBar, lp2);
progressBar.setIndeterminateDrawable(context.getResources().getDrawable(R.mipmap.icon_circle_menu));
}
}
构造函数中添加中心定位点和旋转背景图片,并设置合适的大小。
2、根据传入的图片数组和菜单名字数组,生成菜单原始位置效果。
/**
* 菜单的数量 和 半径 名字 和图片 这里只为3个菜单做了适配
* @param size
* @param center_distance
*/
public void initMenuItem(int size, int center_distance, String[] titles, int[] imgs)
{
radus = 360f / size;
int width = dip2px(context, 50); //菜单宽度
int height = dip2px(context, 50);//菜单高度
for(int i = 0; i < size; i++) //循环添加布局
{
int top = 0;
int left = 0;
top = -(int)(Math.sin(radus * i * 3.1415f / 180) * center_distance); //r * cos(ao * 3.14 /180 )
left = -(int)(Math.cos(radus * i * 3.1415f / 180) * center_distance); //计算位置点
LayoutParams lp = new LayoutParams(dip2px(context, 50), dip2px(context, 50));
View view = layoutInflater.inflate(R.layout.item_circle_menu, this, false);
view.setTag(i);
TextView tvname = (TextView) view.findViewById(R.id.tv_name);
ImageView ivimg = (ImageView) view.findViewById(R.id.img);
tvname.setText(titles[i]);
ivimg.setImageResource(imgs[i]);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {//根据点击的区域 旋转菜单
if(!isrun) {
tag = (int) v.getTag();
currentPosition = tag;
if(tag == 0)
{
finishdus = -360;
}
else if(tag == 1)
{
finishdus = -120;
}
else if(tag == 2)
{
finishdus = -240;
}
LayoutParams lp = (LayoutParams) v.getLayoutParams();
int l = lp.leftMargin;
int t = lp.topMargin;
if (t > -dip2px(context, 5) && l > -dip2px(context, 5)) {
oldradus = 120f;
isright = false;
} else if (t > -dip2px(context, 5) && l < -dip2px(context, 5)) {
oldradus = 120f;
isright = true;
} else if (t < -dip2px(context, 5)) {
oldradus = 0f;
}
sub = 0;
circleMenu(8, dip2px(context, 45), oldradus, isright);
}
}
});
lp.addRule(RelativeLayout.BELOW, centerview.getId());
lp.addRule(RelativeLayout.RIGHT_OF, centerview.getId());
lp.setMargins(-width / 2 + top, -height / 2 + left, 0, 0);
addView(view, lp);
menuitems.add(view);
}
handler.postDelayed(runnable, 0);
}
根据菜单的数量循环计算每个菜单的位置,然后在相应的位置添加相应的菜单就可以实现菜单的初始化了。这里为每个菜单添加了点击事件,但是只适配了3个菜单的情况,至于其他数量的菜单,可以自己来改或者写一个通用的方法来计算点击位置。
3、背景旋转动画:
/**
* 根据度数来旋转菜单 菜单中心都在一个圆上面 采用圆周运动来旋转
* @param offserradius
* @param center_distance
* @param d
* @param right
*/
public void circleMenu(float offserradius, int center_distance, float d, boolean right)
{
if(oldradus != 0)
{
progressBar.clearAnimation();
if(isright)
{
mRotateUpAnim = new RotateAnimation(bgdus, bgdus + 120,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
bgdus += 120;
}
else
{
mRotateUpAnim = new RotateAnimation(bgdus, bgdus - 120,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
bgdus -= 120;
}
lir = new LinearInterpolator();
mRotateUpAnim.setDuration(350);
mRotateUpAnim.setFillAfter(true);
mRotateUpAnim.setInterpolator(lir);
// mRotateUpAnim.setRepeatCount(Animation.INFINITE);
progressBar.startAnimation(mRotateUpAnim);
}
circleMenuItem(offserradius, center_distance, d, right);
}
这个比较简单,就是根据旋转的角度,启用旋转动画。
4、旋转菜单:
/**
* 菜单旋转
* @param offserradius
* @param center_distance
* @param d
* @param right
*/
public void circleMenuItem(float offserradius, int center_distance, float d, boolean right)
{
sub += offserradius;
if(sub > d)
{
if(onMenuItemSelectedListener != null)
{
onMenuItemSelectedListener.onMenuItemOnclick(tag);
}
isrun = false;
return;
}
if(right) {
offsetradus -= offserradius;
}
else
{
offsetradus += offserradius;
}
int size = menuitems.size();
int width = dip2px(context, 50);
int height = dip2px(context, 50);
for(int i = 0; i < size; i++)
{
if(Math.abs(sub - d) <= 8)
{
offsetradus = finishdus;
}
LayoutParams lp = (LayoutParams) menuitems.get(i).getLayoutParams();
float ds = radus * i + offsetradus;
int top = -(int)(Math.sin(ds * 3.1415f / 180) * center_distance); //r * cos(ao * 3.14 /180 )
int left = -(int)(Math.cos(ds * 3.1415f / 180) * center_distance);
lp.setMargins(-width / 2 + top, -height / 2 + left, 0, 0);
menuitems.get(i).requestLayout();
}
if(sub <= d) {
isrun = true;
offsetradus = offsetradus % 360;
handler.postDelayed(runnable, 5);
}
else
{
if(onMenuItemSelectedListener != null)
{
onMenuItemSelectedListener.onMenuItemOnclick(tag);
}
isrun = false;
}
}
这里旋转是根据初始化时每个菜单所在的位置来求的旋转角度,然后启动handler来动递加或递减角度来求响应的位置,就实现了动画效果。
5、手动设置菜单项(有局限,没有通用性):
/**
* 设置旋转到哪个菜单项
* @param tag
*/
public void setCurrentTag(int tag)
{
if(currentPosition == tag)
{
return;
}
if(tag == 0)
{
finishdus = -360;
}
else if(tag == 1)
{
finishdus = -120;
}
else if(tag == 2)
{
finishdus = -240;
}
if(currentPosition == 0) //当前是0
{
if(tag == 1)
{
oldradus = 120f;
isright = true;
}
else if(tag == 2)
{
oldradus = 120f;
isright = false;
}
}
else if(currentPosition == 1)
{
if(tag == 2)
{
oldradus = 120f;
isright = true;
}
else if(tag == 0)
{
oldradus = 120f;
isright = false;
}
}
else if(currentPosition == 2)
{
if(tag == 0)
{
oldradus = 120f;
isright = true;
}
else if(tag == 1)
{
oldradus = 120f;
isright = false;
}
}
currentPosition = tag;
this.tag = tag;
sub = 0;
circleMenu(8, dip2px(context, 45), oldradus, isright);
}
这样就可以实现旋转效果了。
6、调用方法:
(1)布局文件:
<com.ywl5320.circlemenu.CircleMenuLayout
android:id="@+id/cml_menu"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_marginBottom="92dp"/>
(2)菜单布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="100dp"
android:layout_height="100dp"
android:padding="5dp"
android:gravity="center">
<ImageView
android:id="@+id/img"
android:layout_width="25dp"
android:layout_height="25dp"
android:scaleType="fitXY"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="菜单项"
android:textSize="9sp"
android:gravity="center"
android:textColor="#ffffff"/>
</LinearLayout>
(3)Activity中调用
<span style="white-space:pre"> </span>cmlmenu = (CircleMenuLayout) findViewById(R.id.cml_menu);
btn = (Button) findViewById(R.id.btn);
cmlmenu.initDatas(titles, imgs);
cmlmenu.setOnMenuItemSelectedListener(new CircleMenuLayout.OnMenuItemSelectedListener() {
@Override
public void onMenuItemOnclick(int code) {
if(code == 0)//
{
Toast.makeText(MainActivity.this, "支付宝", Toast.LENGTH_SHORT).show();
}
else if(code == 1)
{
Toast.makeText(MainActivity.this, "银联", Toast.LENGTH_SHORT).show();
}
else if(code == 2)
{
Toast.makeText(MainActivity.this, "微信", Toast.LENGTH_SHORT).show();
}
}
});
OK,就完成了三个菜单旋转效果(注:这里仅仅是为了3个菜单而设计的,其他个数的自己还需要精简或更改一些代码,相信自己改出来的会更有收获的~~)。
以上所述是小编给大家介绍的Android圆形旋转菜单开发实例网站的支持!
来源:http://blog.csdn.net/ywl5320/article/details/52449392


猜你喜欢
- 本人所使用的开发环境是VS2008,开发的系统所在移动终端版本为windows mobile 5.0。由于需要进行身份的验证,需要获取移动终
- 需求winForm 程序输出类型为 windows 程序(不是命令行程序)在运行时想输入一些信息编译开发调试,如何实现这一功能解答:Allo
- 本文实例讲述了C#基于socket模拟http请求的方法。分享给大家供大家参考。具体实现方法如下:using System;using Sy
- 我在做毕设的时候采用shiro进行登录认证和权限管理的实现。其中需求涉及使用三个角色分别是:学生、教师、管理员。现在要三者实现分开登录。即需
- 相信有些同学跟我一样,曾经对这个问题很疑惑。在网上也看了一些别人说的观点,评论不一。有说有值传递和引用传递两种,也有说只有值传递的,这里只说
- 一、前言在一些对高并发请求有限制的系统或者功能里,比如说秒杀活动,或者一些网站返回的当前用户过多,请稍后尝试。这些都是通过对同一时刻请求数量
- 本文实例为大家分享了winform可拖动的自定义Label控件,供大家参考,具体内容如下效果预览:实现步骤如下:(1)首先在项目上右击选择:
- 日期、数字格式化显示,是web开发中的常见需求,spring mvc采用XXXFormatter来处理,先看一个最基本的单元测试:packa
- 前言最近一段时间看了一些介绍ViewDragHelper的博客,感觉这是一个处理手势滑动的神奇,看完以后就想做点东西练练手,于是就做了这个A
- 逆转交替合并两个链表,即从一个链表的尾指针指向另一个链表的尾指针,依次逆转交替进行合并。下面就通过实例来详细的介绍该逆转交替合并两个链表的思
- 1.结构体类型C语言中的2种类型:原生类型和自定义类型,结构体类型是一种自定义类型。2.结构体使用时先定义结构体类型再用类型定义变量->
- 本文实例讲述了Java Web实现session过期后自动跳转到登陆页功能。分享给大家供大家参考,具体如下:通过过滤器的方式实现 sessi
- 目录一、为什么用DiskLruCache1、LruCache和DiskLruCache2、为何使用DiskLruCache二、DiskLru
- 效果图如下所示:先给大家说下实现android 跳转到通讯录的实现思路:1.点击跳转到通讯录界面2.获取通讯录姓名和手机号码3.回调显示姓名
- GridView设置如下:<asp:GridView ID="GridViewlb" runat="se
- 本篇使用java自带的MessageDigest实现对文本的md5加密算法,具体代码如下: /** *@Description
- Android环境布置完毕,直接就是一个Helloworld程序,详情请看《利用adt-bundle轻松搭建Android开发环境与Hell
- 本文研究的主要是java中的null“类型”的相关实例,具体介绍如下。先给出一道简单的null相关的题目,引发我们对null的探讨,后面会根
- import java.io.BufferedReader;import java.io.IOException;import java.i
- 创建一个用户类类型的集合,手动输入用户库主要是判定输入的用户名和密码是否与库中的匹配做好区别是用户名输入错误还是密码输入错误的提示。定义用户