android的materialDesignLibrary源代码分析
最近使用了一个框架materialDesignLibrary,发现它能做出很炫的ui,比如按钮,一点击就会有水波荡开的效果,非常有意思!所以就想读一读它的源码,看一些开源框架的源码能学到很多东西。首先先贴一下使用该框架做的一个登陆注册界面吧。
一,CustomView分析
上面的是两个ButtonRectangle,点击一下可以看到很明显的水波!那么首先看看ButtonRectangle源码。一开始我想,它是一个button,想必是继承自android.widget.Button的吧?点开一看,发现是确实是继承自Button,不过这个Button是作者自己定义的,它继承自作者重写的一个view,叫做CustomView,而CustomerView继承自我们非常熟悉的RelativeLayout,显然CustomView就是所有有特效的控件的父类了。它直接继承自RelativeLayout。现在看看它对动画的操作,代码如下
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if(enabled) {
this.setBackgroundColor(this.beforeBackground);
} else {
this.setBackgroundColor(this.disabledBackgroundColor);
}
this.invalidate();
}
protected void onAnimationStart() {
super.onAnimationStart();
this.animation = true;
}
protected void onAnimationEnd() {
super.onAnimationEnd();
this.animation = false;
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(this.animation) {
this.invalidate();
}
}
按钮不可用的时候设置成#E2E2E2,动画一开始,animation就设置成true,ondraw就可以调用重绘方法了,动画结束,animation变成false,而ondraw方法也就不重绘。这个父类主要就是控制动画的。
二,ButtonRectangle类
我比较喜欢的就是这个按钮,水波效果,现在开始分析一下这个类。首先看它的构造方法。代码如下
public ButtonRectangle(Context context, AttributeSet attrs) {
super(context, attrs);
this.setDefaultProperties();
}
里面有一个初始化属性的方法setDefaultProperties(),在点开看看setDefaultProperties(),代码如下
protected void setDefaultProperties() {
super.minWidth = 80;
super.minHeight = 36;
super.background = drawable.background_button_rectangle;
super.setDefaultProperties();
}
初始化的时候把按钮最小宽高背景都设置了,比较简单,但是这个类的另外两个方法值得分析分析的,第一个当然就是ondraw类了,代码如下
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(this.x != -1.0F) {
Rect src = new Rect(0, 0, this.getWidth() - Utils.dpToPx(6.0F, this.getResources()), this.getHeight() - Utils.dpToPx(7.0F, this.getResources()));
Rect dst = new Rect(Utils.dpToPx(6.0F, this.getResources()), Utils.dpToPx(6.0F, this.getResources()), this.getWidth() - Utils.dpToPx(6.0F, this.getResources()), this.getHeight() - Utils.dpToPx(7.0F, this.getResources()));
canvas.drawBitmap(this.makeCircle(), src, dst, (Paint)null);
this.invalidate();
}
}
这个方法就是为什么我们看到的效果就是这个按钮是四四方方的,要是x!=-1.0f就执行重绘,那么什么时候会改变x的值呢?这就要看上面提过的Button类了。主要是有个ontouch事件,在Button里面,看看,代码
public boolean onTouchEvent(MotionEvent event) {
this.invalidate();
if(this.isEnabled()) {
this.isLastTouch = true;
if(event.getAction() == 0) {
this.radius = (float)(this.getHeight() / this.rippleSize);
this.x = event.getX();
this.y = event.getY();
} else if(event.getAction() == 2) {
this.radius = (float)(this.getHeight() / this.rippleSize);
this.x = event.getX();
this.y = event.getY();
if(event.getX() > (float)this.getWidth() || event.getX() < 0.0F || event.getY() > (float)this.getHeight() || event.getY() < 0.0F) {
this.isLastTouch = false;
this.x = -1.0F;
this.y = -1.0F;
}
} else if(event.getAction() == 1) {
if(event.getX() <= (float)this.getWidth() && event.getX() >= 0.0F && event.getY() <= (float)this.getHeight() && event.getY() >= 0.0F) {
++this.radius;
if(!this.clickAfterRipple && this.onClickListener != null) {
this.onClickListener.onClick(this);
}
} else {
this.isLastTouch = false;
this.x = -1.0F;
this.y = -1.0F;
}<pre name="code" class="java"> <span style="font-family: Arial, Helvetica, sans-serif;"> } else if(event.getAction() == 3) {</span>
this.isLastTouch = false; this.x = -1.0F; this.y = -1.0F; } } return true; }
这其实是监听了按钮的点击事件,按下和不按.。有意思的是,还可以设置水波的速度。
public float getRippleSpeed() {
return this.rippleSpeed;
}
三,ProgressBarIndeterminateDeterMinate
这个类使用了属性动画objectanimation,构造函数创建一个线程以启动动画。代码如下
public ProgressBarIndeterminateDeterminate(Context context, AttributeSet attrs) {
super(context, attrs);
this.post(new Runnable() {
public void run() {
ProgressBarIndeterminateDeterminate.this.setProgress(60);
ViewHelper.setX(ProgressBarIndeterminateDeterminate.this.progressView, (float)(ProgressBarIndeterminateDeterminate.this.getWidth() + ProgressBarIndeterminateDeterminate.this.progressView.getWidth() / 2));
ProgressBarIndeterminateDeterminate.this.animation = ObjectAnimator.ofFloat(ProgressBarIndeterminateDeterminate.this.progressView, "x", new float[]{(float)(-ProgressBarIndeterminateDeterminate.this.progressView.getWidth() / 2)});
ProgressBarIndeterminateDeterminate.this.animation.setDuration(1200L);
ProgressBarIndeterminateDeterminate.this.animation.addListener(new AnimatorListener() {
int cont = 1;
int suma = 1;
int duration = 1200;
public void onAnimationEnd(Animator arg0) {
if(ProgressBarIndeterminateDeterminate.this.runAnimation) {
ViewHelper.setX(ProgressBarIndeterminateDeterminate.this.progressView, (float)(ProgressBarIndeterminateDeterminate.this.getWidth() + ProgressBarIndeterminateDeterminate.this.progressView.getWidth() / 2));
this.cont += this.suma;
ProgressBarIndeterminateDeterminate.this.animation = ObjectAnimator.ofFloat(ProgressBarIndeterminateDeterminate.this.progressView, "x", new float[]{(float)(-ProgressBarIndeterminateDeterminate.this.progressView.getWidth() / 2)});
ProgressBarIndeterminateDeterminate.this.animation.setDuration((long)(this.duration / this.cont));
ProgressBarIndeterminateDeterminate.this.animation.addListener(this);
ProgressBarIndeterminateDeterminate.this.animation.start();
if(this.cont == 3 || this.cont == 1) {
this.suma *= -1;
}
}
}
public void onAnimationStart(Animator arg0) {
}
public void onAnimationRepeat(Animator arg0) {
}
public void onAnimationCancel(Animator arg0) {
}
});
ProgressBarIndeterminateDeterminate.this.animation.start();
}
});
}