android圆形菜单,android 圆形旋转菜单例子
【实例简介】
【实例截图】
【核心代码】
package com.szugyi.circlemenu.view;
/*
* Copyright 2013 Csaba Szugyiczki
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import com.szugyi.circlemenu.R;
/**
*
* @author Szugyi
* Creates a rotatable circle menu which can be parameterized by custom attributes.
* Handles touches and gestures to make the menu rotatable, and to make the
* menu items selectable and clickable.
*
*/
public class CircleLayout extends ViewGroup {
// Event listeners
private OnItemClickListener mOnItemClickListener = null;
private OnItemSelectedListener mOnItemSelectedListener = null;
private OnCenterClickListener mOnCenterClickListener = null;
// Background image
private Bitmap imageOriginal, imageScaled;
private Matrix matrix;
private int mTappedViewsPostition = -1;
private View mTappedView = null;
private int selected = 0;
// Child sizes
private int mMaxChildWidth = 0;
private int mMaxChildHeight = 0;
private int childWidth = 0;
private int childHeight = 0;
// Sizes of the ViewGroup
private int circleWidth, circleHeight;
private int radius = 0;
// Touch detection
private GestureDetector mGestureDetector;
// needed for detecting the inversed rotations
private boolean[] quadrantTouched;
// Settings of the ViewGroup
private boolean allowRotating = true;
private float angle = 90;
private float firstChildPos = 90;
private boolean rotateToCenter = true;
private boolean isRotating = true;
/**
* @param context
*/
public CircleLayout(Context context) {
this(context, null);
}
/**
* @param context
* @param attrs
*/
public CircleLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* @param context
* @param attrs
* @param defStyle
*/
public CircleLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
/**
* Initializes the ViewGroup and modifies it's default behavior by the passed attributes
* @param attrsthe attributes used to modify default settings
*/
protected void init(AttributeSet attrs) {
mGestureDetector = new GestureDetector(getContext(),
new MyGestureListener());
quadrantTouched = new boolean[] { false, false, false, false, false };
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.Circle);
// The angle where the first menu item will be drawn
angle = a.getInt(R.styleable.Circle_firstChildPosition, 90);
firstChildPos = angle;
rotateToCenter = a.getBoolean(R.styleable.Circle_rotateToCenter,
true);
isRotating = a.getBoolean(R.styleable.Circle_isRotating, true);
// If the menu is not rotating then it does not have to be centered
// since it cannot be even moved
if (!isRotating) {
rotateToCenter = false;
}
if (imageOriginal == null) {
int picId = a.getResourceId(
R.styleable.Circle_circleBackground, -1);
// If a background image was set as an attribute,
// retrieve the image
if (picId != -1) {
imageOriginal = BitmapFactory.decodeResource(
getResources(), picId);
}
}
a.recycle();
// initialize the matrix only once
if (matrix == null) {
matrix = new Matrix();
} else {
// not needed, you can also post the matrix immediately to
// restore the old state
matrix.reset();
}
// Needed for the ViewGroup to be drawn
setWillNotDraw(false);
}
}
/**
* Returns the currently selected menu
* @return the view which is currently the closest to the start position
*/
public View getSelectedItem() {
return (selected >= 0) ? getChildAt(selected) : null;
}
@Override
protected void onDraw(Canvas canvas) {
// the sizes of the ViewGroup
circleHeight = getHeight();
circleWidth = getWidth();
if (imageOriginal != null) {
// Scaling the size of the background image
if (imageScaled == null) {
matrix = new Matrix();
float sx = (((radius childWidth / 4) * 2) / (float) imageOriginal
.getWidth());
float sy = (((radius childWidth / 4) * 2) / (float) imageOriginal
.getHeight());
matrix.postScale(sx, sy);
imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0,
imageOriginal.getWidth(), imageOriginal.getHeight(),
matrix, false);
}
if (imageScaled != null) {
// Move the background to the center
int cx = (circleWidth - imageScaled.getWidth()) / 2;
int cy = (circleHeight - imageScaled.getHeight()) / 2;
Canvas g = canvas;
canvas.rotate(0, circleWidth / 2, circleHeight / 2);
g.drawBitmap(imageScaled, cx, cy, null);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMaxChildWidth = 0;
mMaxChildHeight = 0;
// Measure once to find the maximum child size.
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST);
final int count = getChildCount();
for (int i = 0; i < count; i ) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
mMaxChildWidth = Math.max(mMaxChildWidth, child.getMeasuredWidth());
mMaxChildHeight = Math.max(mMaxChildHeight,
child.getMeasuredHeight());
}
// Measure again for each child to be exactly the same size.
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildWidth,
MeasureSpec.EXACTLY);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxChildHeight,
MeasureSpec.EXACTLY);
for (int i = 0; i < count; i ) {
final View child = getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
setMeasuredDimension(resolveSize(mMaxChildWidth, widthMeasureSpec),
resolveSize(mMaxChildHeight, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int layoutWidth = r - l;
int layoutHeight = b - t;
// Laying out the child views
final int childCount = getChildCount();
int left, top;
radius = (layoutWidth <= layoutHeight) ? layoutWidth / 3
: layoutHeight / 3;
childWidth = (int) (radius / 1.5);
childHeight = (int) (radius / 1.5);
float angleDelay = 360 / getChildCount();
for (int i = 0; i < childCount; i ) {
final CircleImageView child = (CircleImageView) getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
if (angle > 360) {
angle -= 360;
} else {
if (angle < 0) {
angle = 360;
}
}
child.setAngle(angle);
child.setPosition(i);
left = Math
.round((float) (((layoutWidth / 2) - childWidth / 2) radius
* Math.cos(Math.toRadians(angle))));
top = Math
.round((float) (((layoutHeight / 2) - childHeight / 2) radius
* Math.sin(Math.toRadians(angle))));
child.layout(left, top, left childWidth, top childHeight);
angle = angleDelay;
}
}
/**
* Rotate the buttons.
*
* @param degrees The degrees, the menu items should get rotated.
*/
private void rotateButtons(float degrees) {
int left, top, childCount = getChildCount();
float angleDelay = 360 / childCount;
angle = degrees;
if (angle > 360) {
angle -= 360;
} else {
if (angle < 0) {
angle = 360;
}
}
for (int i = 0; i < childCount; i ) {
if (angle > 360) {
angle -= 360;
} else {
if (angle < 0) {
angle = 360;
}
}
final CircleImageView child = (CircleImageView) getChildAt(i);
if (child.getVisibility() == GONE) {
continue;
}
left = Math
.round((float) (((circleWidth / 2) - childWidth / 2) radius
* Math.cos(Math.toRadians(angle))));
top = Math
.round((float) (((circleHeight / 2) - childHeight / 2) radius
* Math.sin(Math.toRadians(angle))));
child.setAngle(angle);
if (Math.abs(angle - firstChildPos) < (angleDelay / 2)
&& selected != child.getPosition()) {
selected = child.getPosition();
if (mOnItemSelectedListener != null && rotateToCenter) {
mOnItemSelectedListener.onItemSelected(child, selected,
child.getId(), child.getName());
}
}
child.layout(left, top, left childWidth, top childHeight);
angle = angleDelay;
}
}
/**
* @return The angle of the unit circle with the image view's center
*/
private double getAngle(double xTouch, double yTouch) {
double x = xTouch - (circleWidth / 2d);
double y = circleHeight - yTouch - (circleHeight / 2d);
switch (getQuadrant(x, y)) {
case 1:
return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
case 2:
case 3:
return 180 - (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
case 4:
return 360 Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
default:
// ignore, does not happen
return 0;
}
}
/**
* @return The selected quadrant.
*/
private static int getQuadrant(double x, double y) {
if (x >= 0) {
return y >= 0 ? 1 : 4;
} else {
return y >= 0 ? 2 : 3;
}
}
private double startAngle;
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isEnabled()) {
if (isRotating) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// reset the touched quadrants
for (int i = 0; i < quadrantTouched.length; i ) {
quadrantTouched[i] = false;
}
allowRotating = false;
startAngle = getAngle(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
double currentAngle = getAngle(event.getX(), event.getY());
rotateButtons((float) (startAngle - currentAngle));
startAngle = currentAngle;
break;
case MotionEvent.ACTION_UP:
allowRotating = true;
rotateViewToCenter((CircleImageView) getChildAt(selected),
false);
break;
}
}
// set the touched quadrant to true
quadrantTouched[getQuadrant(event.getX() - (circleWidth / 2),
circleHeight - event.getY() - (circleHeight / 2))] = true;
mGestureDetector.onTouchEvent(event);
return true;
}
return false;
}
private class MyGestureListener extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (!isRotating) {
return false;
}
// get the quadrant of the start and the end of the fling
int q1 = getQuadrant(e1.getX() - (circleWidth / 2), circleHeight
- e1.getY() - (circleHeight / 2));
int q2 = getQuadrant(e2.getX() - (circleWidth / 2), circleHeight
- e2.getY() - (circleHeight / 2));
// the inversed rotations
if ((q1 == 2 && q2 == 2 && Math.abs(velocityX) < Math
.abs(velocityY))
|| (q1 == 3 && q2 == 3)
|| (q1 == 1 && q2 == 3)
|| (q1 == 4 && q2 == 4 && Math.abs(velocityX) > Math
.abs(velocityY))
|| ((q1 == 2 && q2 == 3) || (q1 == 3 && q2 == 2))
|| ((q1 == 3 && q2 == 4) || (q1 == 4 && q2 == 3))
|| (q1 == 2 && q2 == 4 && quadrantTouched[3])
|| (q1 == 4 && q2 == 2 && quadrantTouched[3])) {
CircleLayout.this.post(new FlingRunnable(-1
* (velocityX velocityY)));
} else {
// the normal rotation
CircleLayout.this
.post(new FlingRunnable(velocityX velocityY));
}
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
mTappedViewsPostition = pointToPosition(e.getX(), e.getY());
if (mTappedViewsPostition >= 0) {
mTappedView = getChildAt(mTappedViewsPostition);
mTappedView.setPressed(true);
} else {
float centerX = circleWidth / 2;
float centerY = circleHeight / 2;
if (e.getX() < centerX (childWidth / 2)
&& e.getX() > centerX - childWidth / 2
&& e.getY() < centerY (childHeight / 2)
&& e.getY() > centerY - (childHeight / 2)) {
if (mOnCenterClickListener != null) {
mOnCenterClickListener.onCenterClick();
return true;
}
}
}
if (mTappedView != null) {
CircleImageView view = (CircleImageView) (mTappedView);
if (selected != mTappedViewsPostition) {
rotateViewToCenter(view, false);
if (!rotateToCenter) {
if (mOnItemSelectedListener != null) {
mOnItemSelectedListener.onItemSelected(mTappedView,
mTappedViewsPostition, mTappedView.getId(), view.getName());
}
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(mTappedView,
mTappedViewsPostition, mTappedView.getId(), view.getName());
}
}
} else {
rotateViewToCenter(view, false);
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(mTappedView,
mTappedViewsPostition, mTappedView.getId(), view.getName());
}
}
return true;
}
return super.onSingleTapUp(e);
}
}
/**
* Rotates the given view to the center of the menu.
* @param viewthe view to be rotated to the center
* @param fromRunnableif the method is called from the runnable which animates the rotation
* then it should be true, otherwise false
*/
private void rotateViewToCenter(CircleImageView view, boolean fromRunnable) {
if (rotateToCenter) {
float velocityTemp = 1;
float destAngle = (float) (firstChildPos - view.getAngle());
float startAngle = 0;
int reverser = 1;
if (destAngle < 0) {
destAngle = 360;
}
if (destAngle > 180) {
reverser = -1;
destAngle = 360 - destAngle;
}
while (startAngle < destAngle) {
startAngle = velocityTemp / 75;
velocityTemp *= 1.0666F;
}
CircleLayout.this.post(new FlingRunnable(reverser * velocityTemp,
!fromRunnable));
}
}
/**
* A {@link Runnable} for animating the menu rotation.
*/
private class FlingRunnable implements Runnable {
private float velocity;
float angleDelay;
boolean isFirstForwarding = true;
public FlingRunnable(float velocity) {
this(velocity, true);
}
public FlingRunnable(float velocity, boolean isFirst) {
this.velocity = velocity;
this.angleDelay = 360 / getChildCount();
this.isFirstForwarding = isFirst;
}
public void run() {
if (Math.abs(velocity) > 5 && allowRotating) {
if (rotateToCenter) {
if (!(Math.abs(velocity) < 200 && (Math.abs(angle
- firstChildPos)
% angleDelay < 2))) {
rotateButtons(velocity / 75);
velocity /= 1.0666F;
CircleLayout.this.post(this);
}
} else {
rotateButtons(velocity / 75);
velocity /= 1.0666F;
CircleLayout.this.post(this);
}
} else {
if (isFirstForwarding) {
isFirstForwarding = false;
CircleLayout.this.rotateViewToCenter(
(CircleImageView) getChildAt(selected), true);
}
}
}
}
private int pointToPosition(float x, float y) {
for (int i = 0; i < getChildCount(); i ) {
View item = (View) getChildAt(i);
if (item.getLeft() < x && item.getRight() > x & item.getTop() < y
&& item.getBottom() > y) {
return i;
}
}
return -1;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.mOnItemClickListener = onItemClickListener;
}
public interface OnItemClickListener {
void onItemClick(View view, int position, long id, String name);
}
public void setOnItemSelectedListener(
OnItemSelectedListener onItemSelectedListener) {
this.mOnItemSelectedListener = onItemSelectedListener;
}
public interface OnItemSelectedListener {
void onItemSelected(View view, int position, long id, String name);
}
public interface OnCenterClickListener {
void onCenterClick();
}
public void setOnCenterClickListener(
OnCenterClickListener onCenterClickListener) {
this.mOnCenterClickListener = onCenterClickListener;
}
}
android圆形菜单,android 圆形旋转菜单例子相关推荐
- Android 9.0 系统设置显示主菜单添加屏幕旋转菜单实现旋转屏幕功能
1.前言 在android9.0的系统rom定制化开发中,在对系统设置进行定制开发中,有产品需求要求增加旋转屏幕功能的菜单,就是在点击旋转屏幕菜单后弹窗显示旋转0度,旋转 90度,旋转180度,旋转2 ...
- Android 12.0 系统设置显示主菜单添加屏幕旋转菜单实现旋转屏幕功能
1.前言 在android12.0的系统rom定制化开发中,在对系统设置进行定制开发中,有产品需求要求增加 旋转屏幕功能的菜单,就是在点击旋转屏幕菜单后弹窗显示旋转0度,旋转 90度,旋转180度, ...
- [Android自定义控件]双圆圈内外旋转菜单
概述 双圆圈菜单是之前做的一个项目的需求,写完以后boss决定不用了,提取以后把它贴出来,第一次发技术博客,希望各位前辈多多指教. 先看一下效果图. 需求就是 * 外圈放置菜单选项,内圈为圆形大图: ...
- Android 高仿优酷旋转菜单
这是一个很早版本的优酷菜单,效果挺不错的,实现起来也挺简单的.废话不说,直接上代码: 首先是xml文件: <RelativeLayout xmlns:android="http://s ...
- android圆形旋转菜单,而对于移动转换功能支持
LZ该公司最近接手一个项目,需要写一个圆形旋转菜单,和菜单之间的移动换位支持,我本来以为这样的demo如若互联网是非常.想想你妈妈也帮不了我,空旋转,但它不能改变位置,所以LZ我们只能靠自己摸索. 最 ...
- Android的自定义view的旋转圆形菜单实现
之前项目中有遇到过 首页需要做一个 圆形饼状可旋转的菜单 捣鼓了一两天完成了这里就把代码放出来 首先是 自定义view public class CakeView extends View {priv ...
- Android 圆形旋转菜单
我的视频课程:<FFmpeg打造Android万能音频播放器> 最近帮朋友做了一个动画菜单,感觉有一定的实用价值,就在此给大家分享一下,先看看效果:源码下载地址在末尾 实现思路: 从图中可 ...
- Android 打造炫目的圆形菜单 秒秒钟高仿建行圆形菜单
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/43131133,本文出自:[张鸿洋的博客] 1.概述 今天打开建行看存款,一看伤心 ...
- Android 仿酷点圆形菜单
看见一个人写了一个圆形的可以转的菜单,当时看的还是挺模糊的,最后自己模仿的写了一遍,这是源代码 基本思想是这样的 1,把每个图标显示的什么图标确定下来 2,计算每一个点的坐标, 3,在activity ...
- android仿优酷菜单,Android编程实现仿优酷旋转菜单效果(附demo源码)
本文实例讲述了Android编程实现仿优酷旋转菜单效果.分享给大家供大家参考,具体如下: 首先,看下效果: 不好意思,不会制作动态图片,只好上传静态的了,如果谁会,请教教我吧. 首先,看下xml文件: ...
最新文章
- WF4.0 基础篇 (三) 流程实例WorkflowApplication与设计WF程序的基本原则
- asp.net C# MVC 提交表单后清空表单
- 遭遇“烧钱瓶颈” 优酷成本结构堪忧
- quarkus_使用Quarkus调试容器中的系统测试(视频)
- Android开发之Java集合类性能分析
- integer java 随机_如何在Java中生成随机BigInteger值?
- 分布式系统认证方案_分布式系统认证方案_Spring Security OAuth2.0认证授权---springcloud工作笔记136
- linux内核配置usb虚拟串口,霍尼韦尔是否能提供USB串口仿真的Linux驱动程序?
- 兼容西门子 CPU226IE量产方案
- app逆向入门分析——破解某APP登陆请求参数
- 【你好,windows】Windows 7 X64旗舰纯净版版(NVME和USB3.0集合总裁万能网
- python:随机产生n个数
- 阿里云服务器价格,最新收费标准报价及活动价格表
- 梦想在远方,理想在路上
- 古墓丽影暗影显卡测试软件,游戏新消息:战地5古墓丽影暗影8K测试单显卡根本带不动...
- 从刘老师的进化的力量到有感,疫情阶段如何弯道超车
- 【Vue3.0实战逐步深入系列】vue3.0获取问卷调查结果并输出到控制台
- WinForm容器内控件批量效验是否允许为空?设置是否只读?设置是否可用等方法分享
- strncpy()函数用法及其详解
- 探索自助报表BI的现状和未来(文末送福利)
热门文章
- HCIA-RoutingSwitching华为认证路由交换工程师(持续更新中2%)
- 让理科生沉默,让文科生流泪的综合题详解
- Blob如何在html里转换成图片,前端图片canvas,file,blob,DataURL等格式转换
- windows基于TCP/IP的简单文件/图片传输
- 嵌入式系统开发-麦子学院(12)——ARM Cortex A8 硬件基础(2)
- apicloud打开地图导航
- 云呐容灾备份策略,存储容灾备份系统
- winscp中解压文件
- win10 pycharm小写变大写,键盘输入错乱
- 安卓手机软件开发_这款安卓神器,让你下片更轻松!千万别滥用