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. 数据库管理工具-Navicat Premium 12

    首先感谢下github上大佬,我才能使用这个软件.也可以直接浏览https://github.com/DoubleLabyrinth/navicat-keygen进行安装,非常详细. 1.https: ...

  2. Linux中条件语句

    Linux中备份一个jar包,需要既判断进程是否停止,又判断文件是否存在 1. 进程停了,文件存在则备份 2.进程停了,文件不存在,无需备份 3. 进程在跑,停止进程:文件存在,将文件进行备份 4. ...

  3. Spring Boot配置多数据源并实现Druid自动切换

    原文:https://blog.csdn.net/acquaintanceship/article/details/75350653 Spring Boot配置多数据源配置yml文件主数据源配置从数据 ...

  4. java线程基础巩固---多Product多Consumer之间的通讯导致出现程序假死的原因分析

    在上一次中已经实现一个生产者与消费者的初步模型(http://www.cnblogs.com/webor2006/p/8413286.html),但是当时只是一个生产者对应一个消费者,先贴下代码: p ...

  5. Light OJ - 1026 - Critical Links(图论-Tarjan算法求无向图的桥数) - 带详细注释

     原题链接   无向连通图中,如果删除某边后,图变成不连通,则称该边为桥. 也可以先用Tajan()进行dfs算出所有点 的low和dfn值,并记录dfs过程中每个 点的父节点:然后再把所有点遍历一遍 ...

  6. 05-Docker私有仓库

    一.介绍私有仓库顾名思义,如果我们不想把docker镜像公开放到公有仓库中,只想在部门或团队内部共享docker镜像,这时私有仓库就来了. 二.私有仓库搭建与配置1.拉取私有仓库镜像,这里说明一下,私 ...

  7. 二、操作XML DOM:XML Document

    需要添加的命名空间:using System.Xml; 一.创建xml文件: 1.XmlDocument方式创建 XmlDocument xmldoc = new XmlDocument(); //加 ...

  8. GITHUB下载源码方式

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

  9. 2019-2020-1 20199312《Linux内核原理与分析》第四周作业

    计算机和操作系统的法宝 计算机三个法宝 存储程序计算机.函数调用堆栈机制.中断 操作系统:中断中断上下文的切换--保护和恢复现场 进程上下文的切换. Linux源代码目录分析 arch目录:代码量庞大 ...

  10. docker:设置国内镜像仓储

    修改docker仓储镜像 vi /etc/docker/daemon.json 增加下面数据 { "registry-mirrors": ["https://xwx6wx ...