自定义学习——摩天轮原理_一点点Love的博客-程序员宅基地_摩天轮原理

技术标签: android  

这个例子是从课堂上拿过来的;是一个很好的例子,可以学习手势识别,以及事件分发,特别是转动速度和滑动之间的关系。



public class SkyWheel extends RelativeLayout {


private Paint redPaint;
private Paint blackPaint;
private Paint grayPaint;
// 圆心
PointF center = new PointF();
// 角度间隔
double cellDegree = 0;


float radius = 0;
private GestureDetector gestureDetector;


public SkyWheel(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}


public SkyWheel(Context context, AttributeSet attrs) {
this(context, attrs, 0);


}


public SkyWheel(Context context) {
this(context, null);
}


private void init() {
gestureDetector = new GestureDetector(getContext(), gestureListener);


redPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setColor(Color.RED);
redPaint.setStrokeWidth(2);


blackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
blackPaint.setColor(Color.BLACK);
blackPaint.setStyle(Style.STROKE);
blackPaint.setStrokeWidth(3);


grayPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
grayPaint.setColor(Color.GRAY);
grayPaint.setStrokeWidth(2);
}


double firstChildDegree = 0;


public void setDegree(double degree) {
firstChildDegree = degree;
// requestLayout 会导致onLayout 调用
requestLayout();
// invalidate() -- >onDraw


}


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 把触摸事件都拦下来,交给自己的onTouchEvent 处理
// 不向下传递
return true;
}


@Override
public boolean onTouchEvent(MotionEvent event) {
// 用手势探测器来帮助我们完成功能
gestureDetector.onTouchEvent(event);
// 持续关注触摸事件
return true;
}


// 此方法负责孩子的摆放
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 在摆放之前,前去计算关键值
computeValues();
// 完成孩子的摆放
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);


// 孩子的中心点的角度
double childDegree = firstChildDegree + i * cellDegree;
// 中心点
int childCenterX = (int) (center.x + radius * Math.sin(childDegree));
int childCenterY = (int) (center.y - radius * Math.cos(childDegree));
// 依据中心点摆放孩子在父控件中的位置
child.layout(childCenterX - child.getWidth() / 2, childCenterY
- child.getHeight() / 2, childCenterX + child.getWidth()
/ 2, childCenterY + child.getHeight() / 2);
}
}


@SuppressLint("WrongCall")
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
onDraw(canvas);
}


// ViewGroup在分发绘制的时候,不会调用自己的onDraw方法
// 需要手动触发
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);


// computeValues();


// 绘制圆心
canvas.drawCircle(center.x, center.y, 4, redPaint);
// 绘制圆
canvas.drawCircle(center.x, center.y, radius, blackPaint);
// 绘制孩子中心点与圆心的连线
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
// 孩子的中心点的角度
double childDegree = firstChildDegree + i * cellDegree;
// 中心点
int childCenterX = (int) (center.x + radius * Math.sin(childDegree));
int childCenterY = (int) (center.y - radius * Math.cos(childDegree));
canvas.drawLine(childCenterX, childCenterY, center.x, center.y,
grayPaint);


}


};


private void computeValues() {
// 圆心
center.x = getWidth() / 2;
center.y = getHeight() / 2;
// 半径
int childCount = getChildCount();
// 遍历所有的孩子,求出最宽的孩子的宽度 和 最高的孩子的高度
int widthestChildWidth = 0;
int heightestChildheight = 0;


for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
int childWidth = child.getWidth();
int childHeight = child.getHeight();


if (childWidth > widthestChildWidth) {
widthestChildWidth = childWidth;
}
if (childHeight > heightestChildheight) {
heightestChildheight = childHeight;
}
}
radius = Math.min(getWidth() / 2 - widthestChildWidth / 2, getHeight()
/ 2 - heightestChildheight / 2);


// 角度间隔
cellDegree = Math.PI * 2 / childCount;


}


private OnGestureListener gestureListener = new OnGestureListener() {


// 移动
/**
* @param e1 本组触摸事件的起始事件  down
* @param e2 此时的,最新一次move事件,触发了onScroll调用
* @param distanceX 上次的位置与最新的位置的距离(x)
* @param distanceY 上次的位置与最新的位置的距离(y)

* distance =  上一次   - 本次
* 上一次  = distance + 本次
* delta =  本次 - 上一次   


*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {


double oldPointDegree = getPiontDegree(e2.getX() + distanceX,
e2.getY() + distanceY);
double newPointDegree = getPiontDegree(e2.getX(), e2.getY());


double deltaDegree = newPointDegree - oldPointDegree;


setDegree(firstChildDegree + deltaDegree);


Log.d("onScroll", " ");
// 触摸转动
return false;
}


// 抬起手指时,还有速度
/**
* @param e1 本组触摸事件的起始事件  down
* @param e2 本组触摸事件的最后一次move事件  move
* @param velocityX 手指离开屏幕的速度 ,单位是像素/秒  (水平)
* @param velocityY 手指离开屏幕的速度 ,单位是像素/秒  (垂直)
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// 计速器
// VelocityTracker
// 完成 像素速度 --》角速度的转化


// 获得1毫秒后手指所在的位置


// 求出1毫秒后 手指对应的角度
double degreeAfter1Ms = getPiontDegree(
e2.getX() + velocityX / 1000, e2.getY() + velocityY / 1000);
// 求出现在手指的角度
double degreeCurrent = getPiontDegree(e2.getX(), e2.getY());
// 1ms后 - 现在 = 1ms的角度变化量
double degreeDelta1Ms = degreeAfter1Ms - degreeCurrent;
// 1ms的角度变化量 * 1000 = 角速度
double degreeVelocity = degreeDelta1Ms * 1000;


// 根据角速度 让控件自行转动,并减速,直至停下来
startRotate(degreeVelocity);
Log.d("onFling", " degreeVelocity :" + degreeVelocity);
return false;
}


// 单击
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.d("onSingleTapUp", " ");
int touchIndex = findTouchChildIndex(e.getX(), e.getY());
if (touchIndex >= 0) {
// 触发孩子的点击事件
getChildAt(touchIndex).performClick();
}
return false;
}


// 按下后一小会,还没有移动
@Override
public void onShowPress(MotionEvent e) {
Log.d("onShowPress", " ");
}


// 长按
@Override
public void onLongPress(MotionEvent e) {
Log.d("onLongPress", " ");


}


// 手指按下
@Override
public boolean onDown(MotionEvent e) {
Log.d("onDown", " ");
// 如果正在惯性转动,则停下来
if (valueAnimator != null && valueAnimator.isRunning()) {
// 则停下来
valueAnimator.cancel();
}
return false;
}
};
private ValueAnimator valueAnimator;


protected double getPiontDegree(float x, float y) {
return Math.atan2(y - center.y, x - center.x);
}


protected int findTouchChildIndex(float x, float y) {
// 遍历所有的孩子,判断x和y与孩子的4个边的关系


int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (x > child.getLeft() && x < child.getRight()
&& y > child.getTop() && y < child.getBottom()) {
return i;
}


}


return -1;
}


protected void startRotate(double degreeVelocity) {
// 转动的时间
long duration = (long) (Math.abs(degreeVelocity) / Math.PI * 1000);


// 转动的总角度
double totalDegree = degreeVelocity * duration / 1000 / 2;
valueAnimator = ValueAnimator.ofFloat((float) firstChildDegree,
(float) (firstChildDegree + totalDegree));
valueAnimator.setDuration(duration);
// 应该是先快,后慢,设置减速插值器
valueAnimator.setInterpolator(new DecelerateInterpolator(2));


// 设置更新监听,得到当前值
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {


@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
setDegree(value);
// Log.d("onAnimationUpdate", "" + value);
}
});
valueAnimator.start();


}
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/changjiangquan/article/details/77413581

智能推荐

word 2010中设置默认粘贴为 只保留文本粘贴【visio也适用于快捷键方式】_weixin_30722589的博客-程序员宅基地

VISIO也适用如下方式:3. 当然也可以直接是 Ctrl + Alt + V打开选择性粘贴选项卡 来选择其中某项来粘贴也是可以的转:word 2010中设置默认粘贴为 只保留文本粘贴2012年12月21日 21:19:32 小龙王2010 阅读数 6786更多分类专栏: Microsoft Office ...

mysql8 slap_【原创】mysqlslap 使用总结_weixin_39646628的博客-程序员宅基地

mysqlslap 可以用于模拟服务器的负载,并输出计时信息。其被包含在 MySQL 5.1 的发行包中。测试时,可以指定并发连接数,可以指定 SQL 语句。如果没有指定 SQL 语句,mysqlslap 会自动生成查询 schema 的 SELECT 语句。1. 查看帮助信息。[[email protected] libmysql]# mysqlslap --helpmysqlslap Ver 1.0 Di...

java 词云_java生成词云_我们的美学的博客-程序员宅基地

项目有需求生成词云图片,在hub上找到一个开源jar包,记录下,以备以后查阅。github URL:https://github.com/kennycason/kumopackage com.hp.portal.util;import java.awt.Color;import java.awt.Dimension;import java.io.OutputStream;import java.u...

vsftpd 常见问题_diehuojiang5959的博客-程序员宅基地

一、vsftp服务能开启却连接不上的解决办法: 用虚拟机装了centos,vsftp是用centos自带的。启动vsftd服务后却一直连不上,原因是被防火墙给挡了。 查看防火墙状态:/etc/init.d/iptables status 停掉防火墙:/etc/init.d/iptables stop 也可以永久关闭防火墙:chkconfig --level 3...

安装yum-priorities插件_mengfanteng的博客-程序员宅基地

1.安装yum-priorities插件这个插件是用来设置yum在调用软件源时的顺序的。因为官方提供的软件源,都是比较稳定和被推荐使用的。因此,官方源的顺序要高于第三方源的顺序。如何保证这个顺序,就需要安装yum-priorities这插件了。[[email protected] ~]# yum install yum-priorities #安装yum-prioritiesPS:这里cent

【guava.jar】LoadingCache的使用_Wells·Lee的博客-程序员宅基地_guava.jar

缓存,在我们日常开发中是必不可少的一种解决性能问题的方法。简单的说,cache 就是为了提升系统性能而开辟的一块内存空间。 缓存的主要作用是暂时在内存中保存业务系统的数据处理结果,并且等待下次访问使用。在日常开发的很多场合,由于受限于硬盘IO的? 缓存在很多系统和架构中都用广泛的应用,例如: 1.CPU缓存  2.操作系统缓存  3.本地缓存  4.分布式缓存 

随便推点

小迪教程第八天——注入工具sqlmap的使用_仲夏199603的博客-程序员宅基地_sqlmap注入教程

1、安装过程1)安装python2.72)在python2.7的目录中解压sqlmap 3)在桌面建立快捷方式 2、使用方法1)get类型注入Sqlmap.py -u 注入地址 检测指定站点注入情况,默认会以get类型注入检测 2)post类型注入3)cookie类型注入Sqlmap.py -u 注入地址 –cookie “参数” –level 2 以cookie注入提交检测网站

仿纳科机器人编程_智慧职教云课堂2020工业机器人系统离线编程与仿真章节答案..._weixin_39858132的博客-程序员宅基地

如图:A处通入氯气,关闭B阀时,C处湿润红色布条无变化,打开B阀时,C处湿润的红色布条褪色.则D中可以盛放足量的下列叙述正确的是 [ ]A.CO2通入Na2SiO3溶液中可以得到硅酸 B.因为高温时SiO2与Na2CO3反应放出CO2,所以硅酸下列情况下,最终有沉淀的是[ ]A.向澄清石灰水中通入过量的CO2 B.向澄清石灰水中通入过量的SO2 C.向Na2SiO3(1)氯气可溶于水,常温下,1体...

unity多开同一个工程_sam军的博客-程序员宅基地

多开同一工程 https://www.jianshu.com/p/ebb26bcbd38f在Assets 同级目录下 执行cmdmkdir UnityProject_cpcd UnityProject_cpmklink /J Assets F:\Work\program\code\client\Game\Assetsmklink /J Library F:\Work\progra...

运维自动化工具ansible_weixin_30892037的博客-程序员宅基地

企业级自动化运维工具应用实战ansible公司计划在年底做一次大型市场促销活动,全面冲刺下交易额,为明年的上市做准备。公司要求各业务组对年底大促做准备,运维部要求所有业务容量进行三倍的扩容,并搭建出多套环境可以共开发和测试人员做测试,运维老大为了在年底有所表现,要求运维部门同学尽快实现,当你接到这个任务时,有没有更快的解决方案?Ansible发展史Ansible创始人,Michael ...

android 处理http状态码,AndroidHTTPClient访问状态码200但是内容却是空? 爱问知识人..._weixin_39621695的博客-程序员宅基地

android中测试 跑得了 加点分可以继续追问我哦 呵呵~~~~public static String getHtml( String urlpath,String encoding) throws Exception {URL url = new URL(urlpath);//实例化一个HTTP连接对象connHttpURLConnection conn = (HttpURLConne...

游戏服务器:到底使用UDP还是TCP_陈心朔的博客-程序员宅基地_游戏用tcp还是udp

本文转载于伯乐在线,原文地址:http://blog.jobbole.com/64638/在编写网络游戏的时候,到底使用UDP还是TCP的问题迟早都要面对。一般来说你会听到人们这样说:“除非你正在写一个动作类游戏,否则你就用TCP吧” 或者是 “你能够在MMO游戏中用TCP,因为魔兽世界就用的TCP!”遗憾的是,这些观点都没有反映这个问题的复杂性。

推荐文章

热门文章

相关标签