LZ-Says:每件事情,总是在经历后,才会收获更多。有时候我们要的其实很简单,只是在追逐过程中渐渐忘记了初衷。愿大家在前行的路上,勿忘初心,加油前行~!!!
Material Design,谷歌良心之作,深入其中,妙趣横生~
今天为大家带来CardView,一起来看~
嘿嘿嘿,起来准备开车了~
CardView,字如其名,卡片式View,类似小卡片一样,简单举个小例子~
是不是感觉炫彩了许多?
首先介绍下有关CardView基本属性:
先简单介绍到这里。
第一步:引入依赖
implementation 'com.android.support:cardview-v7:26.0.2'
第二步:添加CardView 并为其设置Content
首先,我们先实现第一个效果,也就是CardView内填充一个TextView,下面附上源码:
.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="180dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
app:cardBackgroundColor="#80f"
app:cardCornerRadius="15dp"
app:cardElevation="15dp"
app:contentPadding="15dp">
"wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="CardView测试"
android:textColor="#fff"
android:textSize="16sp" />
.support.v7.widget.CardView>
效果如下:
接着,着手实现第二个效果,CardView设置ImageView,如下:
.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="180dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
app:cardBackgroundColor="#fff"
app:cardCornerRadius="15dp"
app:cardElevation="15dp">
"match_parent"
android:layout_height="match_parent"
android:background="@drawable/img"
android:scaleType="centerCrop" />
.support.v7.widget.CardView>
而效果如下:
最后,我们实现第三张图的效果:
.support.v7.widget.CardView
android:layout_width="300dp"
android:layout_height="180dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
app:cardBackgroundColor="#fff"
app:cardCornerRadius="15dp"
app:cardElevation="15dp"
app:contentPadding="15dp">
"match_parent"
android:layout_height="match_parent"
android:background="@drawable/img"
android:scaleType="centerCrop" />
.support.v7.widget.CardView>
效果嘛,如下喽~
使用easy,看看源码是否也很nice~
首先,我们先看下CardView继承自何方神圣~
public class CardView extends FrameLayout
所以说,为什么我们能在CardView中放置控件,一个合理的解释完美摆在面前。
接着往下看:
static {
if (Build.VERSION.SDK_INT >= 21) {
IMPL = new CardViewApi21Impl();
} else if (Build.VERSION.SDK_INT >= 17) {
IMPL = new CardViewApi17Impl();
} else {
IMPL = new CardViewBaseImpl();
}
IMPL.initStatic();
}
首先,静态块中进行当前系统版本判断,根据不同的状态去实例化不同的实例,如下:
1. 如果Android API 基于21以上,实例化CardViewApi21Impl;
2. 如果Android API 基于17以下,实例化CardViewApi17Impl;
3. 如果Android API 基于17以下,实例化CardViewBaseImpl。
最后,进行一个IMPL初始化。
那么这三个都分别是什么呢?
我们一起来看看~
@Override
public void initialize(CardViewDelegate cardView, Context context,
ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
final RoundRectDrawable background = new RoundRectDrawable(backgroundColor, radius);
cardView.setCardBackground(background);
View view = cardView.getCardView();
view.setClipToOutline(true);
view.setElevation(elevation);
setMaxElevation(cardView, maxElevation);
}
实例化内容如上,首先是实例化RoundRectDrawable一个对象,那么这个东西又是什么呢,它的作用何为?一起来看。
RoundRectDrawable(ColorStateList backgroundColor, float radius) {
mRadius = radius;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
setBackground(backgroundColor);
mBoundsF = new RectF();
mBoundsI = new Rect();
}
看到这块内容,大家是不是很熟悉,丫的,这不就是自定义View之绘制部分。
接收设置圆角弧度值,初始化画笔、设置背景颜色以及矩形区域,一气呵成。
而关于CardViewApi17Impl,又是如何的呢?
@Override
public void initStatic() {
RoundRectDrawableWithShadow.sRoundRectHelper =
new RoundRectDrawableWithShadow.RoundRectHelper() {
@Override
public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
Paint paint) {
canvas.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
}
};
}
看到这里,大家会有个疑问,为什么不同系统版本下,初始化不一样呢?
如果大家看过LZ上一篇关于沉浸式兼容性开发的博文后就会明白,我们兼容原因何在。而下面,我们要一起来看下api17上又是如何实现的呢?
RoundRectDrawableWithShadow(Resources resources, ColorStateList backgroundColor, float radius,
float shadowSize, float maxShadowSize) {
mShadowStartColor = resources.getColor(R.color.cardview_shadow_start_color);
mShadowEndColor = resources.getColor(R.color.cardview_shadow_end_color);
mInsetShadow = resources.getDimensionPixelSize(R.dimen.cardview_compat_inset_shadow);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
setBackground(backgroundColor);
mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mCornerShadowPaint.setStyle(Paint.Style.FILL);
mCornerRadius = (int) (radius + .5f);
mCardBounds = new RectF();
mEdgeShadowPaint = new Paint(mCornerShadowPaint);
mEdgeShadowPaint.setAntiAlias(false);
setShadowSize(shadowSize, maxShadowSize);
}
而这些内容则是设置一些相关属性,例如颜色、弧度以及颜色等。
而最终,都会在回调中调用Canvas类中的drawRoundRect()方法进行绘制,如下:
/**
* Draw the specified round-rect using the specified paint. The roundrect will be filled or
* framed based on the Style in the paint.
*
* @param rect The rectangular bounds of the roundRect to be drawn
* @param rx The x-radius of the oval used to round the corners
* @param ry The y-radius of the oval used to round the corners
* @param paint The paint used to draw the roundRect
*/
public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
super.drawRoundRect(rect, rx, ry, paint);
}
而关于CardViewBaseImpl,则如下。
@Override
public void initStatic() {
// Draws a round rect using 7 draw operations. This is faster than using
// canvas.drawRoundRect before JBMR1 because API 11-16 used alpha mask textures to draw
// shapes.
RoundRectDrawableWithShadow.sRoundRectHelper =
new RoundRectDrawableWithShadow.RoundRectHelper() {
@Override
public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
Paint paint) {
final float twoRadius = cornerRadius * 2;
final float innerWidth = bounds.width() - twoRadius - 1;
final float innerHeight = bounds.height() - twoRadius - 1;
if (cornerRadius >= 1f) {
// increment corner radius to account for half pixels.
float roundedCornerRadius = cornerRadius + .5f;
mCornerRect.set(-roundedCornerRadius, -roundedCornerRadius, roundedCornerRadius,
roundedCornerRadius);
int saved = canvas.save();
canvas.translate(bounds.left + roundedCornerRadius,
bounds.top + roundedCornerRadius);
canvas.drawArc(mCornerRect, 180, 90, true, paint);
canvas.translate(innerWidth, 0);
canvas.rotate(90);
canvas.drawArc(mCornerRect, 180, 90, true, paint);
canvas.translate(innerHeight, 0);
canvas.rotate(90);
canvas.drawArc(mCornerRect, 180, 90, true, paint);
canvas.translate(innerWidth, 0);
canvas.rotate(90);
canvas.drawArc(mCornerRect, 180, 90, true, paint);
canvas.restoreToCount(saved);
//draw top and bottom pieces
canvas.drawRect(bounds.left + roundedCornerRadius - 1f, bounds.top,
bounds.right - roundedCornerRadius + 1f,
bounds.top + roundedCornerRadius, paint);
canvas.drawRect(bounds.left + roundedCornerRadius - 1f,
bounds.bottom - roundedCornerRadius,
bounds.right - roundedCornerRadius + 1f, bounds.bottom, paint);
}
// center
canvas.drawRect(bounds.left, bounds.top + cornerRadius,
bounds.right, bounds.bottom - cornerRadius , paint);
}
};
}
@Override
public void initialize(CardViewDelegate cardView, Context context,
ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
RoundRectDrawableWithShadow background = createBackground(context, backgroundColor, radius,
elevation, maxElevation);
background.setAddPaddingForCorners(cardView.getPreventCornerOverlap());
cardView.setCardBackground(background);
updatePadding(cardView);
}
private RoundRectDrawableWithShadow createBackground(Context context,
ColorStateList backgroundColor, float radius, float elevation,
float maxElevation) {
return new RoundRectDrawableWithShadow(context.getResources(), backgroundColor, radius,
elevation, maxElevation);
}
@Override
public void updatePadding(CardViewDelegate cardView) {
Rect shadowPadding = new Rect();
getShadowBackground(cardView).getMaxShadowAndCornerPadding(shadowPadding);
cardView.setMinWidthHeightInternal((int) Math.ceil(getMinWidth(cardView)),
(int) Math.ceil(getMinHeight(cardView)));
cardView.setShadowPadding(shadowPadding.left, shadowPadding.top,
shadowPadding.right, shadowPadding.bottom);
}
首先,在initStatic()方法中,依旧实例化RoundRectDrawableWithShadow对象并在回调中设置针对此系统版本做单独设置,最终都是调用canvas.drawRect实现最后效果。
里面便是进行弧度的判断以及当开发者未添加弧度默认值设定,这里就不一一细说了。
而initialize()方法中则是重点针对于background以及padding值设置而且更新即可。
而最后,我们要查看下在首次最后实例化内容将会是什么?
/**
* Interface for platform specific CardView implementations.
*/
interface CardViewImpl {
void initialize(CardViewDelegate cardView, Context context, ColorStateList backgroundColor,
float radius, float elevation, float maxElevation);
void setRadius(CardViewDelegate cardView, float radius);
float getRadius(CardViewDelegate cardView);
void setElevation(CardViewDelegate cardView, float elevation);
float getElevation(CardViewDelegate cardView);
void initStatic();
void setMaxElevation(CardViewDelegate cardView, float maxElevation);
float getMaxElevation(CardViewDelegate cardView);
float getMinWidth(CardViewDelegate cardView);
float getMinHeight(CardViewDelegate cardView);
void updatePadding(CardViewDelegate cardView);
void onCompatPaddingChanged(CardViewDelegate cardView);
void onPreventCornerOverlapChanged(CardViewDelegate cardView);
void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color);
ColorStateList getBackgroundColor(CardViewDelegate cardView);
}
这里面便是一些关键的对外接口等等内容,同时做法也比较贴合我们开发实际。
最最最后,我们回归我们的CardView。
public CardView(Context context) {
super(context);
initialize(context, null, 0);
}
public CardView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs, 0);
}
public CardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs, defStyleAttr);
}
自定义View,实例化。没啥可说的。
到此结束~!!!
https://github.com/HLQ-Struggle/MaterialDesignStudy
东西Easy,还是要多去看看,只要看了,慢慢就会有收获~
伙计们,一起加油~