CardView簡析

CardView是android L的新特性。卡片式容器,其特色是圓角和陰影效果,給扁平化元素增長了立體感。其實就是一種Material Design設計理念的代碼實現。android

先描述下屬性:

cardBackgroundColor  整個卡片背景顏色
cardCornerRadius    卡片邊角半徑
cardElevation     卡片的陰影實際寬度,你們都知道 Material Design 就是引入了材質厚度的概念。實際就是經過投射陰影的原理實現這種視覺效果。
cardMaxElevation    陰影區域寬度,與實際上的padding有關。
cardUseCompatPadding     5.0以上有效,true,padding=陰影區域寬度+contentPadding
cardPreventCornerOverlap  設置是否圓角重疊。
contentPadding     cardview內容區邊緣和其子view邊緣的距離。padding=contentpadding+陰影寬度。setpadding在cardview裏失效。
contentPaddingLeft    與上面同理
contentPaddingTop    與上面同理
contentPaddingRight     與上面同理
contentPaddingBottom    與上面同理canvas

下面根據源碼來分析一下:
public class CardView extends FrameLayout implements CardViewDelegate {
    private static final CardViewImpl IMPL;
    ...
}

繼承至frameLayout,實現接口CardViewDelegate,實現cardview的 效果的處理邏輯就是在CardViewDelegate和CardViewImpl中實現得。
CardViewJellybeanMr1  21>api>=17實例化它
CardViewEclairMr1   api<17實例化它
CardViewApi21   api>=21實例化它
都是cardview的陰影和邊角繪製工具,都繼承CardViewImpl
CardViewJellybeanMr1  extends CardViewEclairMr1,惟一差異就是在初始化方法initStatic()中建立邊框(RoundRectDrawableWithShadow)時,對canvas的方法調用的兼容。通俗點說就是對這個邊框的畫法不一樣。
android L對API的更新後,view自己就具有了繪製陰影的能力。view在繪製時,添加了對view背景的繪製drawBackground(Canvas canvas),其中引入的RenderNode是對繪製的信息以及本地接口進行封裝,包括setElevation設置的elevation值最終也是經過它來調用本地接口進行繪製
cardview出現的目的,能夠說是一個爲了圓角和陰影效果向後兼容。api

上面說了,爲了兼容,cardview的各類屬性設置都是經過CardViewImpl來實現。但爲了解耦,CardViewDelegate就像它字面意思同樣,被CardView委託,目的是爲了向CardViewImpl提供必要的對象,好比背景Drawable。ide

它的實現並無重寫onLayout,那麼他的佈局方式跟framelayout同樣。但onMeasure方法被重寫:工具

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (IMPL instanceof CardViewApi21 == false) {
            final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            switch (widthMode) {
                case MeasureSpec.EXACTLY:
                case MeasureSpec.AT_MOST:
                    final int minWidth = (int) Math.ceil(IMPL.getMinWidth(this));
                    widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minWidth,
                            MeasureSpec.getSize(widthMeasureSpec)), widthMode);
                    break;
            }
 
            final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            switch (heightMode) {
                case MeasureSpec.EXACTLY:
                case MeasureSpec.AT_MOST:
                    final int minHeight = (int) Math.ceil(IMPL.getMinHeight(this));
                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minHeight,
                            MeasureSpec.getSize(heightMeasureSpec)), heightMode);
                    break;
            }
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

其實能夠看出,5.0及以上,尺寸的計算方式沒變,可是5.0如下從新計算了尺寸。形成的結果是,5.0如下CardView的父容器設置wrap_content的時候,CardView即使沒有子視圖且它也是wrap_content,它也佔據必定尺寸,由於有顯示陰影和圓角邊框;但在android L它的尺寸就會變成0,看不到cardview以及其陰影效果。佈局

5.0如下版本,CardView的最小尺寸取決於IMPL.getMinWIdth(this),實際上調用的RoundRectDrawableWithShadow的getMinWidth:this

float getMinWidth() {
        final float content = 2 *
                Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2);
        return content + (mRawMaxShadowSize + mInsetShadow) * 2;
    }

換言之,android L的CardView大小跟framelayout同理,但android L之前的版本,它的大小還跟設置的elevation和圓角半徑有關。須要注意!設計

接下來講說這些屬性須要注意的地方:

cardUseCompatPadding在5.0如下版本設置沒有效果,5.0以上若是設置爲false,陰影區域寬度不會加入到實際padding中。
cardElevation > cardMaxElevation時,以cardElevation爲準,且陰影區域寬度=陰影實際寬度。
cardPreventCornerOverlap 當邊角過大時,這個設置可能也會失效,以下圖,設置了邊角爲300dp,可能實際寬度也就400dp左右code

相關文章
相關標籤/搜索