CardView是android L的新特性。卡片式容器,其特点是圆角和阴影效果,给扁平化元素增加了立体感。其实就是一种Material Design设计理念的代码实现。
先描述下属性:
下面根据源码来分析一下:
<span style="font-size:12px;">public class CardView extends FrameLayout implements CardViewDelegate {
</span><pre name="code" class="java"><span style="font-size:12px;"> private static final CardViewImpl IMPL;</span>
...}
继承至frameLayout,实现接口CardViewDelegate,实现cardview的效果的处理逻辑就是在CardViewDelegate和CardViewImpl中实现得。
android L对API的更新后,view本身就具备了绘制阴影的能力。view在绘制时,添加了对view背景的绘制drawBackground(Canvas canvas),其中引入的RenderNode是对绘制的信息以及本地接口进行封装,包括setElevation设置的elevation值最终也是通过它来调用本地接口进行绘制
cardview出现的目的,可以说是一个为了圆角和阴影效果向后兼容。
上面说了,为了兼容,cardview的各种属性设置都是通过CardViewImpl来实现。但为了解耦,CardViewDelegate就像它字面意思一样,被CardView委托,目的是为了向CardViewImpl提供必要的对象,比如背景Drawable。
它的实现并没有重写onLayout,那么他的布局方式跟framelayout一样。但onMeasure方法被重写:
<span style="font-size:12px;">//in CardView.java
@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);
}
}</span>
其实可以看出,5.0及以上,尺寸的计算方式没变,但是5.0以下重新计算了尺寸。造成的结果是,5.0以下CardView的父容器设置wrap_content的时候,CardView即便没有子视图且它也是wrap_content,它也占据一定尺寸,因为有显示阴影和圆角边框;但在android L它的尺寸就会变成0,看不到cardview以及其阴影效果。
5.0以下版本,CardView的最小尺寸取决于IMPL.getMinWIdth(this),实际上调用的RoundRectDrawableWithShadow的getMinWidth:
<span style="font-size:12px;">float getMinWidth() {
final float content = 2 *
Math.max(mRawMaxShadowSize, mCornerRadius + mInsetShadow + mRawMaxShadowSize / 2);
return content + (mRawMaxShadowSize + mInsetShadow) * 2;
}</span>
换言之,android L的CardView大小跟framelayout同理,但android L以前的版本,它的大小还跟设置的elevation和圆角半径有关。需要注意!
接下来说说这些属性需要注意的地方:
cardUseCompatPadding在5.0以下版本设置没有效果,5.0以上如果设置为false,阴影区域宽度不会加入到实际padding中。
cardElevation>cardMaxElevation时,以cardElevation为准,且阴影区域宽度=阴影实际宽度。
cardPreventCornerOverlap 当边角过大时,这个设置可能也会失效,如下图,设置了边角为300dp,可能实际宽度也就400dp左右
如下图:
左图cardPreventCornerOverlap =true。