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. Linux命令——finger

    简介 查询并显示系统用户的相关信息. 最小化安装Linux可能没有改名了,需要单独安装. RHEL/CentOS yum install finger* -y Ubuntu  apt-get inst ...

  2. 虚拟机更改MAC

    有两种方式修改MAC地址 方法一 现实中网卡一出厂就有MAC地址,虚拟机的MAC地址见下图,这个就相当于出厂MAC.在这里修改MAC相当于直接修改硬件MAC 方法二 这里修改MAC,硬件MAC并没有变 ...

  3. 修改虚拟机磁盘uuid

    cd 到这个目录: C:\Program Files\Oracle\VirtualBox> ####  sethduuid 后为 路径+文件名. VBoxManage internalcomma ...

  4. charles 右键菜单

    本文参考:charles 右键菜单 在网址/域名上右键 可以获得下面菜单 区域 1 基本操作 :基本的URL复制,文件保存,以及选中文件内搜索 区域 2 重写操作 :重写发送请求(调用接口合适),或者 ...

  5. js依赖mui.css生成图片验证码

    js依赖mui.css生成图片验证码 相关css和js引入路径 https://cdnjs.cloudflare.com/ajax/libs/mui/3.7.1/css/mui.css https:/ ...

  6. Luogu P2330 繁忙的都市

    Luogu P2330 繁忙的都市 裸的最小生成树. 当然,一定要注意它让你输出什么. #include<bits/stdc++.h> #define N 100010 using nam ...

  7. 基于Java+Selenium的WebUI自动化测试框架(七)--IE浏览器的设置

    在上一篇我们讲了关于WebDriver的版本,浏览器初始化,以及下载的设定. 在设置IE浏览器进行WebDriver的测试时,通常会遇见以下几种错误: 1.没有关闭IE浏览器的保护模式. 当运行测试用 ...

  8. java容器一:Collection概述

    Collection概览 java容器有两类,第一类是Collection,存储的是对象的集合:第二类是Map,存储的是键值对(两个对象以及它们之间的对应关系)的集合 Collection接口下面有三 ...

  9. m_strcmp

    strcmp比较两个字符串的大小,strcmp(str1, str2); 从str1和str2的第一个元素比较直到出现不同,或者遇到'\0'结束.如果str1 > str2 返回正数,str1 ...

  10. Android-jacoco代码覆盖率:单元测试覆盖率+功能测试覆盖率

    参考:https://docs.gradle.org/current/dsl/org.gradle.testing.jacoco.tasks.JacocoCoverageVerification.ht ...