原创做品,转载请注明出处:http://blog.csdn.net/qiujuer/article/details/39831451
java
前段时间Android L 发布了,相信看过发布会了解过的朋友都为其中的 “Material Design” 感到由衷的惊艳吧!至少我是的。 git
在惊艳之余感到由衷的遗憾,由于其必须在 ”Android L“ 上才能使用,MD,郁闷啊。预热一下: github
上面的两张动画相信你们都看过吧?是否是挺不错的?反正我是以为手机上有这样的动画是很爽的,比较手机是用来增长体验的。可是这些动画只能在Android L 才能体验到,对于如今国内的 Android 厂商的状况来看,估计谷歌出新的版本的时候咱们就能用上这个 L 版本了。 编程
下面给大伙看看我作的 “MaterialButton” 按钮: canvas
效果还不错吧?好了开始开工了。 ide
介绍一下个人工具:“Android Studio” 固然你们用其余也行。 工具
第一步:新建项目(这个任意,本身捣鼓吧) 布局
第二步:新建自定义控件:在java文件夹上右击选择自定义控件: 字体
取个名字:“MaterialButton” 动画
如今来看看多了一个类(MaterialButton),一个布局文件 “sample_material_button”,一个属性文件 “attrs_material_button”
到这里第二步完成了。多了3个文件。
第三步:修改 “MaterialButton” 类:
分为几步走:删除示例代码,从新继承自 “Button” 类,复写 “onTouchEvent()” 方法。完成后的代码:
public class MaterialButton extends Button { public MaterialButton(Context context) { super(context); init(null, 0); } public MaterialButton(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public MaterialButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, defStyle); } private void init(AttributeSet attrs, int defStyle) { // Load attributes final TypedArray a = getContext().obtainStyledAttributes( attrs, R.styleable.MaterialButton, defStyle, 0); a.recycle(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } }
是否是感受干净多了?到此第三步完成了。
第四步:就是作实际的动画了,在这里须要给你们说说三个须要注意的东西:
1.点击事件响应,这个很好理解,在 “onTouchEvent()” 方法中完成,在该方法中咱们须要完成的是点击后启动一个动画,同时须要获取到当时点击的位置。
2.动画,这里的动画不是放大动画而是属性动画,说实话 这个要说清楚还真不是一点点就能说清楚的事情。简单说就是在动画中能够控制一个属性的变化,而在这里来讲就是在 “MaterialButton” 类中创建一个宽度和一个颜色的属性,而后在动画中控制这两个属性的变化。
3.属性的创建以及属性的变化区域肯定问题。
首先创建两个属性:
private Paint backgroundPaint; private float radius; private Property<MaterialButton, Float> mRadiusProperty = new Property<MaterialButton, Float>(Float.class, "radius") { @Override public Float get(MaterialButton object) { return object.radius; } @Override public void set(MaterialButton object, Float value) { object.radius = value; //刷新Canvas invalidate(); } }; private Property<MaterialButton, Integer> mBackgroundColorProperty = new Property<MaterialButton, Integer>(Integer.class, "bg_color") { @Override public Integer get(MaterialButton object) { return object.backgroundPaint.getColor(); } @Override public void set(MaterialButton object, Integer value) { object.backgroundPaint.setColor(value); } };
两个属性对比一下能够发如今半径的属性 “set” 操做中调用了 “invalidate()” 方法,该方法的做用是告诉系统刷新当前控件的 “Canvas”,也就是触发一次:“onDraw(Canvas canvas)” 方法。
而后复写 “onTouchEvent()” 方法以下:
@Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { //记录坐标 paintX = event.getX(); paintY = event.getY(); //启动动画 startAnimator(); } return super.onTouchEvent(event); }
在该方法中,首先肯定是不是点击下去的事件,而后记录坐标,并启动动画。
在启动动画方法 “startAnimator()” 方法中,咱们这样写:
private void startAnimator() { //计算半径变化区域 int start, end; if (getHeight() < getWidth()) { start = getHeight(); end = getWidth(); } else { start = getWidth(); end = getHeight(); } float startRadius = (start / 2 > paintY ? start - paintY : paintY) * 1.15f; float endRadius = (end / 2 > paintX ? end - paintX : paintX) * 0.85f; //新建动画 AnimatorSet set = new AnimatorSet(); //添加变化属性 set.playTogether( //半径变化 ObjectAnimator.ofFloat(this, mRadiusProperty, startRadius, endRadius), //颜色变化 黑色到透明 ObjectAnimator.ofObject(this, mBackgroundColorProperty, new ArgbEvaluator(), Color.BLACK, Color.TRANSPARENT) ); // 设置时间 set.setDuration((long) (1200 / end * endRadius)); //先快后慢 set.setInterpolator(new DecelerateInterpolator()); set.start(); }
在这一步咱们须要知道有些按钮并非横向的,因此长不必定大于宽度,因此须要先判断获取到最长与最短,而后进行计算获取到开始的半径与结束的半径,这里有一个个人思路图:
咱们知道在 Android 中都是以左上脚为圆心,而后右边为X正数,下边为Y正数。因此创建了如上坐标系。
蓝色矩形区域表明按钮,蓝色点表明点击的点。灰色矩形表明点击后的开始区域,而后4边开始扩散开;以上就是一个简单的原理。固然思路有些跳跃,若是不懂能够在下边评论我都会进行回复的。
第五步:画画,对就是画画;这一步就是利用上面的半径和画笔颜色进行实际的绘制。
这里须要了解的是:
1:画画是在:“onDraw(Canvas canvas)” 方法中完成
2:在画板(Canvas)上是分层级的,简单说就是先画背景而后画房子,而后画人,最后画人的一些小细节 自底向上的流程
3:画板每次画 都是新的画板,预示着你每次都须要从背景画起而后才到人;在编程中就是每次 “onDraw(Canvas canvas)” 方法中的画板(Canvas )都是新的(New)。
说了那么多其实很简单,由于复杂的都在上一步中完成了。 “onDraw(Canvas canvas)” 源码以下:
@Override protected void onDraw(Canvas canvas) { canvas.save(); canvas.drawCircle(paintX, paintY, radius, backgroundPaint); canvas.restore(); super.onDraw(canvas); }
这里解释一下:
1.为何 “super.onDraw(canvas)” 须要放在最后调用?
由于画板是分层级的,当调用 “super.onDraw(canvas)” 的时候进行的工做是绘制字体那些,若是放在前面调用那么形成的后果是咱们的圆会覆盖到字体上面。因此咱们须要先画圆背景。
2.为何只有一次画圆操做(canvas.drawCircle())?
由于在半径属性中调用了 “invalidate()” ,当每次变化半径值的时候将进行一次 “onDraw(canvas)” 操做,也就画一次圆,在必定时间内快速重复画半径逐渐增大的圆的时候就造成了动画效果。
最后给出此次控件的代码:
public class MaterialButton extends Button { private Paint backgroundPaint; private float paintX, paintY, radius; public MaterialButton(Context context) { super(context); init(null, 0); } public MaterialButton(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public MaterialButton(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, defStyle); } private void init(AttributeSet attrs, int defStyle) { // Load attributes final TypedArray a = getContext().obtainStyledAttributes( attrs, R.styleable.MaterialButton, defStyle, 0); a.recycle(); } @Override protected void onDraw(Canvas canvas) { canvas.save(); canvas.drawCircle(paintX, paintY, radius, backgroundPaint); canvas.restore(); super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { //记录坐标 paintX = event.getX(); paintY = event.getY(); //启动动画 startAnimator(); } return super.onTouchEvent(event); } private void startAnimator() { //计算半径变化区域 int start, end; if (getHeight() < getWidth()) { start = getHeight(); end = getWidth(); } else { start = getWidth(); end = getHeight(); } float startRadius = (start / 2 > paintY ? start - paintY : paintY) * 1.15f; float endRadius = (end / 2 > paintX ? end - paintX : paintX) * 0.85f; //新建动画 AnimatorSet set = new AnimatorSet(); //添加变化属性 set.playTogether( //半径变化 ObjectAnimator.ofFloat(this, mRadiusProperty, startRadius, endRadius), //颜色变化 黑色到透明 ObjectAnimator.ofObject(this, mBackgroundColorProperty, new ArgbEvaluator(), Color.BLACK, Color.TRANSPARENT) ); // 设置时间 set.setDuration((long) (1200 / end * endRadius)); //先快后慢 set.setInterpolator(new DecelerateInterpolator()); set.start(); } private Property<MaterialButton, Float> mRadiusProperty = new Property<MaterialButton, Float>(Float.class, "radius") { @Override public Float get(MaterialButton object) { return object.radius; } @Override public void set(MaterialButton object, Float value) { object.radius = value; //刷新Canvas invalidate(); } }; private Property<MaterialButton, Integer> mBackgroundColorProperty = new Property<MaterialButton, Integer>(Integer.class, "bg_color") { @Override public Integer get(MaterialButton object) { return object.backgroundPaint.getColor(); } @Override public void set(MaterialButton object, Integer value) { object.backgroundPaint.setColor(value); } }; }
固然后续的工做还有:不一样的颜色的按钮,按钮属性的问题;
介于你们可能没有 Android Studio 没法看到效果,特地把 Apk 上传了,若是Eclipse不知道怎么导入的话 就加我QQ,我给你说一下!
地址:APK
这些我都在我的的项目中完成了,你们拿去试试: