安卓基础布局核心知识点整理

发布于:2025-08-02 ⋅ 阅读:(12) ⋅ 点赞:(0)

一、基础布局类型

  1. LinearLayout

    • 线性排列子 View,支持垂直 / 水平方向
    • 关键属性:android:orientationlayout_weight
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="按钮1"/>
            
        <Button
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:text="按钮2"/>
    </LinearLayout>
    
  2. RelativeLayout

    • 通过相对位置规则放置子 View
    • 关键属性:layout_abovelayout_toRightOflayout_centerInParent
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <TextView
            android:id="@+id/text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="顶部文本"/>
            
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/text1"
            android:layout_alignParentRight="true"
            android:text="右侧按钮"/>
    </RelativeLayout>
    
  3. FrameLayout

    • 所有子 View 堆叠在左上角
    • 适合实现图层效果或单元素展示
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="200dp">
        
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/image_bg"
            android:scaleType="centerCrop"/>
            
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="图片标题"
            android:textColor="#FFFFFF"/>
    </FrameLayout>
    
  4. ConstraintLayout

    • 基于约束关系定位子 View,性能优化
    • 关键特性:链 (Chains)、屏障 (Barrier)、引导线 (GuideLine)
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <EditText
            android:id="@+id/input"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@id/button"
            app:layout_constraintTop_toTopOf="parent"/>
            
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="@id/input"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
    

二、布局性能优化

  1. 减少布局层级

    • 使用 ConstraintLayout 替代嵌套 LinearLayout
    • 避免超过 3 层布局嵌套
  2. 布局复用技术

    • <include>标签:复用布局文件
    <include layout="@layout/common_toolbar"/>
    
     
    • <merge>标签:减少布局层级
    <merge xmlns:android="http://schemas.android.com/apk/res/android">
        <TextView android:layout_width="match_parent" .../>
        <Button android:layout_width="wrap_content" .../>
    </merge>
    
  3. 延迟加载

    • ViewStub:需要时再加载布
    <ViewStub
        android:id="@+id/stub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/progress_view"/>
    
     

    java

    ViewStub stub = findViewById(R.id.stub);
    View inflated = stub.inflate(); // 延迟加载
    

三、尺寸单位

单位 说明 适用场景
px 像素 不推荐使用
dp/dip 密度无关像素 大多数场景
sp 缩放像素 文本大小
pt 点 (1/72 英寸) 很少使用
% 百分比 ConstraintLayout 中使用

四、常见面试问题

  1. LinearLayout 中 layout_weight 的工作原理

    • 剩余空间按权重比例分配
    • 建议将 width/height 设为 0dp 优化性能
  2. RelativeLayout 与 LinearLayout 性能对比

    • RelativeLayout:单次测量,但布局算法复杂
    • LinearLayout:可能需要多次测量 (有 weight 时)
    • ConstraintLayout 性能最优
  3. 如何实现流式布局 (FlowLayout)

    • 自定义 View 继承 ViewGroup
    • 重写 onMeasure 和 onLayout 方法
    • 或使用第三方库:HFlowLayout
  4. 谈谈 ConstraintLayout 的优势

    • 扁平化布局减少层级
    • 支持复杂约束关系
    • 性能优于 RelativeLayout
    • 可视化编辑器友好

五、自定义布局

  1. 继承 ViewGroup 并重写:

    • onMeasure ():测量子 View 尺寸
    • onLayout ():放置子 View 位置

    java

    public class CustomLayout extends ViewGroup {
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // 测量所有子View
            measureChildren(widthMeasureSpec, heightMeasureSpec);
            // 计算自身尺寸
            setMeasuredDimension(width, height);
        }
        
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            // 放置每个子View的位置
            child.layout(left, top, right, bottom);
        }
    }
    
  2. 自定义属性:

    xml

    <declare-styleable name="CustomLayout">
        <attr name="item_space" format="dimension"/>
    </declare-styleable>
    
     

    java

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomLayout);
    int itemSpace = a.getDimensionPixelSize(R.styleable.CustomLayout_item_space, 0);
    a.recycle();
    

六、最佳实践

  1. 使用 ConstraintLayout 作为主要布局

  2. 使用 VectorDrawable 替代多分辨率图片

  3. 为不同屏幕尺寸创建布局别名

    xml

    <!-- values-sw600dp/dimens.xml -->
    <dimen name="card_padding">24dp</dimen>
    
    <!-- values/dimens.xml -->
    <dimen name="card_padding">16dp</dimen>
    
  4. 使用 Data Binding 减少 findViewById

    xml

    <layout xmlns:android="http://schemas.android.com/apk/res/android">
        <data>
            <variable name="user" type="com.example.User"/>
        </data>
        <TextView
            android:text="@{user.name}"
            .../>
    </layout>
    

七、补充面试问题

1. View 的绘制流程是怎样的?
  • 核心流程measure() → layout() → draw()
    • measure:确定 View 的测量宽高(通过父容器传递的 MeasureSpec 和自身需求计算)
    • layout:确定 View 在父容器中的位置(left/top/right/bottom)
    • draw:绘制 View 的内容(背景、文本、图形等)
2. MeasureSpec 的作用是什么?
  • 定义:32 位 int 值,高 2 位表示模式,低 30 位表示尺寸
  • 三种模式
    • EXACTLY:父容器已确定精确尺寸(如match_parent或固定值)
    • AT_MOST:父容器指定最大尺寸(如wrap_content
    • UNSPECIFIED:父容器不对 View 尺寸做限制(如 ScrollView)
3. 如何优化自定义 View 的性能?
  • 关键优化点
    • 避免在onDraw()中创建对象(如 Paint 应在初始化时创建)
    • 使用setWillNotDraw(false)明确需要绘制
    • 实现onSaveInstanceState()保存状态
    • 使用Canvas.clipRect()减少重绘区域
    • 考虑硬件加速兼容性
4. FrameLayout、LinearLayout、RelativeLayout 的使用场景?
  • FrameLayout:简单堆叠场景(如加载框、图片叠加)
  • LinearLayout:单行 / 单列排列(避免过多 weight 嵌套)
  • RelativeLayout:复杂相对位置关系(已逐渐被 ConstraintLayout 替代)
5. 谈谈 ConstraintLayout 的链 (Chain) 机制
  • 作用:水平 / 垂直方向上的权重分配
  • 类型
    • Spread(默认):均匀分布元素
    • Spread Inside:两端元素贴近边界,中间均分
    • Packed:元素紧凑排列,可通过chainStyle设置权重
6. 什么情况下会导致 View 的过度绘制?如何检测和修复?
  • 过度绘制:同一像素被多次绘制(如多层重叠背景)
  • 检测工具:开发者选项 → 显示过度绘制区域
  • 修复方法
    • 移除不必要的背景
    • 使用<merge>标签减少层级
    • 按需显示隐藏视图(如使用 ViewStub)
    • 优化 Drawable 资源(避免多层 Alpha 叠加)
7. 如何实现一个自适应屏幕方向的布局?
  • 方案
    • 创建layout-landlayout-port目录存放不同方向布局
    • 使用ConstraintLayoutBarrierGuideline动态调整
    • 代码中监听屏幕旋转事件:

      java

      @Override
      public void onConfigurationChanged(Configuration newConfig) {
          super.onConfigurationChanged(newConfig);
          // 重新布局或加载对应资源
      }
      
8. 对比match_parentwrap_content在 MeasureSpec 中的区别
属性 父容器为 EXACTLY 父容器为 AT_MOST
match_parent EXACTLY (父尺寸) EXACTLY (父尺寸)
wrap_content AT_MOST (父尺寸) AT_MOST (父尺寸)
9. 自定义 View 如何支持 padding 和 margin?
  • Padding:在onDraw()中处理(如canvas.translate(paddingLeft, paddingTop)
  • Margin:需在父容器(自定义 ViewGroup)中解析 LayoutParams 并应用
10. 如何实现布局的懒加载?
  • 方案
    • 使用ViewStub(仅加载一次)
    • 动态 addView(如 Fragment 的setUserVisibleHint()
    • 配合 Jetpack 的LazyColumn/LazyRow(Compose)

八、代码实战问题

1. 手写一个正方形 ImageView

java

public class SquareImageView extends ImageView {
    public SquareImageView(Context context) {
        super(context);
    }
    
    public SquareImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        int size = Math.min(width, height);
        setMeasuredDimension(size, size);
    }
}
2. 优化以下布局(减少层级)

xml

<!-- 原始布局 -->
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
        
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

<!-- 优化后(ConstraintLayout) -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    
    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"/>
        
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/image"/>
</androidx.constraintlayout.widget.ConstraintLayout>