CardView 简介
本文链接:https://blog.csdn.net/ShawnXiaFei/article/details/81568537
CardView 是 Google 官方发布 MD 风格卡片布局控件,开发者可以很方便的使用它将布局做成卡片效果。在使用 CardView 之前,多少应该对它有一定的了解,下面将对其实现做简单的介绍。
自定义属性
CardView 继承自 FrameLayout,并在其基础上添加了圆角和阴影等效果。为了更方便的使用这些效果,Google 提供了一系列的自定义属性,这些属性在类注释中都有列出来,如下:
 /**
 * @attr ref android.support.v7.cardview.R.styleable#CardView_cardBackgroundColor
 * @attr ref android.support.v7.cardview.R.styleable#CardView_cardCornerRadius
 * @attr ref android.support.v7.cardview.R.styleable#CardView_cardElevation
 * @attr ref android.support.v7.cardview.R.styleable#CardView_cardMaxElevation
 * @attr ref android.support.v7.cardview.R.styleable#CardView_cardUseCompatPadding
 * @attr ref android.support.v7.cardview.R.styleable#CardView_cardPreventCornerOverlap
 * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPadding
 * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingLeft
 * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingTop
 * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingRight
 * @attr ref android.support.v7.cardview.R.styleable#CardView_contentPaddingBottom
 */
public class CardView extends FrameLayout {

这些属性的作用和用法如下:

CardView_cardBackgroundColor        设置背景色
CardView_cardCornerRadius           设置圆角大小
CardView_cardElevation              设置z轴阴影
CardView_cardMaxElevation           设置z轴最大高度值
CardView_cardUseCompatPadding       是否使用CompadPadding
设置内边距,V21+的版本和之前的版本具有一样的计算方式。
部分机器不开这个属性会导致卡片效果“消失”,如荣耀6(6.0系统)。
CardView_cardPreventCornerOverlap   是否使用PreventCornerOverlap
在V20和之前的版本中添加内边距,这个属性为了防止内容和边角的重叠
CardView_contentPadding             内部边距,子View与CardView的距离
CardView_contentPaddingLeft         内部左侧边距
CardView_contentPaddingTop          内部顶部边距
CardView_contentPaddingRight        内部右侧边距
CardView_contentPaddingBottom       内部底部边距

CardViewImpl 接口
跟着源码往下看,接下来就是做多 API 版本适配的代码,这段代码使得不同版本的 Android 能达到相同或者相似的效果,尽可能的做到了兼容。这里 CardViewImpl 的几个子类实现请自行查阅,这里不多说了。

private static final CardViewImpl IMPL;
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();
}
上面这段代码很有意思,首先它是static{}包裹的静态代码块,而静态代码块是属于类的,只会在类被加载到内存时执行一次,以后不管如何实例化,new 出多少实例对象,静态代码块都不会再执行了。其次,IMPL 对象是是static final修饰的,这就意味着 IMPL 对象也是属于类,并且只能被初始化一次。
final 修饰的对象,若是基本类型+String,则其值不能修改;若是复杂类型,则其引用不能修改。
基本类型+String的值、复杂类型的引用,存储在栈中;复杂类型的实体类容存储在堆中。final 是指明栈中的类容不能修改。
那么,一旦 CardView 被加载到内存,IMPL 对象(地址)就不会再变化了,也就会被后续系统中所有实例化的 CardView 对象共享。而纵观整个 CardView 的源码,我们会发现 IMPL 对象几乎出现在 CardView 的所有方法中,那么是不是系统中所有的 CardView 实例化对象都会有相同的表现呢?
实际使用中我们发现,即便一个APP内部的多个CardView也能有不同的表现,更不用说整个系统上的所有APP了,那这又是怎么做到的呢?
我们接着看下 CardViewImpl 接口的定义:
/**
 * 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);
}
不难发现,这里面几乎所有方法都有一个参数——CardViewDelegate,在CardView的方法调用时,会通过早已初始化的 IMPL 调用对应的方法,并传入一个mCardViewDelegate对象,并通过它进行下一步操作。如:
/**
 * Updates the background color of the CardView
 *
 * @param color The new color to set for the card background
 * @attr ref android.support.v7.cardview.R.styleable#CardView_cardBackgroundColor
 */
public void setCardBackgroundColor(@ColorInt int color) {
    IMPL.setBackgroundColor(mCardViewDelegate, ColorStateList.valueOf(color));
}
CardViewDelegate 代理
接下来就简单说下 CardViewDelegate 对象是如何工作的。
首先是定义,这一系列方法定义与 CardView 提供的方法迷之相似。
/**
 * Interface provided by CardView to implementations.
 * <p>
 * Necessary to resolve circular dependency between base CardView and platform implementations.
 */
interface CardViewDelegate {
    void setCardBackground(Drawable drawable);
    Drawable getCardBackground();
    boolean getUseCompatPadding();
    boolean getPreventCornerOverlap();
    void setShadowPadding(int left, int top, int right, int bottom);
    void setMinWidthHeightInternal(int width, int height);
    View getCardView();
}
然后 CardViewDelegate 的实例化是在 CardView 中进行的,在 CardView 代码末尾可看到其实现:
private final CardViewDelegate mCardViewDelegate = new CardViewDelegate() {
    ······
}
这里没有使用 static,那么这个 mCardViewDelegate 对象在 CardView 实例化时也会 new 一个新的,然后通过不同 mCardViewDelegate 对象,就做到了一个系统上不同CardView有不同表现。
最后这一系列操作的示意图大致是这样的:

这一系列的操作,将 CardView 的实现分成多个类,各个类只处理和自己相关的逻辑,简化了 CardView 自身逻辑。同时,能很方便的做到多平台适配,不需要将各个平台特定的实现代码全部挤在 CardView 内部。而且能很方便进行扩展,如添加新平台、新特性,而且不会对 CardView 的代码造成很大改动,只需要添加新的 IMPL,并在static{}中添加新分支即可。
CardView 使用
添加依赖库
CardView 是随 MD 推出的补充库,并非 SDK 的内容,因此在使用 CardView 时,必须先引入依赖库:
implementation 'com.android.support:cardview-v7:xx.x.x'
1
使用 CardView 布局
前面已经介绍了,CardView 继承自 FrameLayout,那么我们就可以直接在布局中,将CardView作为容器,放入其它控件即可。
如果已有现成的布局,想再引入卡片效果,也只需要在已有布局最外层添加 CardView 即可。
举个栗子:
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="@dimen/dp8"
    android:orientation="vertical"
    app:cardBackgroundColor="@color/white"
    app:cardCornerRadius="@dimen/dp8"
    app:cardElevation="@dimen/dp8"
    app:cardUseCompatPadding="true"
    app:contentPadding="@dimen/dp8">
    <TextView
        android:id="@+id/tvTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试\n卡片\n效果"
        android:textSize="@dimen/sp32" />
</android.support.v7.widget.CardView>

前面介绍属性已经说了,部分机器(如荣耀6,6.0系统)如果不打开 cardUseCompatPadding,将不会呈现出卡片效果。因此建议打开。

效果如下:
类似效果

要实现卡片效果,除了用 CardView 以外,还有其它方法,比如使用shape+elevation。
举个栗子:
先定义一个shape,用作背景。

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">//shape样式
    //圆角
    <corners android:radius="@dimen/dp8" />
    //边框
    <stroke
        android:width="1dp"
        android:color="@color/divider" />
    //内边距
    <padding
        android:bottom="@dimen/dp8"
        android:left="@dimen/dp8"
        android:right="@dimen/dp8"
        android:top="@dimen/dp8" />
    //内部填充
    <solid android:color="@color/white" />
</shape>

然后在布局中引用:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="top|center_horizontal"
    android:layout_margin="@dimen/dp8"
    android:background="@drawable/shape"
    android:elevation="@dimen/dp8"    //z轴高度,控制阴影效果
    android:text="测试\n卡片\n效果"
    android:textSize="@dimen/sp32" />
运行效果:

可以看到,与前面使用 CardView 的效果几乎一样。
但是,elevation属性也是随MD出来的,它只支持 5.0+(也就是API21+)的系统。因此,如果要卡片效果能想兼容低版本系统,那还是应该优先考虑用 CardView。
————————————————

CardView 简介和使用的更多相关文章

  1. Android零基础入门第71节:CardView简单实现卡片式布局

    还记得我们一共学过了多少UI控件了吗?都掌握的怎么样啊. 安卓中一些常用控件学习得差不多了,今天再来学习一个新的控件CardView,在实际开发中也有非常高的地位. 一.CardView简介 Card ...

  2. 【转】GitHub 排名前 100 的安卓、iOS项目简介

    GitHub Android Libraries Top 100 简介 排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不 ...

  3. GitHub Android Libraries Top 100 简介

    本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过 ...

  4. RecyclerView,CardView导入和使用(Demo)

    简介: 这篇文章是ANDROID L——Material Design详解(UI控件)的一个补充或者说是应用实例,如果有时间建议大家稍微浏览一下上篇文章. 本文主要介绍Android L新增加的两个U ...

  5. 2016年GitHub 排名前 100 的安卓、iOS项目简介(收藏)

    排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不相关的项目, 所以排名并不具备任何官方效力, 仅供参考学习, 方便初学者 ...

  6. 64.GitHub 排名前100的android项目简介

    GitHub Android Libraries Top 100 简介 排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不 ...

  7. Android5.0新特性——Material Design简介

    Material Design Material Design简介 Material Design是谷歌新的设计语言,谷歌希望寄由此来统一各种平台上的用户体验,Material Design的特点是干 ...

  8. GitHub Android Librarys Top 100 简介

    GitHub Android Librarys Top 100 简介 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据GitHub搜索J ...

  9. <Android开源库 ~ 1> GitHub Android Libraries Top 100 简介

    转载自GitHub Android Libraries Top 100 简介 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据 GitH ...

随机推荐

  1. CentOS7.X+LAMP环境下安装zabbix4.X

    1.安装zabbix仓库 rpm -ivh https://repo.zabbix.com/zabbix/4.2/rhel/7/x86_64/zabbix-release-4.2-1.el7.noar ...

  2. Django+bootstrap+注册登录系统

    转自:https://www.cnblogs.com/robindong/p/9610057.html Robin_D 博客园 首页 新随笔 联系 订阅 管理 随笔 - 10  文章 - 0  评论 ...

  3. GPU---NVIDIA GPU 计算能力

    查询网址:https://developer.nvidia.com/cuda-gpus 使用,makefile文件实例: GPU= CUDNN= OPENCV= OPENMP= DEBUG= ARCH ...

  4. Robot Framework--变量

    Robot Framework的变量分为标量, 列表和字典, 分别使用语法格式 ${SCALAR}, @{LIST} 和 &{DICT} 来定义. 此外, 环境变量可以直接使用语法 %{ENV ...

  5. GITHUB下载源码方式

    从昨天开始就想着从GitHub上下载一个开源的Vue的实战项目,希望能从中学习更多的Vue的实用内容,结果搞了半天好不容易下载了,不知道怎么弄.然而,今天终于成功了,激动地我赶紧来记录一下.如何从Gi ...

  6. javaWeb下载

    ---恢复内容开始--- 下载 1. 下载就是向客户端响应字节数据!  原来我们响应的都是html的字符数据!  把一个文件变成字节数组,使用response.getOutputStream()来各应 ...

  7. Base 编解码(转)

    private static final char[] legalChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0 ...

  8. 关于windows中 redis 闪退问题

    就在刚刚,因为 Redis 闪退原因,搞了快半小时,电脑关机前还能用,关机后一打开就秒退,所以我先发个解决方案再继续码.. 按照步骤一步一步来 ( 设置 redis 密码看文章最后 ) 给你省个事,我 ...

  9. 03_Tutorial 3: Class-based Views 基于类的视图(CBV)

    1.CBV 0.文档 https://q1mi.github.io/Django-REST-framework-documentation/tutorial/3-class-based-views_z ...

  10. [CSS] Change the Alignment of a Single Flexed Item with 'align-self'

    Inside of a flexed container, a single item can control its own flex with align-self. The possible v ...