1.自定义view

2.xml文件引用该view

3.定义第一个按钮图片

----------------------------------------------------------------------------

package roundspinviewdemo.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PathEffect;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.View;

import com.example.uiset.R;

/**
 * 圆盘式的view
 * 
 * @author chroya
 * 
 */
public class MineServiceView extends View {

private Paint mPaint = new Paint();
private PaintFlagsDrawFilter pfd;

private int startMenu;   //菜单的第一张图片的资源id

// stone列表
private BigStone[] mStones;
// 数目
private static final int STONE_COUNT = 4;

// 圆心坐标
private int mPointX = 0, mPointY = 0;
// 半径
private int mRadius = 0;
// 每两个点间隔的角度
private int mDegreeDelta;

private int menuRadius; // 菜单的半径

private int mCur = -1; // 正在被移动的menu;

private boolean[] quadrantTouched;   //对每个象限触摸情况的记录

// Touch detection
private GestureDetector mGestureDetector;

private onMineServiceViewListener mListener;  //自定义事件监听器

private final static int TO_ROTATE_BUTTON = 0;  //旋转按钮;

private Handler handler = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case TO_ROTATE_BUTTON:
float velocity = Float.parseFloat(msg.obj.toString());
rotateButtons(velocity/75);
velocity /= 1.0666F;
new Thread(new FlingRunnable(velocity)).start();
break;

default:
break;
}
};
};

public interface onMineServiceViewListener{
public void onSingleTapUp(int position);  //监听每个菜单的单击事件
}

public MineServiceView(Context context, AttributeSet attrs) {
super(context, attrs);
if(attrs!=null){
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.RoundSpinView);
startMenu = a.getResourceId(R.styleable.RoundSpinView_menuStart, 0);
}
pfd = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG); 
mPaint.setColor(Color.GREEN);
mPaint.setStrokeWidth(2);
mPaint.setAntiAlias(true); //消除锯齿  
mPaint.setStyle(Paint.Style.STROKE); //绘制空心圆 
PathEffect effects = new DashPathEffect(new float[]{5,5,5,5},1);  
mPaint.setPathEffect(effects);

quadrantTouched = new boolean[] { false, false, false, false, false };
mGestureDetector = new GestureDetector(getContext(),
new MyGestureListener());

setupStones();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mPointX = this.getMeasuredWidth()/2;
mPointY = this.getMeasuredHeight()/2;

//初始化半径和菜单半径
mRadius = mPointX-mPointX/5;
menuRadius = (int)(mPointX/5.5);

computeCoordinates();
}

/**
* 初始化每个点
*/
private void setupStones() {
mStones = new BigStone[STONE_COUNT];
BigStone stone;
int angle = 270;
mDegreeDelta = 360 / STONE_COUNT;

for (int index = 0; index < STONE_COUNT; index++) {
stone = new BigStone();
if (angle >= 360) {
angle -= 360;
}else if(angle < 0){
angle += 360;
}
stone.angle = angle;
stone.bitmap = BitmapFactory.decodeResource(getResources(),
startMenu + index);
angle += mDegreeDelta;

mStones[index] = stone;
}
}

/**
* 重新计算每个点的角度
*/
private void resetStonesAngle(float x, float y) {
int angle = computeCurrentAngle(x, y);
Log.d("RoundSpinView", "angle:" + angle);
for (int index = 0; index < STONE_COUNT; index++) {
mStones[index].angle = angle;
angle += mDegreeDelta;
}
}

/**
* 计算每个点的坐标
*/
private void computeCoordinates() {
BigStone stone;
for (int index = 0; index < STONE_COUNT; index++) {
stone = mStones[index];
stone.x = mPointX
+ (float) (mRadius * Math.cos(Math.toRadians(stone.angle)));
stone.y = mPointY
+ (float) (mRadius * Math.sin(Math.toRadians(stone.angle)));
}
}

/**
* 计算某点的角度

* @param x
* @param y
* @return
*/
private int computeCurrentAngle(float x, float y) {
float distance = (float) Math
.sqrt(((x - mPointX) * (x - mPointX) + (y - mPointY)
* (y - mPointY)));
int degree = (int) (Math.acos((x - mPointX) / distance) * 180 / Math.PI);
if (y < mPointY) {
degree = -degree;
}

Log.d("RoundSpinView", "x:" + x + ",y:" + y + ",degree:" + degree);
return degree;
}

private double startAngle;

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// resetStonesAngle(event.getX(), event.getY());
// computeCoordinates();
// invalidate();

int x, y;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
x = (int) event.getX();
y = (int) event.getY();
mCur = getInCircle(x, y);
if (mCur == -1) {
startAngle = computeCurrentAngle(x, y);
}
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
x = (int) event.getX();
y = (int) event.getY();
if (mCur != -1) {
mStones[mCur].x = x;
mStones[mCur].y = y;
invalidate();
} else {
double currentAngle = computeCurrentAngle(x, y);
rotateButtons(startAngle - currentAngle);
startAngle = currentAngle;
}
} else if (event.getAction() == MotionEvent.ACTION_UP) {
x = (int) event.getX();
y = (int) event.getY();
if (mCur != -1) {
computeCoordinates();
int cur = getInCircle(x, y);
if (cur != mCur && cur != -1) {
int angle = mStones[mCur].angle;
mStones[mCur].angle = mStones[cur].angle;
mStones[cur].angle = angle;
}
computeCoordinates();
invalidate();
mCur = -1;
}
}

// set the touched quadrant to true
quadrantTouched[getQuadrant(event.getX() - mPointX,
mPointY - event.getY())] = true;
mGestureDetector.onTouchEvent(event);
return true;
}

private class MyGestureListener extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// get the quadrant of the start and the end of the fling
int q1 = getQuadrant(e1.getX() - mPointX, mPointY - e1.getY());
int q2 = getQuadrant(e2.getX() - mPointX, mPointY - e2.getY());

// 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)));
new Thread(new FlingRunnable(velocityX+velocityY)).start();
} else {
// the normal rotation
// CircleLayout.this
// .post(new FlingRunnable(velocityX + velocityY));
new Thread(new FlingRunnable(-(velocityX+velocityY))).start();
}

return true;

}

@Override
public boolean onSingleTapUp(MotionEvent e) {

int cur = getInCircle((int)e.getX(),(int)e.getY());
if(cur!=-1){
if(mListener!=null){
mListener.onSingleTapUp(cur);
}
// Toast.makeText(getContext(), "position:"+cur, 0).show();
return true;
}
return false;
}

}

private class FlingRunnable implements Runnable{

private float velocity;

public FlingRunnable(float velocity){
this.velocity = velocity;
}

@Override
public void run() {
// TODO Auto-generated method stub
if(Math.abs(velocity)>=200){
Message message = Message.obtain();
message.what = TO_ROTATE_BUTTON;
message.obj = velocity;
handler.sendMessage(message);
}
}

}

/**
* @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 void rotateButtons(double degree) {
for (int i = 0; i < STONE_COUNT; i++) {
mStones[i].angle -= degree;
if (mStones[i].angle < 0) {
mStones[i].angle += 360;
}else if(mStones[i].angle >=360){
mStones[i].angle -= 360;
}
}

computeCoordinates();
invalidate();
}

@Override
public void onDraw(Canvas canvas) {
//画一个白色的圆环
canvas.drawCircle(mPointX, mPointY, mRadius, mPaint); 
Matrix matrix = new Matrix(); 
matrix.postScale(2.5f,2.5f); //长和宽放大缩小的比例
// Bitmap resizeBmp = Bitmap.createBitmap(
//  bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);
//将每个菜单画出来
for (int index = 0; index < STONE_COUNT; index++) {
if (!mStones[index].isVisible)
continue;
drawInCenter(canvas, mStones[index].bitmap, mStones[index].x,
mStones[index].y);

// canvas.drawBitmap(resizeBmp, mPointX/1.4f, mPointY/1.3f, mPaint);
}
}

/**
* 把中心点放到中心处

* @param canvas
* @param bitmap
* @param left
* @param top
*/
private void drawInCenter(Canvas canvas, Bitmap bitmap, float left,
float top) {
Rect dst = new Rect();
dst.left = (int) (left - menuRadius);
dst.right = (int) (left + menuRadius);
dst.top = (int) (top - menuRadius);
dst.bottom = (int) (top + menuRadius);
canvas.setDrawFilter(pfd); 
canvas.drawBitmap(bitmap, null, dst, mPaint);
}

private int getInCircle(int x, int y) {
for (int i = 0; i < STONE_COUNT; i++) {
BigStone stone = mStones[i];
int mx = (int) stone.x;
int my = (int) stone.y;
if (((x - mx) * (x - mx) + (y - my) * (y - my)) < menuRadius
* menuRadius) {
return i;
}
}
return -1;
}

public void setOnMineServiceViewListener(onMineServiceViewListener listener){
this.mListener = listener;
}

class BigStone {

// 图片
Bitmap bitmap;

// 角度
int angle;

// x坐标
float x;

// y坐标
float y;

// 是否可见
boolean isVisible = true;
}
}

--------------------------------------------------------------------------

<roundspinviewdemo.view.MineServiceView
        android:id="@+id/msa_test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        app:menuStart="@drawable/ic_launcher1" />

-------------------------------------------------------------------------

values文件夹定义xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 设置旋转菜单对应的第一张图片 -->
    <declare-styleable name="RoundSpinView">
        <attr name="menuStart" format="reference" />
    </declare-styleable>
</resources>

圆盘旋转按钮--自定义view相关推荐

  1. android 画布旋转,Android自定义View叶子旋转完整版(六)

    上一篇实现多叶子飘动旋转,今天完成最后的功能. 1.添加右侧旋转枫叶 2.添加滑动条效果,显示百分比 3.修复叶子飘出边框问题 1.添加右侧旋转叶子 Bitmap turnBitmap = ((Bit ...

  2. android view设置按钮颜色_Android 酷炫自定义 View:高仿 QQ 窗帘菜单

    作者:大公爵 链接:https://www.jianshu.com/p/cdb3d373fe37 介绍 不知道大家是否有印象,QQ 曾经有个版本用到了一种双向侧拉菜单,就像窗帘一样可以两边开合,并且伴 ...

  3. android 开发打赏布局,Android自定义View模仿虎扑直播界面的打赏按钮功能

    Android自定义View模仿虎扑直播界面的打赏按钮功能 发布时间:2020-09-28 12:15:53 来源:脚本之家 阅读:77 作者:shenhuniurou 前言 作为一个资深篮球爱好者, ...

  4. android圆形波纹按钮,android自定义View——圆形波纹扫描效果

    蓝牙项目,考虑到后面可能会用到这个扫描的效果,所以参照大神写好的控件,增加了自己需要使用的接口.也顺便巩固一下自定义view中各种零碎的知识点. 需要的效果图 先放一个效果图,点击中心图片开始动画,再 ...

  5. Android自定义View实现不断旋转的圆形图片

    自定义View是android开发的一个重要技能,用android提供的2/3D绘制相关类可以实现非常多炫酷的效果,需要实打实的编程基础.(吧). 但是自定义View又是我的弱项,所以最近都在摸索.练 ...

  6. android设置自定义按钮,Android自定义View之元素按钮

    Android自定义View之元素按钮 之前在dribbble看到的三个元素的按钮,参考了设计的创意,添加了自己定义的动画效果来实现.先看效果 效果图 分别是水火电三个元素的按钮实现.其中电的实现最简 ...

  7. android 展开式按钮,Android自定义View实现可展开、会呼吸的按钮

    不专门练习的话,自定义View的知识又忘了许多.正好新项目里有这个需求,就再练习一下,代码已上传:地址 可以修改文本.文字大小.各种颜色: 1.按照国际惯例,就是新建attrs,写各种需要的属性,然后 ...

  8. Android开发之自定义view进行旋转动画

    老套路先上图: 整个view非常简单,我自定义view里面都有详细的注释说明 先看自定义view部分代码: package cn.xiayiye5.customizestudy.view;import ...

  9. Android开发自定义View之滑动按钮与自定义属性

    写博客辛苦了,转载的朋友请标明出处哦,finddreams:(http://blog.csdn.net/finddreams/article/details/40392975) 话不多说,先运行效果图 ...

最新文章

  1. REST中的PUT与POST
  2. 汇编语言基础 debug的使用
  3. spring 配置文件无法加载,junit找不到xml配置文件java.lang.IllegalStateException: Failed to load ApplicationContext...
  4. 推荐一个SAM文件中flag含义解释工具--转载
  5. 恐龙机器人钢索恐龙形态_四川恐龙多,自贡是个窝——恐龙,我来了
  6. 关于WebService中用到的QName详解
  7. live555 源码分析:子会话 SDP 行生成
  8. c++ stack 遍历_C/C++内存分配!
  9. linux 权限加号是,请教:drwxrwxr-x   什么权限后面有个加号,代表什么意思
  10. windows下文件路径太深,删除解决方案
  11. 【我的世界Minecraft-MC】常见及各种指令大杂烩【2022.8版】
  12. lua工具库penlight--06数据(二)
  13. Android 调用手机相册、摄像头拍照及剪裁照片
  14. 计算机加内存还是固态硬盘,电脑慢加内存还是固态硬盘好
  15. Android记账本APP开发进阶版
  16. 个人如何打破部门墙_做事要有霸气
  17. Django REST 框架的 FBV 与 CBV 选择
  18. SAS常用基础代码例子-数据描述性分析
  19. pip安装报错ValueError: check_hostname requires server_hostname
  20. 服务企业云原生转型 KubeSphere容器平台获评CSDN“年度云原生技术产品”

热门文章

  1. ACE:高级CIP评估
  2. postman传入参数加密
  3. MySql中 delimiter 使用
  4. solr查询参数使用说明
  5. 使用神经网络预测光伏电站功率
  6. 和 Nature 封面论文一作,聊了聊天机芯的科研故事
  7. Ofcom利用5GHz频段为英国家庭WiFi提速
  8. thinkpadE570c拆机介绍
  9. matlab 代码 位图矢量化,性能 – matlab代码的矢量化
  10. x265与HM编码性能对比