文档章节

Button 不错的点击效果

竹草席
 竹草席
发布于 2015/06/16 18:43
字数 2017
阅读 172
收藏 15


官方的:


我们上个版本的:


可以看出谷歌的是有位移效果,而我们的是原地扩散的效果,当然动画速度这个与PS的设置有关,不做比较,实际速度比上面的略快。

下面咱们就来试试做做位移的特效,先画个图给大家看看:


相信大家都能看懂,第一种就是之前的实现方式,只是在原地扩散,第二种就是新的,将在扩散的同时向中心靠拢,且为了达到更加好的视觉效果,靠拢中心的XY轴速度并不是一样的,X轴的靠拢时间=整个扩散时间,向Y轴靠拢的时间~=整个扩散时间*0.3(且都是先快后慢),现在来看看成品效果:


点击中间的时候与第一种差距不大,但是点击两边的时候将会有明显的差距,能感觉到向中心靠拢的触觉。是不是和谷歌的相比起来又近了一些了?


说了这个多的理论与演示,下面来说说整个的实现:

首先我们抽取上一篇文章的成果作为这篇的开头,具体怎么新建控件就不再做介绍了,先看看上一篇的代码成果(该代码进行了一定的修改):


  1. public class MaterialButton extends Button {  

  2.     private static final Interpolator ANIMATION_INTERPOLATOR = new DecelerateInterpolator();  

  3.     private static final long ANIMATION_TIME = 600;  

  4.   

  5.     private Paint backgroundPaint;  

  6.     private static ArgbEvaluator argbEvaluator = new ArgbEvaluator();  

  7.     private float paintX, paintY, radius;  

  8.   

  9.   

  10.     public MaterialButton(Context context) {  

  11.         super(context);  

  12.         init(null0);  

  13.     }  

  14.   

  15.     public MaterialButton(Context context, AttributeSet attrs) {  

  16.         super(context, attrs);  

  17.         init(attrs, 0);  

  18.     }  

  19.   

  20.     public MaterialButton(Context context, AttributeSet attrs, int defStyle) {  

  21.         super(context, attrs, defStyle);  

  22.         init(attrs, defStyle);  

  23.     }  

  24.   

  25.     @SuppressWarnings("deprecation")  

  26.     private void init(AttributeSet attrs, int defStyle) {  

  27.         ...  

  28.     }  

  29.   

  30.   

  31.     @SuppressWarnings("NullableProblems")  

  32.     @Override  

  33.     protected void onDraw(Canvas canvas) {  

  34.         canvas.save();  

  35.         canvas.drawCircle(paintX, paintY, radius, backgroundPaint);  

  36.         canvas.restore();  

  37.   

  38.         super.onDraw(canvas);  

  39.     }  

  40.   

  41.     @SuppressWarnings("NullableProblems")  

  42.     @Override  

  43.     public boolean onTouchEvent(MotionEvent event) {  

  44.         if (event.getAction() == MotionEvent.ACTION_DOWN) {  

  45.             paintX = event.getX();  

  46.             paintY = event.getY();  

  47.             startRoundAnimator();  

  48.         }  

  49.         return super.onTouchEvent(event);  

  50.     }  

  51.   

  52.     /** 

  53.      * ============================================================================================= 

  54.      * The Animator methods 

  55.      * ============================================================================================= 

  56.      */  

  57.   

  58.     /** 

  59.      * Start Round Animator 

  60.      */  

  61.     private void startRoundAnimator() {  

  62.         float start, end, height, width;  

  63.         long time = (long) (ANIMATION_TIME * 1.85);  

  64.   

  65.         //Height Width  

  66.         height = getHeight();  

  67.         width = getWidth();  

  68.   

  69.         //Start End  

  70.         if (height < width) {  

  71.             start = height;  

  72.             end = width;  

  73.         } else {  

  74.             start = width;  

  75.             end = height;  

  76.         }  

  77.   

  78.         float startRadius = (start / 2 > paintY ? start - paintY : paintY) * 1.15f;  

  79.         float endRadius = (end / 2 > paintX ? end - paintX : paintX) * 0.85f;  

  80.   

  81.         //If The approximate square approximate square  

  82.         if (startRadius > endRadius) {  

  83.             startRadius = endRadius * 0.6f;  

  84.             endRadius = endRadius / 0.8f;  

  85.             time = (long) (time * 0.5);  

  86.         }  

  87.   

  88.         AnimatorSet set = new AnimatorSet();  

  89.         set.playTogether(  

  90.                 ObjectAnimator.ofFloat(this, mRadiusProperty, startRadius, endRadius),  

  91.                 ObjectAnimator.ofObject(this, mBackgroundColorProperty, argbEvaluator, attributes.getColor(1), attributes.getColor(2))  

  92.         );  

  93.         // set Time  

  94.         set.setDuration((long) (time / end * endRadius));  

  95.         set.setInterpolator(ANIMATION_INTERPOLATOR);  

  96.         set.start();  

  97.     }      

  98.   

  99.   

  100.     /** 

  101.      * ============================================================================================= 

  102.      * The custom properties 

  103.      * ============================================================================================= 

  104.      */  

  105.   

  106.       

  107.     private Property<MaterialButton, Float> mRadiusProperty = new Property<MaterialButton, Float>(Float.class"radius") {  

  108.         @Override  

  109.         public Float get(MaterialButton object) {  

  110.             return object.radius;  

  111.         }  

  112.   

  113.         @Override  

  114.         public void set(MaterialButton object, Float value) {  

  115.             object.radius = value;  

  116.             invalidate();  

  117.         }  

  118.     };  

  119.   

  120.     private Property<MaterialButton, Integer> mBackgroundColorProperty = new Property<MaterialButton, Integer>(Integer.class"bg_color") {  

  121.         @Override  

  122.         public Integer get(MaterialButton object) {  

  123.             return object.backgroundPaint.getColor();  

  124.         }  

  125.   

  126.         @Override  

  127.         public void set(MaterialButton object, Integer value) {  

  128.             object.backgroundPaint.setColor(value);  

  129.         }  

  130.     };  

  131.   

  132. }  

在上述代码中我们实现了点击时进行扩散的效果,初始化控件部分由于我加入了许多的代码这里删除了,具体可以看看我的项目实现,最后会给出地址。

现在基于此开工!

首先我们建立 两个新的属性 分别X坐标与Y坐标属性:


  1. private Property<MaterialButton, Float> mPaintXProperty = new Property<MaterialButton, Float>(Float.class"paintX") {  

  2.         @Override  

  3.         public Float get(MaterialButton object) {  

  4.             return object.paintX;  

  5.         }  

  6.   

  7.         @Override  

  8.         public void set(MaterialButton object, Float value) {  

  9.             object.paintX = value;  

  10.         }  

  11.     };  

  12.   

  13.     private Property<MaterialButton, Float> mPaintYProperty = new Property<MaterialButton, Float>(Float.class"paintY") {  

  14.         @Override  

  15.         public Float get(MaterialButton object) {  

  16.             return object.paintY;  

  17.         }  

  18.   

  19.         @Override  

  20.         public void set(MaterialButton object, Float value) {  

  21.             object.paintY = value;  

  22.         }  

  23.     };  

在这两个属性中并未调用第一篇所说的 “ invalidate();”方法进行界面刷新,因为该方法应该放在持续时间最长的半径属性中调用。

之后我们获取到高宽 以及根据高和宽 计算出对应的 开始半径与结束半径:


  1. <span style="white-space:pre">    </span>float start, end, height, width, speed = 0.3f;  

  2.         long time = ANIMATION_TIME;  

  3.   

  4.         //Height Width  

  5.         height = getHeight();  

  6.         width = getWidth();  

  7.   

  8.         //Start End  

  9.         if (height < width) {  

  10.             start = height;  

  11.             end = width;  

  12.         } else {  

  13.             start = width;  

  14.             end = height;  

  15.         }  

  16.         start = start / 2 > paintY ? start - paintY : paintY;  

  17.         end = end * 0.8f / 2f;  

  18.   

  19.         //If The approximate square approximate square  

  20.         if (start > end) {  

  21.             start = end * 0.6f;  

  22.             end = end / 0.8f;  

  23.             time = (long) (time * 0.65);  

  24.             speed = 1f;  

  25.         }  

我们首先比较了高与宽的长度 把短的赋予为开始半径 长的赋予为结束半径。

第二步,我们把开始长度除以2  得出其一半的长度 然后与 点击时的Y轴坐标比较,如果Y轴较长则取Y,如果不够则取其相减结果。这样能保证点击开始时的半径能刚好大于其高或者宽(短的一边),这样就不会出现小圆扩散的效果,看起来将会由椭圆的效果(当然以后将会直接画出椭圆)

第三步,我们运算出结束半径,同时保证结束半径为长的一边的一半的8/10 这样的效果是不会出现布满整个控件的情况。8/10 的空间刚好是个不错的选择。

第四步,判断开始长度是否大于结束长度,如果是(近似正方形情况),进行一定规则的重新运算,保证其开始半径能刚好与控件长度差不多(0.48左右),结束半径能刚刚布满控件,同时减少动画时间

当然,我现在才发现了一个BUG,在第二步的地方的BUG,大家看看,希望能提出是哪里的BUG;就当是一个互动!该BUG将会在下个版本修复。


之后我们建立每个属性的动画,并给每个属性动画设置对应的时间:

  1.         //PaintX  

  2.         ObjectAnimator aPaintX = ObjectAnimator.ofFloat(this, mPaintXProperty, paintX, width / 2);  

  3.         aPaintX.setDuration(time);  

  4.         //PaintY  

  5.         ObjectAnimator aPaintY = ObjectAnimator.ofFloat(this, mPaintYProperty, paintY, height / 2);  

  6.         aPaintY.setDuration((long) (time * speed));  

  7.         //Radius  

  8.         ObjectAnimator aRadius = ObjectAnimator.ofFloat(this, mRadiusProperty, start, end);  

  9.         aRadius.setDuration(time);  

  10.         //Background  

  11.         ObjectAnimator aBackground = ObjectAnimator.ofObject(this, mBackgroundColorProperty, argbEvaluator, attributes.getColor(1), attributes.getColor(2));  

  12.         aBackground.setDuration(time);  


可以看见Y轴的时间乘以了一个speed变量,该变量默认是0.3 如果是近似正方形将初始化为1以便能同时对齐到中心位置,在上一步中有对应变量。

然后咱们把所有的属性动画添加到一个动画集并设置其速度方式为:先快后慢。最后启动该动画集。

  1. //AnimatorSet  

  2. AnimatorSet set = new AnimatorSet();  

  3. set.playTogether(aPaintX, aPaintY, aRadius, aBackground);  

  4. set.setInterpolator(ANIMATION_INTERPOLATOR);  

  5. set.start();  


以上就是最新的动画效果的实现原理及代码了,当然我们可以将其合并到第一篇的代码中,并使用一个 Bool 属性来控制使用哪一种动画:

  1. public class MaterialButton extends Button {  

  2.     private static final Interpolator ANIMATION_INTERPOLATOR = new DecelerateInterpolator();  

  3.     private static final long ANIMATION_TIME = 600;  

  4.   

  5.     private Paint backgroundPaint;  

  6.     private static ArgbEvaluator argbEvaluator = new ArgbEvaluator();  

  7.     private float paintX, paintY, radius;  

  8.     private Attributes attributes;  

  9.   

  10.     public MaterialButton(Context context) {  

  11.         super(context);  

  12.         init(null0);  

  13.     }  

  14.   

  15.     public MaterialButton(Context context, AttributeSet attrs) {  

  16.         super(context, attrs);  

  17.         init(attrs, 0);  

  18.     }  

  19.   

  20.     public MaterialButton(Context context, AttributeSet attrs, int defStyle) {  

  21.         super(context, attrs, defStyle);  

  22.         init(attrs, defStyle);  

  23.     }  

  24.   

  25.     @SuppressWarnings("deprecation")  

  26.     private void init(AttributeSet attrs, int defStyle) {  

  27.         ...  

  28.     }  

  29.   

  30.   

  31.     @SuppressWarnings("NullableProblems")  

  32.     @Override  

  33.     protected void onDraw(Canvas canvas) {  

  34.         canvas.save();  

  35.         canvas.drawCircle(paintX, paintY, radius, backgroundPaint);  

  36.         canvas.restore();  

  37.   

  38.         super.onDraw(canvas);  

  39.     }  

  40.   

  41.     @SuppressWarnings("NullableProblems")  

  42.     @Override  

  43.     public boolean onTouchEvent(MotionEvent event) {  

  44.         if (attributes.isMaterial() && event.getAction() == MotionEvent.ACTION_DOWN) {  

  45.             paintX = event.getX();  

  46.             paintY = event.getY();  

  47.             if (attributes.isAutoMove())  

  48.                 startMoveRoundAnimator();  

  49.             else  

  50.                 startRoundAnimator();  

  51.         }  

  52.         return super.onTouchEvent(event);  

  53.     }  

  54.   

  55.     /** 

  56.      * ============================================================================================= 

  57.      * The Animator methods 

  58.      * ============================================================================================= 

  59.      */  

  60.   

  61.     /** 

  62.      * Start Round Animator 

  63.      */  

  64.     private void startRoundAnimator() {  

  65.         float start, end, height, width;  

  66.         long time = (long) (ANIMATION_TIME * 1.85);  

  67.   

  68.         //Height Width  

  69.         height = getHeight();  

  70.         width = getWidth();  

  71.   

  72.         //Start End  

  73.         if (height < width) {  

  74.             start = height;  

  75.             end = width;  

  76.         } else {  

  77.             start = width;  

  78.             end = height;  

  79.         }  

  80.   

  81.         float startRadius = (start / 2 > paintY ? start - paintY : paintY) * 1.15f;  

  82.         float endRadius = (end / 2 > paintX ? end - paintX : paintX) * 0.85f;  

  83.   

  84.         //If The approximate square approximate square  

  85.         if (startRadius > endRadius) {  

  86.             startRadius = endRadius * 0.6f;  

  87.             endRadius = endRadius / 0.8f;  

  88.             time = (long) (time * 0.5);  

  89.         }  

  90.   

  91.         AnimatorSet set = new AnimatorSet();  

  92.         set.playTogether(  

  93.                 ObjectAnimator.ofFloat(this, mRadiusProperty, startRadius, endRadius),  

  94.                 ObjectAnimator.ofObject(this, mBackgroundColorProperty, argbEvaluator, attributes.getColor(1), attributes.getColor(2))  

  95.         );  

  96.         // set Time  

  97.         set.setDuration((long) (time / end * endRadius));  

  98.         set.setInterpolator(ANIMATION_INTERPOLATOR);  

  99.         set.start();  

  100.     }  

  101.   

  102.     /** 

  103.      * Start Move Round Animator 

  104.      */  

  105.     private void startMoveRoundAnimator() {  

  106.         float start, end, height, width, speed = 0.3f;  

  107.         long time = ANIMATION_TIME;  

  108.   

  109.         //Height Width  

  110.         height = getHeight();  

  111.         width = getWidth();  

  112.   

  113.         //Start End  

  114.         if (height < width) {  

  115.             start = height;  

  116.             end = width;  

  117.         } else {  

  118.             start = width;  

  119.             end = height;  

  120.         }  

  121.         start = start / 2 > paintY ? start - paintY : paintY;  

  122.         end = end * 0.8f / 2f;  

  123.   

  124.         //If The approximate square approximate square  

  125.         if (start > end) {  

  126.             start = end * 0.6f;  

  127.             end = end / 0.8f;  

  128.             time = (long) (time * 0.65);  

  129.             speed = 1f;  

  130.         }  

  131.   

  132.         //PaintX  

  133.         ObjectAnimator aPaintX = ObjectAnimator.ofFloat(this, mPaintXProperty, paintX, width / 2);  

  134.         aPaintX.setDuration(time);  

  135.         //PaintY  

  136.         ObjectAnimator aPaintY = ObjectAnimator.ofFloat(this, mPaintYProperty, paintY, height / 2);  

  137.         aPaintY.setDuration((long) (time * speed));  

  138.         //Radius  

  139.         ObjectAnimator aRadius = ObjectAnimator.ofFloat(this, mRadiusProperty, start, end);  

  140.         aRadius.setDuration(time);  

  141.         //Background  

  142.         ObjectAnimator aBackground = ObjectAnimator.ofObject(this, mBackgroundColorProperty, argbEvaluator, attributes.getColor(1), attributes.getColor(2));  

  143.         aBackground.setDuration(time);  

  144.   

  145.         //AnimatorSet  

  146.         AnimatorSet set = new AnimatorSet();  

  147.         set.playTogether(aPaintX, aPaintY, aRadius, aBackground);  

  148.         set.setInterpolator(ANIMATION_INTERPOLATOR);  

  149.         set.start();  

  150.     }  

  151.   

  152.   

  153.     /** 

  154.      * ============================================================================================= 

  155.      * The custom properties 

  156.      * ============================================================================================= 

  157.      */  

  158.   

  159.     private Property<MaterialButton, Float> mPaintXProperty = new Property<MaterialButton, Float>(Float.class"paintX") {  

  160.         @Override  

  161.         public Float get(MaterialButton object) {  

  162.             return object.paintX;  

  163.         }  

  164.   

  165.         @Override  

  166.         public void set(MaterialButton object, Float value) {  

  167.             object.paintX = value;  

  168.         }  

  169.     };  

  170.   

  171.     private Property<MaterialButton, Float> mPaintYProperty = new Property<MaterialButton, Float>(Float.class"paintY") {  

  172.         @Override  

  173.         public Float get(MaterialButton object) {  

  174.             return object.paintY;  

  175.         }  

  176.   

  177.         @Override  

  178.         public void set(MaterialButton object, Float value) {  

  179.             object.paintY = value;  

  180.         }  

  181.     };  

  182.   

  183.     private Property<MaterialButton, Float> mRadiusProperty = new Property<MaterialButton, Float>(Float.class"radius") {  

  184.         @Override  

  185.         public Float get(MaterialButton object) {  

  186.             return object.radius;  

  187.         }  

  188.   

  189.         @Override  

  190.         public void set(MaterialButton object, Float value) {  

  191.             object.radius = value;  

  192.             invalidate();  

  193.         }  

  194.     };  

  195.   

  196.     private Property<MaterialButton, Integer> mBackgroundColorProperty = new Property<MaterialButton, Integer>(Integer.class"bg_color") {  

  197.         @Override  

  198.         public Integer get(MaterialButton object) {  

  199.             return object.backgroundPaint.getColor();  

  200.         }  

  201.   

  202.         @Override  

  203.         public void set(MaterialButton object, Integer value) {  

  204.             object.backgroundPaint.setColor(value);  

  205.         }  

  206.     };  

  207.   

  208. }  


在最后附上两种方式运行后的效果对比图:




本文转载自:http://blog.csdn.net/qiujuer/article/details/39998961

共有 人打赏支持
竹草席
粉丝 1
博文 44
码字总数 2945
作品 0
兰州
cocos2d-x中实现不规则按钮的点击效果

cocos2d-x中实现不规则按钮的点击效果 先说一点,这是从别人那里扒来的,亲测有效觉得不错,分享一下: 原理很简单,就是判断按钮图片的点击区域 像素点透明度是不是0,需要修改源代码; 我拿...

panpan123_
2017/11/01
0
0
关于你的SlidingMenu 的demo滑动问题

@roc2013 你好,想跟你请教个问题:demo效果很不错,但是有个小bug,打开侧滑菜单后,拖动viewpager和button以外的区域无法拖动关闭侧滑菜单栏,我打log看了,onInterceptTouchEvent(MotionE...

cshxql
2014/03/23
346
0
Android之Button自定义点击效果

我们在界面上经常会用到button按钮,但通常button点击后看不到点击的效果,如果用户连续点击了两次,就会报NAR错误,这样交互性就比较差了。如果我们自定义了button点击效果,比如我们点击了...

晨曦之光
2012/03/13
0
2
翻翻git之---偏向iOS风格的Switch ToggleSwitch

转载请注明出处:王亟亟的大牛之路 早上上了个很6的图片选择器 GalleryFinal 如果没有看的同学 可以看下,东西很不错哦,传送门:http://blog.csdn.net/ddwhan0123/article/details/5081715...

ddwhan0123
2016/03/07
0
0
Vue技术栈仿写的Tabler

Qabler 参考Tabler的项目,使用Vue技术栈来实现。求一波Star,谢谢!Github地址:github.com/QianGuoqing… 效果图 (效果图是动态图,如果图片静止请点击图片查看) Home Interface/Cards De...

杏花微雨
07/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

20180920 rzsz传输文件、用户和用户组相关配置文件与管理

利用rz、sz实现Linux与Windows互传文件 [root@centos01 ~]# yum install -y lrzsz # 安装工具sz test.txt # 弹出对话框,传递到选择的路径下rz # 回车后,会从对话框中选择对应的文件传递...

野雪球
今天
2
0
OSChina 周四乱弹 —— 毒蛇当辣条

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @ 达尔文:分享花澤香菜/前野智昭/小野大輔/井上喜久子的单曲《ミッション! 健?康?第?イチ》 《ミッション! 健?康?第?イチ》- 花澤香菜/前野智...

小小编辑
今天
7
3
java -jar运行内存设置

java -Xms64m #JVM启动时的初始堆大小 -Xmx128m #最大堆大小 -Xmn64m #年轻代的大小,其余的空间是老年代 -XX:MaxMetaspaceSize=128m # -XX:CompressedClassSpaceSize=6...

李玉长
今天
4
0
Spring | 手把手教你SSM最优雅的整合方式

HEY 本节主要内容为:基于Spring从0到1搭建一个web工程,适合初学者,Java初级开发者。欢迎与我交流。 MODULE 新建一个Maven工程。 不论你是什么工具,选这个就可以了,然后next,直至finis...

冯文议
今天
2
0
RxJS的另外四种实现方式(四)——性能最高的库(续)

接上一篇RxJS的另外四种实现方式(三)——性能最高的库 上一篇文章我展示了这个最高性能库的实现方法。下面我介绍一下这个性能提升的秘密。 首先,为了弄清楚Most库究竟为何如此快,我必须借...

一个灰
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部