原帖地址

源码下载地址

我要介绍的是一个 能旋转的view,说这个view能旋转有点不切实际,那是视觉效果,其实是对图片的旋转。目前它只支持图片。你可以把它认为是一个能响应手势旋转的View。

它的功能有:

1.会响应手势旋转

2.该view模拟真实罗盘旋转:a.旋转的时候会有惯性,继续旋转,而且是减速旋转b.旋转期间手指扳动罗盘,能加速罗盘旋转c.当罗盘在旋转的时候,手指按住罗盘,它会有刹车的效果。

效果截图:

为了形象点我用了一张风车的图作为例子

技术要点

1.需要扩展一个view,重写ondraw(),onTouchEvent(),onMeasure(),onDetachedFromWindow()方法

a.onDraw():主要是控制图片旋转绘图

b.onTouchEvent():主要是监听手势

c.onMeasere():用来测量view的长宽,在xml里最好配置成wrap_content,因为如果为固定值可能会因为长宽不够,导致显示不全

d.onDetachedFromWindow():用来回收bitmap

2.需要通过handler来处理惯性

3.需要一个速度分析器,来分析手势离开时的瞬时速度

4.需要用到圆和三角函数的知识:如反正切函数,弧度等

技术难点分析

1.如何扩展这个View

a.View的旋转图片的设置

我们可以提供一个方法来设置旋转的图片,并定义旋转图片的成员变量,这里我将它命名为rotaBitmap

public void setRotatBitmap(Bitmap bitmap) {
rotatBitmap = bitmap;
initSize();
postInvalidate();
} public void setRotatDrawableResource(int id) { BitmapDrawable drawable = (BitmapDrawable)getContext().getResources().getDrawable(id); setRotatDrawable(drawable);
} public void setRotatDrawable(BitmapDrawable drawable) {
rotatBitmap = drawable.getBitmap();
initSize();
postInvalidate();
}

b.View长宽确认

有了图片就可以确认这个view的大小了,这里view的大小不是image的大小,因为还要考虑旋转,一个矩形要确保旋转360度都能被看见,那这个区域应该是个正方形,而且这个正方形的内切圆半径是这个矩形的对角线一半。不知道能否说明白,还是我画图吧。

通过上图,应该很容易发现,这个view的长度应该是被旋转图的对角线的长度

这样我们可以加上这样一段代码:

private void initSize() {
if (rotatBitmap == null) { // throw new NoBitMapError("Error,No bitmap in RotatView!");
return;
}
width = rotatBitmap.getWidth();
height = rotatBitmap.getHeight(); maxwidth = Math.sqrt(width * width + height * height);
o_x = o_y = (float)(maxwidth / 2);//确定圆心坐标
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 它的宽高不是图片的宽高,而是以宽高为直角的矩形的对角线的长度
setMeasuredDimension((int)maxwidth, (int)maxwidth); }

c.旋转原理

图片的旋转是在ondraw()里实现的,通过一个变量:deta_degree 来控制旋转的度数

/**
* 当前圆盘所转的弧度(以该 view 的中心为圆点)
*/
float deta_degree;

然后用Matrix来控制旋转图片,主要是preRotate(deta_degree)这里的单位是度,360度为一圈,最后把旋转的图画到画布上

@Override
protected void onDraw(Canvas canvas) { Matrix matrix = new Matrix();
// 设置转轴位置
matrix.setTranslate((float)width / 2, (float)height / 2); // 开始转
matrix.preRotate(deta_degree);
// 转轴还原
matrix.preTranslate(-(float)width / 2, -(float)height / 2); // 将位置送到view的中心
matrix.postTranslate((float)(maxwidth - width) / 2, (float)(maxwidth - height) / 2); canvas.drawBitmap(rotatBitmap, matrix,paint); super.onDraw(canvas);
}

考虑到它的周期为360,如果detaDegree的度数太大可能会越界,我们可以做一个于求余处理,让它的值在-360到360之间

/**
* 通过此方法来控制旋转度数,如果超过360,让它求余,防止,该值过大造成越界
*
* @param added
*/
private void addDegree(float added) {
deta_degree += added;
if (deta_degree > 360 || deta_degree < -360) {
deta_degree = deta_degree % 360;
} }

这里的动画是通过不停的走ondraw()方法刷新,产生的效果,类似放电影一样

d.view的手势响应

当用户触摸它时会响应onTouch事件,在onTouch里分析手指的坐标,通过坐标算出与圆心的夹角

下图为手指与view中心的夹角(这里的原点就是view的旋转中心):

每次手指滑动或是松开都会计算它与原点的夹角,这个方法可以通过反正切函数求出来,详情:

/** * 计算以(src_x,src_y)为坐标圆点,建立直角体系,求出(target_x,target_y)坐标与x轴的夹角 * 主要是利用反正切函数的知识求出夹角 *  * @param src_x * @param src_y * @param target_x * @param target_y * @return */float detaDegree(float src_x, float src_y, float target_x, float target_y) { float detaX = target_x - src_x; float detaY = target_y - src_y; double d; //坐标在四个象限里 if (detaX != 0) { float tan = Math.abs(detaY / detaX); if (detaX > 0) { //第一象限 if (detaY >= 0) { d = Math.atan(tan); } else { //第四象限 d = 2 * Math.PI - Math.atan(tan); } } else { if (detaY >= 0) { //第二象限 d = Math.PI - Math.atan(tan); } else { //第三象限 d = Math.PI + Math.atan(tan); } } } else { //坐标在y轴上 if (detaY > 0) { //坐标在y>0上 d = Math.PI / 2; } else { //坐标在y<0上 d = -Math.PI / 2; } } return (float)((d * 180) / Math.PI); }

通过以上方法,可以把每次移动的时候的夹角求出来,把当前的夹角和上次的手指夹角坐差运算就能求出手指相对圆心旋转的角度增量,得到这个角度增量就可以通过调用

这前提过的addDegree()方法,改变图片的角度,然后调用invalidate()方法重绘,就实现了罗盘随手指旋转的效果。

在这里赋上ontouch处理down和move事件的代码(up事件是用来处理惯性用的):

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
if (rotatBitmap == null) { throw new NoBitMapError("Error,No bitmap in RotatView!");
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
down_x = event.getX();
down_y = event.getY();
current_degree = detaDegree(o_x, o_y, down_x, down_y); break; }
case MotionEvent.ACTION_MOVE: {
down_x = target_x = event.getX();
down_y = target_y = event.getY();
float degree = detaDegree(o_x, o_y, target_x, target_y); // 滑过的弧度增量
float dete = degree - current_degree;
// 如果小于-90度说明 它跨周了,需要特殊处理350->17,
if (dete < -270) {
dete = dete + 360; // 如果大于90度说明 它跨周了,需要特殊处理-350->-17,
} else if (dete > 270) {
dete = dete - 360;
} addDegree(dete);
current_degree = degree;
invalidate(); break;
}

e.View的瞬时速度获取

为了得到瞬时速度,我的思路是通过一个固定长度的2维数组,把手指与原点的最近几次夹角增量和时间点记录下来,通过这几个夹角增量和时间点,可以算出平均速度,因为手指滑动的时候,响应ontouch事件次数非常多,我们可以把最后几次ontouch记录下的数据,取平均值,把它认为是瞬时速度。

这里用到了一点物理知识:

假如上图为record记录的4组数据,t代表时间点,表示产生这个事件的时间,d代表手指与圆心夹角的增量,它是这次夹角与上次夹角的差值

这样我们可以把t=t3-t0算出经过的时间,把sum=d1+d2+d3算出这段时间一共经历过的弧度

再把sum/t就是平均速度了

但需要注意一个细节:d0是无效的

这里给出计算速度 的代码:

因为考虑不能让速度太快,所以给出了一个最大值

/**
* 最大速度
*/
public static final double max_speed = 8; /**
* 通过数组里所装载的数据分析出即时速度<br>
* 原理是:计算数组里的时间长度和增量的总数,然后求出每毫秒所走过的弧度<br>
* 当然不能超过{@link VRecord#max_speed}
*
* @return
*/
public double getSpeed() { if (addCount == 0) {
return 0;
}
int maxIndex = Math.min(addCount, length) - 1; if ((record[0][1] - record[maxIndex][1]) == 0) {
return 0;
} double detaTime = record[0][1] - record[maxIndex][1];
double sumdegree = 0;
for (int i = 0; i < length - 1; i++) { sumdegree += record<I>[0];
// System.out.println(record[0]);
} // System.out.println("----------");
// System.out.println(sumdegree);
// System.out.println(detaTime);
double result = sumdegree / detaTime;
if (result > 0) {
return Math.min(result, max_speed);
} else {
return Math.max(result, -max_speed);
}
// System.out.println("v=" + result); }

讲到这我要提一下,这个二维数组是如何做到获取最近的数据,如果超过容量,它将把原来的数据丢弃,我想直接上代码,大家就能看懂吧

/**
* 二维数组,1.保存弧度增量.2.保存产生这个增量的时间点
*/
double[][] record = new double[length][2]; /**
* 为二维数组装载数据* 注:通过此方法,有个特点,能把最后的length组数据记录下来,length以外的会丢失
*
* @param detadegree
* @param time
*/
public void add(double detadegree, double time) { for (int i = length - 1; i > 0; i--) {
record[0] = record[i - 1][0];
record[1] = record[i - 1][1];
}
record[0][0] = detadegree;
record[0][1] = time;
addCount++; }

f.View惯性处理

这里的惯性处理就是用到加速度了,再用handler发消息,控制它不停减速,减到它为零为止就停止发消息

下面是handler代码

@Override
public void handleMessage(Message msg) { double detaTime = System.currentTimeMillis() - currentTime;
switch (msg.what) { case play: {
//如果是顺时针
if (isClockWise) {
speed = speed - a * detaTime;//减速
if (speed <= 0) {
return;
} else {
handler.sendEmptyMessageDelayed(play, delayedTime);
}
} else {
speed = speed + a * detaTime;
if (speed >= 0) {
return;
} else {
handler.sendEmptyMessageDelayed(play, delayedTime);
}
} addDegree((float)(speed * detaTime + (a * detaTime * detaTime) / 2));//高中物理算路程S=vt+at2 // if (a < a_max) {
// a = (float)(a + a_add*detaTime);
// System.out.println("a:"+a);
// }
currentTime = System.currentTimeMillis();
invalidate(); break;
}
case stop: {
speed = 0;
handler.removeMessages(play);
}
} super.handleMessage(msg);
}
};

android 之旋转罗盘 风车 开发 Android 随手指旋转图片相关推荐

  1. android旋转动画惯性,android 之旋转罗盘 风车 开发

    我要介绍的是一个 能旋转的view,说这个view能旋转有点不切实际,那是视觉效果,其实是对图片的旋转.目前它只支持图片.你可以把它认为是一个能响应手势旋转的View. 它的功能有: 1.会响应手势旋 ...

  2. android+使用bmob冲突,bmob开发android遇到的问题

    昨天尝试使用bmob,但是在使用文件对象时候遇到问题,但是他们的工作人员周末没上班,问题没解决,昨晚一晚没睡好,之后大清早的来求帮助了. 源码如下: String path = Environment ...

  3. mac android手机调试,mac os开发android应用通过usb真机调试

    当前开发用的ari,sublime + webstrom 开发node.js还比较流畅,还没有出现被堵被坑的局面. 尝试开发android应用时,各种坑,其中一个比较深的坑就是今天要和大家分享的. m ...

  4. kotlin开发Android入门篇八Kotlin开发Android的基本使用

    基础篇:Kotlin开发Android的基本使用 使用AndroidStudio3.0及以上开发Kotlin在新建项目中勾选这个选项则会默认开发语言为Koltin,然后再gradle(Project的 ...

  5. Android仿支付宝UI功能开发,Android 自定义view仿支付宝咻一咻功能

    支付宝上有一个咻一咻的功能,就是点击图片后四周有水波纹的这种效果,今天也写一个类似的功能. 效果如下所示: 思路: 就是几个圆的半径不断在变大,这个可以使用动画缩放实现,还有透明动画 还有就是这是好几 ...

  6. eclipse android 第一个程序,Eclipse 开发 Android,第一个 HelloWord 程序(学习1)-Fun言

    名词解释: SDK:Software Development Kit AVD:Android Virtual Devices ADT:Android Development Tools 1. Inst ...

  7. Android使用OpenGL ES 3.0实现随手指旋转3D立方体

    OpenGL ES在做普通应用方面3D使用的不多,但有时候实现一些有趣的功能也是蛮不错的.画立方体的的demo网上已经很多了,这次我们就实现一个随手指旋转的立方体,这个demo基本可以了解各个坐标系转 ...

  8. Android深入浅出系列之实例应用—简单的手指拖动图片,图片滑来滑去显示应用Gallery和BaseAdapter以及ImageView的使用...

    前言 我们现在在随便一个手机上用手指在屏幕上滑来滑都可以去拖动图片,其实在Android里这很简单,下面我就给大家具体讲解一下. 思路   我们首先需要Gallery这个对象,俗称画廊对象,大家都知道 ...

  9. go语言能编android程序吗,用 Golang 开发 Android 应用(二)—— 简单 UI-Go语言中文社区...

    计划按以下的内容更新 简单 UI 关于开发一个应用,要有自己的应用名(显示用),和包名(真正唯一的应用名),简单说一台 Android 手机中所有应用的包名是唯一的,如果新安装的应用包名和已安装的应用 ...

最新文章

  1. Go-err is shadowed during return
  2. java基本数据类型线程_Java基本数据类型
  3. php和mysql两种不同方式的分割字符串和类型转换
  4. 57张PPT彻底搞清楚区块链技术。。
  5. 全源最短路径之弗洛伊德算法(C语言)
  6. vxworks linux 多线程api,vxWorks操作系统的多线程实现研究
  7. php dsn port,PHP的InfluxDB客户端库使用
  8. 基于C语言开发的教师管理系统
  9. gvim【一】【安装和基本使用】
  10. 水彩风建筑效果图制作教程
  11. 省考计算机专业课考什么,计算机考研专业课考什么
  12. JLINK+AXD 调试出现 RDI Warning 00005:Data Abort 问题的解决办法
  13. 3dmax简单的bip制作
  14. RationalDMIS 2020高级编程之提取数据OBTAIN语句
  15. 一般熟练盲打需要多久_练习拼音打字练到[盲打]的境界一般需要多久(时间)?...
  16. 武汉java开发工资一般多少_武汉Java开发工资是否还会增长?工资为什么那么高?...
  17. 【GNN报告】腾讯AI lab 徐挺洋:图生成模型及其在分子生成中的应用
  18. 云更新无盘服务器缓存,云更新无盘服务器缓存设置
  19. 用三种方式安装Nginx
  20. 知识图谱评价指标的学习笔记——MAP,MRR

热门文章

  1. C语言的头文件和宏定义详解
  2. 5G毫米波和超宽带信号的验证和测试
  3. SecureCRT取消Session记录的密码
  4. [Swift]URL编码|CharacterSet字符集
  5. Java反射:使用Annotation功能
  6. 蚂蚁花呗的钱从哪里来?
  7. Python——annoy的安装如何安装包
  8. html5 retina 1像素,HTML5 canvas 在 iPhone 4 retina 屏幕上的优化
  9. ArcGIS API for Javascript学习
  10. UCSB与UCI计算机专业,UCSB和UCI录取拿到选择去哪个?