分享一则最近流行的笑话:

最新科学研究表明:寒冷可以使人保持年轻,楼下的王大爷表示虽然今年已经60多岁了,但是仍然冷的跟孙子一样。

呃。好吧,这个冬天确实有点冷,在广州活生生的把我这个原生北方人,冻成一条狗。(研究表明:寒冷可以让人类基因突变。。。。)

好了不扯了。前些日子有朋友让我写博客来分析一下这个仿MIUI的时钟,从中学到了一些炫酷效果的实现。

本项目地址:https://github.com/githubwing/compassView

那么是啥3D效果呢,先来看看效果图,额。。有好多个:



这里写图片描述

其实后两个都是png来的。。

转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50590051

那么请问,看到图形的变换,你想到了什么?

没错!就是Matrix。

关于Matrix你可以到爱哥的博客了解到及其详细的讲解(谢谢爱哥!)。

下面我们就来研究一下如何用矩阵,实现这个3d的效果。

首先新建自定义view类。


  1. public class TDView extends View {
  2. private Paint mPaint;
  3. private int mCenterX;
  4. private int mCenterY;
  5. public TDView(Context context, AttributeSet attrs) {
  6. super(context, attrs);
  7. mPaint = new Paint();
  8. }
  9. }

然后在圆心画一个圆出来

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. mCenterX = getWidth() / 2;
  4. mCenterY = getHeight() / 2;
  5. canvas.drawCircle(mCenterX,mCenterY,100,mPaint);
  6. }

现在是这样的:

我们知道,处理一个图片的时候(切错)可以使用矩阵来处理,同时处理X,Y的话可以使用Camera类,camera可以生成一个指定效果的矩阵。直接来看用法:

  1. private Camera mCamera;
  2. private Matrix mMatrix
  3. mMatrix = new Matrix();
  4. mCamera = new Camera();

在onDraw里 把camera给旋转一下,并把生成的矩阵给一个矩阵。再把矩阵应用到canvas,看一下效果。

  1. mMatrix.reset();
  2. mCamera.save();
  3. mCamera.rotateX(10);
  4. mCamera.rotateY(20);
  5. mCamera.getMatrix(mMatrix);
  6. mCamera.restore();
  7. //将矩阵作用于整个canvas
  8. canvas.concat(mMatrix);
  9. ```
  10. 这里写图片描述
  11. ![这里写图片描述](http://img.blog.csdn.net/20160127001011557)
  12. 呃。。。确实是变形了。。但是好像不是我们想要的结果?
  13. 这是因为,矩阵的变换坐标总从左上角(0,0)开始。所以我们要把变换的坐标改为中心点,方法如下:
  1. mMatrix.reset();
  2. mCamera.save();
  3. mCamera.rotateX(10);
  4. mCamera.rotateY(20);
  5. mCamera.getMatrix(mMatrix);
  6. mCamera.restore();
  7. //改变矩阵作用点
  8. mMatrix.preTranslate(-mCenterX, -mCenterY);
  9. mMatrix.postTranslate(mCenterX, mCenterY);
  10. canvas.concat(mMatrix);

此时的效果看起来像是向左倾斜了:

这里写图片描述

接下来让他跟随手指移动,重写onTouchEvent:

  1. @Override
  2. public boolean onTouchEvent(MotionEvent event) {
  3. float x = event.getX();
  4. float y = event.getY();
  5. int action = event.getActionMasked();
  6. switch (action) {
  7. case MotionEvent.ACTION_MOVE: {
  8. //这里将camera旋转的变量赋值
  9. mCanvasRotateY = y;
  10. mCanvasRotateX = x;
  11. invalidate();
  12. return true;
  13. }
  14. case MotionEvent.ACTION_UP: {
  15. //这里将camera旋转的变量赋值
  16. mCanvasRotateY = 0;
  17. mCanvasRotateX = 0;
  18. invalidate();
  19. return true;
  20. }
  21. }
  22. return super.onTouchEvent(event);
  23. }
  24. 哈哈。。看看是什么效果:
  25. 这里写图片描述
  26. ![这里写图片描述](http://img.blog.csdn.net/20160127002007112)
  27. 什么鬼,怎么跟转硬币一样。 因为旋转的X,Y给的太大了呗。所以要约束一下。
  28. 定义一个旋转最大值

private float mCanvasMaxRotateDegree = 20;

  1. 再用percent思想(前面博客有提到),来处理手指触摸点和这个度数变化的关系:

private void rotateCanvasWhenMove(float x, float y) {

float dx = x - mCenterX;

float dy = y - mCenterY;

  1. float percentX = dx / mCenterX;
  2. float percentY = dy /mCenterY;
  3. if (percentX > 1f) {
  4. percentX = 1f;
  5. } else if (percentX < -1f) {
  6. percentX = -1f;
  7. }
  8. if (percentY > 1f) {
  9. percentY = 1f;
  10. } else if (percentY < -1f) {
  11. percentY = -1f;
  12. }
  13. mCanvasRotateY = mCanvasMaxRotateDegree * percentX;
  14. mCanvasRotateX = -(mCanvasMaxRotateDegree * percentY);
  15. }

最后将TouchEvent里面的ACTION_MOVE 调用此函数即可。

此时,完整的代码如下:

  1. private int mCenterX;
  2. private int mCenterY;
  3. private float mCanvasRotateX = 0;
  4. private float mCanvasRotateY = 0;
  5. private float mCanvasMaxRotateDegree = 20;
  6. private Matrix mMatrix = new Matrix();
  7. private Camera mCamera = new Camera();
  8. private Paint mPaint;
  9. public TDView(Context context) {
  10. super(context);
  11. }
  12. public TDView(Context context, AttributeSet attrs) {
  13. super(context, attrs);
  14. mPaint = new Paint();
  15. mCanvasMaxRotateDegree = 20;
  16. }
  17. @Override
  18. protected void onDraw(Canvas canvas) {
  19. mCenterX = getWidth() / 2;
  20. mCenterY = getHeight() / 2;
  21. rotateCanvas(canvas);
  22. canvas.drawCircle(mCenterX, mCenterY, 100, mPaint);
  23. }
  24. private void rotateCanvas(Canvas canvas) {
  25. mMatrix.reset();
  26. mCamera.save();
  27. mCamera.rotateX(mCanvasRotateX);
  28. mCamera.rotateY(mCanvasRotateY);
  29. mCamera.getMatrix(mMatrix);
  30. mCamera.restore();
  31. mMatrix.preTranslate(-mCenterX, -mCenterY);
  32. mMatrix.postTranslate(mCenterX, mCenterY);
  33. canvas.concat(mMatrix);
  34. }
  35. @Override
  36. public boolean onTouchEvent(MotionEvent event) {
  37. float x = event.getX();
  38. float y = event.getY();
  39. int action = event.getActionMasked();
  40. switch (action) {
  41. case MotionEvent.ACTION_DOWN: {
  42. rotateCanvasWhenMove(x, y);
  43. return true;
  44. }
  45. case MotionEvent.ACTION_MOVE: {
  46. rotateCanvasWhenMove(x, y);
  47. invalidate();
  48. return true;
  49. }
  50. case MotionEvent.ACTION_UP: {
  51. mCanvasRotateY = 0;
  52. mCanvasRotateX = 0;
  53. invalidate();
  54. return true;
  55. }
  56. }
  57. return super.onTouchEvent(event);
  58. }
  59. private void rotateCanvasWhenMove(float x, float y) {
  60. float dx = x - mCenterX;
  61. float dy = y - mCenterY;
  62. float percentX = dx / mCenterX;
  63. float percentY = dy /mCenterY;
  64. if (percentX > 1f) {
  65. percentX = 1f;
  66. } else if (percentX < -1f) {
  67. percentX = -1f;
  68. }
  69. if (percentY > 1f) {
  70. percentY = 1f;
  71. } else if (percentY < -1f) {
  72. percentY = -1f;
  73. }
  74. mCanvasRotateY = mCanvasMaxRotateDegree * percentX;
  75. mCanvasRotateX = -(mCanvasMaxRotateDegree * percentY);
  76. }
  77. }

简简单单100行代码,实现了3D效果的view:

接下来做什么呢?

当然是把这个效果加到我们的自定义view里面!

比如,把我的PanelView加上这个效果:

这里写图片描述



哗!瞬间高大上!

那么,你要不要跟我趁热来一发自定义view?

说搞就搞!

在原有类上进行修改

给一个好看的底色,画一条线

  1. mBgColor = Color.parseColor("#227BAE");
  2. canvas.drawLine(mCenterX,100,mCenterX,130,mPaint);

这里写图片描述



嗯。不错 有条线了。微调下间距,旋转画布,画出整个圆形来:

  1. //保存坐标系
  2. canvas.save();
  3. for (int i = 0; i < 120; i++) {
  4. canvas.rotate(3,mCenterX,mCenterY);
  5. canvas.drawLine(mCenterX, 150, mCenterX, 170, mPaint);
  6. }
  7. //恢复坐标系
  8. canvas.restore();



嗯。。看起来想点样子了。 接下来调整透明度。

  1. canvas.save();
  2. for (int i = 0; i < 120; i++) {
  3. //根据i调整透明度alpha
  4. mPaint.setAlpha(255-(mAlpha * i/120));
  5. canvas.drawLine(mCenterX, 150, mCenterX, 170, mPaint);
  6. canvas.rotate(3,mCenterX,mCenterY);
  7. }
  8. canvas.restore();

这里写图片描述

哈哈。。有没有点意思呢。。



我们画个圆球上去!画之前先ctrl + alt + m 把之前画弧的方法提出来。

画一个紧挨着的圆

private void drawCircle(Canvas canvas) {

mPaint.setAlpha(255);

canvas.drawCircle(mCenterX,213,10,mPaint);

}

这里写图片描述



不错不错,给点动态效果吧,让圆点跟着我们触摸的地方走。怎么做呢。。 当然还是旋转画布了!

这里需要注意的是触摸点与12点钟方向形成的夹角计算。画图分析一下



可以看到 我们只需要调用Math.atan方法即可算出a的弧度,再将其转换为角度即可,在进行3D旋转之前,旋转画布:

  1. protected void onDraw(Canvas canvas) {
  2. canvas.drawColor(mBgColor);
  3. mCenterX = getWidth() / 2;
  4. mCenterY = getHeight() / 2;
  5. Log.e("wing",alpha+"");
  6. canvas.rotate((float) alpha,mCenterX,mCenterY);
  7. alpha = Math.atan((mTouchX-mCenterX)/(mCenterY-mTouchY));
  8. alpha = Math.toDegrees(alpha);
  9. if(mTouchY>mCenterY){
  10. alpha = alpha+180;

现在看一下效果:



这里写图片描述

效果出来了,但是还美中不足呀。 因为中间太空了,所以这个3D效果看起来有点奇怪。那就给他中间加点东西吧! 比如一个指针。

  1. private void drawPath(Canvas canvas) {
  2. mPath.moveTo(mCenterX,223);
  3. mPath.lineTo(mCenterX-30,mCenterY);
  4. mPath.lineTo(mCenterX,2*mCenterY-223);
  5. mPath.lineTo(mCenterX+30,mCenterY);
  6. mPath.lineTo(mCenterX,233);
  7. mPath.close();
  8. canvas.drawPath(mPath,mPaint);
  9. mPaint.setColor(Color.parseColor("#55227BAE"));
  10. canvas.drawCircle(mCenterX,mCenterY,20,mPaint);
  11. }

最后大功告成!!! 看效果!

这里写图片描述

如果你喜欢我的博客,请点击关注。欢迎评论~~

下一篇! 手把手教你做一个qq下拉抢红包:打开链接

一个炫字都不够??!!!手把手带你打造3D自定义view的更多相关文章

  1. wing带你玩转自定义view系列(1) 仿360内存清理效果

    本篇是接自 手把手带你做自定义view系列 宗旨都是一样,带大家一起来研究自定义view的实现,与其不同的是本系列省去了简单的坐标之类的讲解,重点在实现思路,用简洁明了的文章,来与大家一同一步步学习. ...

  2. wing带你玩转自定义view系列(2) 简单模仿qq未读消息去除效果

    上一篇介绍了贝塞尔曲线的简单应用 仿360内存清理效果 这一篇带来一个  两条贝塞尔曲线的应用 : 仿qq未读消息去除效果. 转载请注明出处:http://blog.csdn.net/wingicho ...

  3. wing带你玩转自定义view系列(3)模仿微信下拉眼睛

    发现了爱神的自定义view系列,我只想说一个字:凸(艹皿艹 ) !!相见恨晚啊,早看到就不会走这么多弯路了 另外相比之下我这完全是小儿科..所以不说了,这篇是本系列完结篇....我要从零开始跟随爱哥脚 ...

  4. 手把手带你打造一个 Android 热修复框架(上篇)

    本文来自网易云社区 作者:王晨彦 前言 热修复和插件化是目前 Android 领域很火热的两门技术,也是 Android 开发工程师必备的技能. 目前比较流行的热修复方案有微信的 Tinker,手淘的 ...

  5. 手把手带你打造一个 Android 热修复框架

    本文来自网易云社区 作者:王晨彦 Application 处理 上面我们已经对所有 class 文件插入了 Hack 的引用,而插入 dex 是在 Application 中,Application ...

  6. 手把手带你使用JS-SDK自定义微信分享效果

    https://www.cnblogs.com/backtozero/p/7064247.html

  7. 你也可以自己写一个可爱 & 小资风格的Android加载等待自定义View - 转

    http://blog.csdn.net/carson_ho/article/details/77712072

  8. 手把手带你做一个超炫酷loading成功动画view Android自定义view

    写在前面: 本篇可能是手把手自定义view系列最后一篇了,实际上我也是一周前才开始真正接触自定义view,通过这一周的练习,基本上已经熟练自定义view,能够应对一般的view需要,那么就以本篇来结尾 ...

  9. 手把手带你画一个 时尚仪表盘 Android 自定义View

    拿到美工效果图,咱们程序员就得画得一模一样. 为了不被老板喷,只能多练啊. 听说你觉得前面几篇都so easy,那今天就带你做个相对比较复杂的. 转载请注明出处:http://blog.csdn.ne ...

随机推荐

  1. Jmeter(二)_基础元件

    测试计划(Test Plan) 它用来描述一个测试方案,包含与本次性能测试所有相关的功能.也就说本次测试的所有内容是于基于一个计划的. "函数测试模式"复选框,如果被选择,它会使J ...

  2. spring-boot配置静态资源映射的坑:properties文件不能添加注释

    如此博文所述,Spring Boot 对静态资源映射提供了默认配置 默认将 /** 所有访问映射到以下目录:classpath:/staticclasspath:/publicclasspath:/r ...

  3. Spring Security基于Java配置

    Maven依赖 <dependencies> <!-- ... other dependency elements ... --> <dependency> < ...

  4. mongo 读分析

    分布式读 读冲突 分布式中数据库有多份数据,各份数据可能存在不一致性. mongo 只会写到primary节点上,理论上来说不会有文档冲突,也就是说数据库中的数据都以primary节点为标准. 但是有 ...

  5. OpenResty和Resis一些基本的性能配置

    Basics: 1. Ensure that you have not disabled Lua code cache: https://github.com/openresty/lua-nginx- ...

  6. Dynamics CRM REST Builder

    今天介绍个很棒的工具叫CRM REST Builder,不管是2016之前的odata查询或者现在的web api都不在话下,界面如下,选项非常丰富 这里以retrieve multiple举个例子, ...

  7. ubuntu日志文件管理

    众所周知,ubuntu的日志文件会越来越大,需要定期管理 logrotate是个十分有用的工具,它可以自动对日志进行截断(或轮循).压缩以及删除旧的日志文件.例如,你可以设置logrotate,让/v ...

  8. eval和列表解析的一处陷阱

    >>> def f(): a=1 return [i+a for i in range(3)] >>> f() [1, 2, 3] >>> def ...

  9. SSO 基于CAS实现单点登录 实例解析(二)

    本文目录: 概述 演示环境 部署CAS-Server相关的Tomcat 部署CAS-Client相关的Tomcat 测试验证SSO 第一: 本demo在一个机器上实现(三个虚拟主机),来看SSO单点登 ...

  10. JSP 2.x 自定义标签

    JSP 1.x的标签,虽然使用起来非常灵活,但是比较复杂,JSP 2.x提供了一组简化的标签写法 SimpleTagSupport是SimpleTag接口的子类,同时支持参数和标签体,最核心的方法时d ...