- 示例工程
【免费下载链接】views-widgets-samples
Multiple samples showing the best practices in views-widgets on Android.
CardView 是 Android 5.0(API 21)时期随 support library 引入的卡片式容器控件,为内容提供统一风格的圆角矩形背景与投影阴影。本文以 views-widgets-samples 仓库中的 CardView 示例 为骨架,结合其 Java/Kotlin 双版本源码与仪器测试,系统讲解 CardView 的布局写法、cardCornerRadius与elevation两大核心属性的作用原理、交互式调节与测试验证方法,帮助你直接落地到自己的 Android 项目中。
示例概览:演示什么、怎么组织
CardView 示例的定位非常明确:演示 support library 中随 Android 5.0 引入的android.support.v7.widget.CardView控件的基本用法。该声明同时出现在 CardView/README.md 与 base-strings.xml 的应用描述中。
从工程结构上看,仓库为该示例提供了两个独立可导入的工程目录:
- Java 版(默认示例):位于 CardView/,主代码在 CardView/Application/src/main/java/com/example/android/cardview/ 下;
- Kotlin 版:位于 CardViewKotlin/,主代码在 CardViewKotlin/Application/src/main/java/com/example/android/cardview/ 下。
两个版本的界面与交互完全一致,差异仅在语言与测试框架选型上:
| 维度 | Java 版(CardView) | Kotlin 版(CardViewKotlin) |
|---|---|---|
| 主界面入口 | CardViewActivity.java | CardViewActivity.kt |
| 卡片逻辑承载 | CardViewFragment.java | CardViewFragment.kt |
| 布局文件 | fragment_card_view.xml | fragment_card_view.xml |
| 测试用例 | SampleTests.java(继承ActivityInstrumentationTestCase2) | SampleTests.kt(JUnit4 +ActivityTestRule+AndroidJUnit4) |
| 前置 SDK 要求 | Android SDK 28 + Build Tools v28.0.3 | Android SDK 27 |
示例运行后呈现一个可交互的演示页:页面中央是一张蓝色圆角卡片,卡片下方依次是Radius(圆角半径)与Elevation(高度/阴影)两个 SeekBar,拖动滑块即可实时观察卡片外观变化。这种"一个控件 + 两个属性调节条"的极简设计,恰好覆盖了 CardView 区别于普通容器的全部核心特性。
搭建环境:前置条件与构建方式
README 明确了构建本示例的三项前置条件:
- Android SDK 28
- Android Build Tools v28.0.3
- Android Support Repository(提供
android.support.v7.widget.CardView所在的 support 库)
其中 Gradle Wrapper 版本为 6.7.1,配置见 CardView/gradle/wrapper/gradle-wrapper.properties;Kotlin 版对应要求 SDK 27。若使用本示例开发环境时的 SDK 版本与上述不完全一致,可在 Android Studio 的 SDK Manager 中安装对应版本,或自行调整工程的 build 配置。
构建方式 README 给出了两条等价路径,任选其一即可:
命令行构建:在工程根目录(即
CardView/或CardViewKotlin/,注意 Kotlin 版需使用其自带的 gradlew)执行:./gradlew build该命令会依次执行编译、打包与测试任务。首次运行会由 Gradle Wrapper 自动下载 6.7.1 发行版并拉取依赖,耗时取决于网络状况。
Android Studio 导入:使用File → Import Project(或 Open)选中工程根目录,由 IDE 基于 settings.gradle(内容为
include 'Application')识别Application模块并完成同步后,即可直接 Run 到模拟器或真机。
工程采用单模块结构:Application是唯一的 app 模块,其入口CardViewActivity已在 AndroidManifest.xml 中声明为带MAIN/LAUNCHERintent-filter 的启动 Activity。
界面骨架:Activity 承载 Fragment,布局定义卡片
整个示例采用 "Activity + 单 Fragment" 的经典组织方式。
CardViewActivity.java 中,onCreate只做两件事:设置activity_card_view布局,并在savedInstanceState == null时(即首次创建,避免旋转重建后重复添加)向R.id.container容器提交CardViewFragment.newInstance():
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_card_view); if (savedInstanceState == null) { getFragmentManager().beginTransaction() .add(R.id.container, CardViewFragment.newInstance()) .commit(); } }容器布局 activity_card_view.xml 只是一个带四边 16dp 内边距的FrameLayout(边距值定义在 dimens.xml 的activity_horizontal_margin/activity_vertical_margin)。
真正的卡片界面在 fragment_card_view.xml 中:外层是ScrollView,内部为垂直LinearLayout,依次包含一个 CardView 与两个"标签 + SeekBar"水平行。Kotlin 版 fragment_card_view.xml 结构相同,差异仅在把硬编码的100dp圆角半径/高度抽成了 card_elevation 与 card_radius 尺寸资源。
核心:CardView 布局写法与两大关键属性
CardView 的声明方式与普通 ViewGroup 无异,关键在于它独有的两个属性。示例中的完整声明如下(摘自 fragment_card_view.xml):
<android.support.v7.widget.CardView android:id="@+id/cardview" android:layout_width="fill_parent" android:layout_height="wrap_content" android:elevation="100dp" card_view:cardBackgroundColor="@color/cardview_initial_background" card_view:cardCornerRadius="8dp" android:layout_marginLeft="@dimen/margin_large" android:layout_marginRight="@dimen/margin_large" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/margin_medium" android:text="@string/cardview_contents" /> </android.support.v7.widget.CardView>两个注意点需要特别说明:
- 自定义属性命名空间:
cardCornerRadius、cardBackgroundColor属于 CardView 自定义属性,必须使用http://schemas.android.com/apk/res-auto命名空间(即card_view:前缀,见布局根元素的xmlns:card_view声明),而不是android:前缀; android:elevation使用系统命名空间:阴影高度即 Android 5.0 引入的 Material Design 海拔(elevation)概念,因此走系统属性通道。
下面逐一拆解 CardView 的属性与作用原理。
cardCornerRadius:圆角半径
card_view:cardCornerRadius控制卡片四角的圆角弧度,示例初始值为8dp。这个值直接影响背景绘制的Path圆角矩形形状,同时也决定了内容裁剪的边界。
在示例的交互逻辑中,CardViewFragment.java 为 Radius SeekBar 注册了监听器,滑块每次变化都调用mCardView.setRadius(progress),progress 取值为 SeekBar 的进度值(0 到滑块上限,单位实际被当作 dp 的浮点值使用):
mRadiusSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { Log.d(TAG, String.format("SeekBar Radius progress : %d", progress)); mCardView.setRadius(progress); } ... });对应 Kotlin 版则使用属性语法cardView.radius = progress.toFloat()(见 CardViewFragment.kt)。特别提醒:示例中setRadius(progress)传入的是整型 progress,实际以 dp 语义参与绘制——这一点从布局中cardCornerRadius="8dp"的初始值和 SeekBar 交互可见;在自己的项目中应使用setRadius(dp2px(...))之类的 dp→px 换算,或直接使用cardCornerRadius的 dp 资源,以免在不同密度屏幕上出现偏差。
elevation:阴影高度(海拔)
android:elevation是 Material Design 中通过 Z 轴海拔产生投影的核心属性:海拔越高,卡片下方投影越深、越柔和、范围越大。示例布局中给出了一个演示性的较大初始值100dp,CardViewFragment.java 中对应的 Elevation SeekBar 监听器则调用mCardView.setElevation(progress):
mElevationSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { Log.d(TAG, String.format("SeekBar Elevation progress : %d", progress)); mCardView.setElevation(progress); } ... });Kotlin 版对应cardView.elevation = progress.toFloat()。
这里有一个值得展开的原理性细节:CardView 是少数同时打通了"绘制阴影"与"硬件渲染"的 support 库控件。从源码结构看,CardView 在 Lollipop 及以上系统通过 elevation 使用原生阴影渲染,在低版本系统则回退到自绘阴影实现;它的setElevation方法(即示例调用的入口)会同步更新背景层与阴影层,setRadius则会重算背景RoundRectDrawable的形状,并联动裁剪内容。示例通过两个 SeekBar 让你直观地看到:圆角只影响卡片外形,阴影随 elevation 变化——二者是独立可调的两个维度。
cardBackgroundColor:卡片背景色
card_view:cardBackgroundColor设置卡片背景色。示例通过 colors.xml 定义了初始背景色cardview_initial_background = #71C3DE(浅蓝色),使圆角与投影效果在演示中一目了然。若不设置,CardView 会使用主题默认的背景色。
交互与验证:测试用例如何锁死行为
CardView 示例的一个亮点在于:它把"SeekBar 调节圆角/高度"这一交互写成了可自动验证的仪器测试,这既是示例质量的保证,也是理解setRadius/setElevation语义的绝佳教材。
Java 版 SampleTests.java 继承自ActivityInstrumentationTestCase2<CardViewActivity>,共三个用例:
- testPreconditions:断言 Activity、Fragment 以及两个 SeekBar 均已正确创建(依赖
mRadiusSeekBar、mElevationSeekBar字段,因此源码中为它们标注了@VisibleForTesting); - testRadiusSeekbarChangesRadiusOfCardView:在主线程中把 Radius SeekBar 进度设为 50,随后断言
mCardView.getRadius()等于 50.0f——证明滑块变化确实写入了 CardView 的圆角值; - testElevationSeekbarChangesElevationOfCardView:同理把 Elevation SeekBar 设为 40,断言
getElevation()返回 40.0f——证明高度属性随滑块联动。
public void testRadiusSeekbarChangesRadiusOfCardView() { getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { float radius = 50.0f; mFragment.mRadiusSeekBar.setProgress((int) radius); assertEquals(radius, mFragment.mCardView.getRadius()); } }); }由于getRadius()返回的是 px 值,而进度值以 dp 语义传入,因此在默认密度(mdpi,1dp = 1px)设备上二者直接相等。该测试也侧面印证了前文"progress 以 dp 语义参与绘制"的推断。
Kotlin 版 SampleTests.kt 则升级为 JUnit4 风格:@RunWith(AndroidJUnit4::class)+ActivityTestRule,测试逻辑与 Java 版一一对应。两套测试放在一起阅读,正好可以对照学习 Instrumentation 测试从ActivityInstrumentationTestCase2到ActivityTestRule的演进写法。
主题与资源组织:让卡片融入 App 视觉体系
示例还展示了如何让 CardView 与应用的 Material 主题协同。看 template-styles.xml 与 values-v21/base-template-styles.xml:默认(低版本)下Theme.Base继承android:Theme.Light,而在 API 21+ 的values-v21限定目录下则改为继承android:Theme.Material.Light。也就是说,示例在 Android 5.0 及以上设备上使用真正的 Material 主题,在低版本设备上退化为兼容主题——这与 CardView 本身"新系统原生渲染、旧系统兼容渲染"的设计哲学是一致的。
资源的组织也值得借鉴:字符串(卡片说明文案、两个 SeekBar 的标签)集中在 strings.xml(含cardview_contents中对android:elevation与card_view:cardCornerRadius用法的说明性文本),尺寸集中在 dimens.xml,颜色集中在 colors.xml,主题样式则通过values-sw600dp、values-v11、values-v21等限定目录按屏幕与版本差异化提供。
迁移到 AndroidX 与 Material 时代的实战建议
最后给出一条面向现代工程实践的衔接建议(基于仓库事实的延伸解读):本示例发布于 Android 5.0 + support library 时代,使用的是android.support.v7.widget.CardView。如今 Android 工程普遍迁移到 AndroidX,对应类是androidx.cardview.widget.CardView,核心属性与 API 一一对应(cardCornerRadius、cardBackgroundColor、elevation、setRadius()、setElevation()),因此本示例中的布局写法与交互逻辑几乎可以无痛平移;若追求更现代的 Material 风格卡片,可进一步参考 Material Components 的MaterialCardView(支持描边、按压态、选中态等增强能力)。需要说明的是,这些扩展能力不在本仓库范围内,迁移时请以目标依赖库的官方文档为准。
小结
围绕 CardView/README.md 这份仅数百字节的示例说明,结合仓库中的双版本实现,可以提炼出 CardView 的完整知识闭环:
- 是什么:Android 5.0 引入、由 support library 提供的卡片容器,自带圆角与投影;
- 怎么配:
card_view:cardCornerRadius控制圆角、android:elevation控制阴影、card_view:cardBackgroundColor控制背景,自定义属性必须走res-auto命名空间; - 怎么验证:参考 SampleTests.java / SampleTests.kt 的仪器测试写法,用 SeekBar 驱动
setRadius/setElevation并断言getRadius/getElevation的返回值。
对希望在列表、详情页或信息流中快速获得统一卡片视觉的开发者而言,CardView 至今仍是最低成本的方案之一;而这份示例的价值在于,它把"圆角 + 阴影"两个抽象概念变成了可拖动、可测试、可对照源码的具体工程。
- 示例工程
【免费下载链接】views-widgets-samples
Multiple samples showing the best practices in views-widgets on Android.
相关推荐
如何用跨平台资源下载工具轻松捕获网络资源:完整指南
如何用跨平台资源下载工具轻松捕获网络资源:完整指南 还在为无法保存喜欢的在线视频而烦恼吗?想要下载无水印内容却找不到合适的工具?今天我要介绍的跨平台资源下载工具
桌面应用网络音视频推荐使用:Android Shadow Layout —— 超越 CardView 的阴影效果库
推荐使用:Android Shadow Layout —— 超越 CardView 的阴影效果库 项目介绍 在移动应用开发中,视觉效果常常是提升用户体验的关键因
Android Material Design 列表与卡片实战:RecyclerView 与 CardView 完整使用指南
Android Material Design 列表与卡片实战:RecyclerView 与 CardView 完整使用指南 本篇指南基于 Android 官方
文档教程移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考