ViewGroup 是什么?

ViewGroup 是 Android UI 组件中的 容器视图(布局),它继承自 View,可以包含多个子 ViewViewGroup,并负责 对子 View 的管理和布局

在 Android 开发中,常见的 ViewGroup 主要包括:

  • LinearLayout(线性布局)
  • RelativeLayout(相对布局)
  • ConstraintLayout(约束布局)
  • FrameLayout(帧布局)
  • RecyclerView(用于列表显示)
  • ScrollView(滚动视图)
  • ViewPager(翻页视图)

ViewGroup 和 View 的区别

对比项ViewViewGroup
是否可以包含子视图❌ 不能✅ 可以
作用作为 UI 组件(如 TextViewButton作为布局容器(如 LinearLayoutConstraintLayout
主要职责处理自身的绘制和事件管理子 View 的位置、大小、排列规则
事件分发直接响应用户触摸负责 分发(dispatch) 事件给子 View

常见的 ViewGroup

1. LinearLayout(线性布局)

水平(horizontal)垂直(vertical) 方向排列子 View。

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:orientation="vertical">
 
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="按钮1"/>
 
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="按钮2"/>
</LinearLayout>

2. RelativeLayout(相对布局)

子 View 之间可以通过 相对关系(如 alignParentTopbelow)进行布局。

<RelativeLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content">
 
  <TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="我是文本"/>
 
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="按钮"
    android:layout_below="@id/textView"/>
</RelativeLayout>

3. ConstraintLayout(约束布局,推荐使用)

可以 **替代 RelativeLayout 和 **LinearLayout,提高性能,减少嵌套层级。

<ConstraintLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content">
 
  <Button
    android:id="@+id/btn1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="按钮"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toStartOf="parent"/>
</ConstraintLayout>

4. FrameLayout(帧布局)

子 View 默认都叠加在左上角,常用于 Fragment 或者 自定义布局

<FrameLayout
  android:layout_width="match_parent"
  android:layout_height="match_parent">
 
  <ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:src="@drawable/background"/>
 
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="叠加的文本"
    android:layout_gravity="center"/>
</FrameLayout>

5. RecyclerView(高效列表)

用于显示长列表,比 ListView 性能更好。

RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new MyAdapter(dataList));

ViewGroup 事件分发机制

ViewGroup 负责 事件的传递,主要流程:

  1. dispatchTouchEvent():事件分发方法,决定是否交给子 View 处理。
  2. onInterceptTouchEvent():决定是否拦截事件,拦截后交给 ViewGroup 自己处理。
  3. onTouchEvent()ViewGroupView 处理触摸事件。
    如果 ViewGroup拦截 了事件(onInterceptTouchEvent() 返回 true),事件就不会传递给子 View,而是由 ViewGroup 自己处理。

总结

  • ViewGroup容器,可以包含多个 ViewViewGroup,主要用于 布局管理
  • 常见的 ViewGroupLinearLayoutRelativeLayoutConstraintLayoutFrameLayoutRecyclerView 等。
  • ViewGroup 负责 对子 View 进行测量、布局、绘制,并处理 事件分发dispatchTouchEvent())。
  • 选择合适的 ViewGroup 可以 优化性能,如 尽量减少嵌套布局,推荐使用 ConstraintLayout

问题

setOnTouchListener 和 viewgroup 什么关系?

setOnTouchListener()View 的方法,所以 ViewGroup** 作为 View 的子类,也可以使用 setOnTouchListener() 来监听触摸事件**。
ViewGroup 有更强的事件处理能力,例如 拦截子 View 事件,所以在 ViewGroup 中,触摸事件的处理比普通 View 更复杂。

**1. ViewGroup 直接使用 **setOnTouchListener()

ViewGroup 作为普通 View 一样,可以使用 setOnTouchListener() 监听触摸事件:

myViewGroup.setOnTouchListener((v, event) -> {
    Log.d("Touch", "ViewGroup 被触摸");
    return true; // 返回 true,事件不会传递给子 View
});

但这样会影响子 View:

  • **如果 **return true,触摸事件 不会传递ViewGroup 内的子 View,子 View 无法响应 onClick() 等事件。
  • **如果 **return false,事件会 继续传递ViewGroup 内的子 View,子 View 可能会处理事件。
    2. ViewGroup 通过 onInterceptTouchEvent() 拦截子 View 事件
    ViewGroup 具有 onInterceptTouchEvent() 方法,可以 决定是否拦截触摸事件,而不是直接消耗事件:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        return true; // 拦截事件,不让子 View 处理
    }
    return super.onInterceptTouchEvent(ev);
}
  • **返回 **true:表示 ViewGroup拦截触摸事件,子 View 无法接收到触摸事件,事件会交给 ViewGroup.onTouchEvent() 处理。
  • **返回 **false:事件会继续传递给子 View 进行处理。

**3. setOnTouchListener() VS **onInterceptTouchEvent()

方法作用适用场景
setOnTouchListener()监听触摸事件需要处理 ViewGroup 本身的触摸,如拖拽
onInterceptTouchEvent()决定是否拦截事件需要控制子 View 是否接收事件,如滑动冲突

示例:ViewGroup 拦截触摸事件

public class MyViewGroup extends LinearLayout {
    public MyViewGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            return true; // 拦截事件,交给 onTouchEvent 处理
        }
        return false; // 不拦截,继续传递给子 View
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("Touch", "ViewGroup 处理触摸事件");
        return true;
    }
}

在这个例子中:

  • onInterceptTouchEvent()拦截ACTION_DOWN,子 View 不会收到触摸事件。
  • onTouchEvent() 处理 ViewGroup 自己的触摸事件。
    4. ViewGroup 作为手势代理
    有时候 ViewGroup 需要监听整个区域的手势,但又需要让子 View 继续接收点击事件。这时,可以在 onInterceptTouchEvent()检测滑动手势并拦截,否则让子 View 继续处理:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_MOVE) {
        return true; // 只拦截滑动,不拦截点击
    }
    return false;
}

这样:

  • 滑动手势ACTION_MOVE)被 ViewGroup 处理,比如用于 RecyclerView 滑动冲突。
  • 点击事件ACTION_DOWNACTION_UP)继续传递给子 View。

更新: 2025-03-24 10:51:30
原文: https://www.yuque.com/dongpozhouzi-mshe3/zhm85g/mw4k2mof1xxkgpg5


相关笔记