15、自适应布局:ConstraintLayout与百分比布局在大屏上的应用

大屏适配,说白了就是让同一个界面在手机、平板、电视上都能看得舒服。我刚开始做TV端开发时,踩过一个坑:用LinearLayout写了个菜单,手机上看挺好,一上电视,左边空出一大片,右边按钮挤成一团。嗯,那时候我就意识到——布局不能写死。

这一章,咱们就聊聊两个核心武器:ConstraintLayout百分比布局。它们不是二选一的关系,而是互补的。我个人习惯是:ConstraintLayout搭骨架,百分比布局调细节。

大屏自适应布局核心体系 ConstraintLayout 百分比布局 相对定位(app:layout_constraint) 链(Chain)与权重 Guideline / Barrier PercentFrameLayout PercentRelativeLayout 百分比宽高 + margin 组合使用 → 真正的大屏自适应

15.1 为什么大屏上不能用固定尺寸?

你想想看,电视屏幕从32寸到85寸都有,分辨率从720p到4K甚至8K。如果你写死一个按钮宽200dp,在手机上可能占半个屏幕,在电视上就变成一个小不点。用户得拿着遥控器凑近了点,体验极差。

我在项目中遇到过最典型的场景:一个视频列表页,手机上是2列,平板上是4列,电视上我们希望是6列。如果用固定宽度写,每种屏幕都得单独调。后来我用ConstraintLayout的链+百分比,一套布局搞定。

核心原则:永远不要用dp写死宽高,除非是图标或分割线这种固定元素。大屏上所有容器尺寸都应该是相对的。

15.2 ConstraintLayout:大屏布局的骨架

ConstraintLayout最厉害的地方,是它允许你用「约束」来描述控件之间的相对位置。说白了,就是告诉系统:「这个按钮的左边,贴着那个图片的右边,间距16dp」。系统会自动计算位置,不管屏幕多大。

15.2.1 相对定位:告别嵌套

以前用LinearLayout做复杂布局,经常要嵌套三四层。嵌套多了,性能就下来了。ConstraintLayout可以做到扁平化——一个根布局,所有子控件通过约束相连。

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="电影名称"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="32dp"/>

    <ImageView
        android:id="@+id/poster"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/title"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_margin="16dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

注意看,layout_width="0dp" 表示宽度由约束决定。这里标题的左右都约束到父容器,所以它会自动撑满屏幕宽度。图片的上下左右都有约束,它会自动填满剩余空间。嗯,这就是自适应。

我的习惯:在大屏布局中,尽量把宽高都设成0dp,让约束来决定尺寸。只在需要固定比例时用wrap_content。

15.2.2 链(Chain):均匀分布的利器

电视上经常需要一排按钮均匀分布,比如「首页、分类、搜索、我的」四个Tab。用Chain可以轻松实现。

<Button
    android:id="@+id/btn_home"
    android:layout_width="0dp"
    android:layout_height="48dp"
    android:text="首页"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toStartOf="@id/btn_category"
    app:layout_constraintTop_toTopOf="parent"/>

<Button
    android:id="@+id/btn_category"
    android:layout_width="0dp"
    android:layout_height="48dp"
    android:text="分类"
    app:layout_constraintStart_toEndOf="@id/btn_home"
    app:layout_constraintEnd_toStartOf="@id/btn_search"
    app:layout_constraintTop_toTopOf="parent"/>

<Button
    android:id="@+id/btn_search"
    android:layout_width="0dp"
    android:layout_height="48dp"
    android:text="搜索"
    app:layout_constraintStart_toEndOf="@id/btn_category"
    app:layout_constraintEnd_toStartOf="@id/btn_mine"
    app:layout_constraintTop_toTopOf="parent"/>

<Button
    android:id="@+id/btn_mine"
    android:layout_width="0dp"
    android:layout_height="48dp"
    android:text="我的"
    app:layout_constraintStart_toEndOf="@id/btn_search"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"/>

然后在代码或XML中设置链式约束:

app:layout_constraintHorizontal_chainStyle="spread"

这样四个按钮就会自动等宽、等间距地铺满屏幕。电视上看起来非常整齐。

15.2.3 Guideline:百分比定位的替代方案

ConstraintLayout自带Guideline,可以按百分比定位。比如我想让一个控件位于屏幕30%的位置:

<androidx.constraintlayout.widget.Guideline
    android:id="@+id/guideline_left"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.3"/>

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="登录"
    app:layout_constraintStart_toStartOf="@id/guideline_left"
    app:layout_constraintTop_toTopOf="parent"/>

这个Guideline就像一根看不见的线,位置是屏幕宽度的30%。按钮的左边对齐这根线。不管屏幕多宽,按钮始终在30%的位置。我在电视遥控器菜单里经常用这个技巧。

我曾经踩过的坑:Guideline的百分比是相对于父容器的。如果你在嵌套布局里用Guideline,它参考的是直接父容器,不是整个屏幕。记得检查父容器是不是match_parent。

15.3 百分比布局:精细控制每一寸

ConstraintLayout虽然强大,但有些场景还是不够灵活。比如我想让一个卡片占屏幕宽度的45%,另一个占55%,中间留10%的间距。用ConstraintLayout的链可以实现,但代码写起来有点绕。这时候百分比布局就派上用场了。

15.3.1 PercentFrameLayout 和 PercentRelativeLayout

这两个是support库提供的,现在已经迁移到androidx。用法很简单:

<androidx.percentlayout.widget.PercentFrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="左半屏"
        app:layout_widthPercent="45%"
        app:layout_heightPercent="50%"
        app:layout_marginStartPercent="2.5%"/>

    <Button
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:text="右半屏"
        app:layout_widthPercent="45%"
        app:layout_heightPercent="50%"
        app:layout_marginStartPercent="52.5%"/>

</androidx.percentlayout.widget.PercentFrameLayout>

你看,layout_widthPercent="45%" 直接告诉系统:这个按钮占父容器宽度的45%。两个按钮各45%,中间自动留出10%的间距。非常直观。

15.3.2 百分比margin:间距也自适应

大屏上不仅控件尺寸要变,间距也要变。电视上16dp的间距看起来太小,48dp才合适。用百分比margin可以做到:

app:layout_marginStartPercent="3%"
app:layout_marginTopPercent="2%"

这样间距会随着屏幕尺寸等比缩放。我在做电视端「推荐位」布局时,就是靠百分比margin让卡片之间的间距在不同尺寸下都保持视觉舒适。

实战建议:百分比布局适合用在「固定比例」的场景,比如二分屏、三分屏、九宫格。ConstraintLayout适合用在「相对位置」复杂的场景,比如图文混排、表单。两者结合使用效果最好。

15.4 组合实战:一个电视端视频详情页

咱们来看一个真实案例。电视端视频详情页,布局要求:

  • 左侧是视频封面,占屏幕宽度的35%
  • 右侧是标题、简介、按钮,占屏幕宽度的60%
  • 中间留5%的间距
  • 底部有一排操作按钮,均匀分布

我的做法是:外层用ConstraintLayout,左侧封面用Guideline定位到35%位置,右侧内容用链约束到Guideline右侧。底部按钮用Chain实现均匀分布。

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="24dp">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/gl_left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.35"/>

    <!-- 左侧封面 -->
    <ImageView
        android:id="@+id/cover"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/gl_left"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:scaleType="centerCrop"/>

    <!-- 右侧内容 -->
    <TextView
        android:id="@+id/title"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toEndOf="@id/gl_left"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginStart="16dp"
        android:textSize="28sp"/>

    <!-- 底部按钮链 -->
    <Button
        android:id="@+id/btn_play"
        android:layout_width="0dp"
        android:layout_height="48dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/btn_fav"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <Button
        android:id="@+id/btn_fav"
        android:layout_width="0dp"
        android:layout_height="48dp"
        app:layout_constraintStart_toEndOf="@id/btn_play"
        app:layout_constraintEnd_toStartOf="@id/btn_share"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <Button
        android:id="@+id/btn_share"
        android:layout_width="0dp"
        android:layout_height="48dp"
        app:layout_constraintStart_toEndOf="@id/btn_fav"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

这个布局在手机上、平板上、电视上都能自适应。左侧封面始终占35%,右侧内容占60%,底部三个按钮均匀分布。你想想看,如果不用这些技术,得写多少个dimens文件?

我的经验:在大屏开发中,dimens文件只用来定义字体大小和最小触摸目标(48dp),其他所有尺寸都用百分比或约束。这样一套布局走天下。

15.5 避坑指南

最后分享几个我踩过的坑,希望能帮你少走弯路。

  • ConstraintLayout版本问题:早期版本对百分比支持不完善,建议使用2.0以上版本。我曾在1.1版本上折腾了半天,升级后就好了。
  • 百分比布局的父容器:PercentFrameLayout的百分比是相对于父容器的宽高。如果父容器不是match_parent,百分比计算会出错。记得检查。
  • 链的权重:ConstraintLayout的链也支持权重(类似LinearLayout的weight),用app:layout_constraintHorizontal_weight设置。但注意,权重只在链模式下生效。
  • 性能问题:不要在一个ConstraintLayout里放太多控件(超过50个)。虽然它比嵌套LinearLayout好,但也不是万能的。复杂页面建议分组。

嗯,这一章的内容就到这里。自适应布局的核心就是「相对」二字——相对位置、相对尺寸、相对间距。掌握了ConstraintLayout和百分比布局,大屏适配就不再是难题。


公众号:蓝海资料掘金营,微信deep3321