Material Design库源码分析--FloatingActionButton

转自:http://www.androidcn.org/topic/556dd1944e6f131d31d543de 

Android support库最近终于放出了一系列material design support控件的官方实现,有了官方给出的实现,以后大家尽量还是使用官方的库吧。从这篇开始,我打算写一个系列,每一篇分析一个material design控件的实现方式,学习一下Google是如何在5.0之前的版本中实现material design的,以及Google是如何让一个控件同时兼容5.0和5.0之前的版本的。

第一篇分析的是FloatingActionButton

public class FloatingActionButton extends ImageView {

FloatingActionButton集成自ImageView,因此你可以对它使用ImageView的各种属性,比如src

FloatingActionButton内部有一个FloatingActionButtonImpl类型的属性 mImpl,FloatingActionButton其实是将几乎所有的方法都会调用这个mImpl对象的同名方法,说白了 FloatingActionButton其实就是一个代理,真正用来控制整个控件显示的是这个mImpl对象。

查看构造函数,可以看到下面的代码:

    if(VERSION.SDK_INT >= 21) {
        this.mImpl = new FloatingActionButtonLollipop(this, delegate);
    } else {
        this.mImpl = new FloatingActionButtonEclairMr1(this, delegate);
    }

可以看到针对api版本是否大于等于21,mImpl属性被new成了不同的对象。我们这里主要分析针对api 21之前的实现,也就是FloatingActionButtonEclairMr1。另外构造函数里传递的delegate对象主要是用来会掉 FloatingActionButton的三个方法,这里给出创建delegate对象的代码:

    ShadowViewDelegate delegate = new ShadowViewDelegate() {
        public float getRadius() {
            return (float)FloatingActionButton.this.getSizeDimension() / 2.0F;
        }
        public void setShadowPadding(int left, int top, int right, int bottom) {
            FloatingActionButton.this.mShadowPadding.set(left, top, right, bottom);
            FloatingActionButton.this.setPadding(left + FloatingActionButton.this.mContentPadding, top + FloatingActionButton.this.mContentPadding, right + FloatingActionButton.this.mContentPadding, bottom + FloatingActionButton.this.mContentPadding);
        }
        public void setBackgroundDrawable(Drawable background) {
            FloatingActionButton.super.setBackgroundDrawable(background);
        }
    };

在创建完mImpl对象后,会调用mImpl对象的几个方法:

    this.mImpl.setBackgroundDrawable(background, this.mBackgroundTint, this.mBackgroundTintMode, this.mRippleColor, this.mBorderWidth);
    this.mImpl.setElevation(elevation);
    this.mImpl.setPressedTranslationZ(pressedTranslationZ);

查看FloatingActionButtonEclairMr1类的setBackgroundDrawable方法,在最后可以看到这几行代码:

    this.mShadowDrawable = new ShadowDrawableWrapper(this.mView.getResources(), new LayerDrawable(layers), this.mShadowViewDelegate.getRadius(), this.mElevation, this.mElevation + this.mPressedTranslationZ);
    this.mShadowDrawable.setAddPaddingForCorners(false);
    this.mShadowViewDelegate.setBackgroundDrawable(this.mShadowDrawable);

其中mShadowViewDelegate就是我们前面讲的ShadowViewDelegate对 象,this.mShadowViewDelegate.setBackgroundDrawable(this.mShadowDrawable)会调 用FloatingActionButton.super.setBackgroundDrawable(background)方法,最终调用了 ImageView的setBackgroundDrawable方法来显示一个drawable。

this.mShadowViewDelegate.setBackgroundDrawable(this.mShadowDrawable)中 传递的mShadowDrawable其实是一个ShadowDrawableWrapper对象。而ShadowDrawableWrapper继承自 DrawableWrapper,DrawableWrapper类继承自Drawable,它其实就是对Drawble的一个包装,它所有的方法都会调 用内部的一个drawable对象的相同方法。ShadowDrawableWrapper多做了一点工作就是通过drawShadow方法给 drawable添加一层阴影,drawShadow方法的实现其实很有意思,我留到以后再写吧,感兴趣的同学可以自己看看。

再看ShadowDrawableWrapper对象的创建this.mShadowDrawable = new ShadowDrawableWrapper(this.mView.getResources(), new LayerDrawable(layers), this.mShadowViewDelegate.getRadius(), this.mElevation, this.mElevation + this.mPressedTranslationZ); 我们可以看到这里是传递了一个LayerDrawable对象给ShadowDrawableWrapper,也就是这个LayerDrawable对象 才是最终显示的。

    Drawable\[\] layers;
    if(borderWidth > 0) {
      this.mBorderDrawable = this.createBorderDrawable(borderWidth, backgroundTint);
      layers = new Drawable\[\]{this.mBorderDrawable, this.mShapeDrawable, this.mRippleDrawable};
    } else {
      this.mBorderDrawable = null;
      layers = new Drawable\[\]{this.mShapeDrawable, this.mRippleDrawable};
  }

LayerDrawable无非就是包含了一个用来显示图像的Drawable,一个添加了边框的Drawable和一个显示水波效果的mRippleDrawable。我比较感兴趣的是rippleDrawable是怎么做到的,看代码:

    GradientDrawable touchFeedbackShape = new GradientDrawable();
    touchFeedbackShape.setShape(1);
    touchFeedbackShape.setColor(-1);
    touchFeedbackShape.setCornerRadius(this.mShadowViewDelegate.getRadius());
    this.mRippleDrawable = DrawableCompat.wrap(touchFeedbackShape);
    DrawableCompat.setTintList(this.mRippleDrawable, createColorStateList(rippleColor));
    DrawableCompat.setTintMode(this.mRippleDrawable, Mode.MULTIPLY);

其实就是一个简单的GradientDrawable,然后使用DrawableCompat的setTintList,给这个 GradientDrawable的不同选中状态设置不同的颜色。另外当FloatingActionButton被选中的时候,会通过一个 Animation对象来改变阴影的大小,从而实现视觉上的z轴移动的效果。

感觉说的有点乱,大家将就看啦。 其实主要的就是一个ShadowDrawableWrapper类,另外就是DrawableCompat提供的一些辅助方法。