Android面试题目基础总结(一)

发布于:2025-03-16 ⋅ 阅读:(10) ⋅ 点赞:(0)

必须知道的Android基础知识如下:

1. Activity 生命周期

Activity 是 Android 应用中最基本的组件之一,它有自己完整的生命周期,主要包括以下几个关键方法:

  • onCreate():Activity 首次创建时调用,用于初始化操作,如设置布局、初始化成员变量等。
  • onStart():Activity 即将可见时调用。
  • onResume():Activity 获得焦点并开始与用户交互时调用,此时 Activity 位于前台。
  • onPause():Activity 失去焦点但仍然可见时调用,通常用于保存一些临时数据或停止一些敏感操作。
  • onStop():Activity 完全不可见时调用,可进行一些资源释放操作。
  • onDestroy():Activity 即将被销毁时调用,用于释放所有资源。
  • onRestart():Activity 从停止状态重新启动时调用,先调用 onRestart (),然后依次调用 onStart () 和 onResume ()。

场景提问:

Activity A 到 B 的生命周期

当从 Activity A 启动 Activity B 时,生命周期方法的调用顺序如下:

  1. Activity A:调用 onPause(),因为要暂停当前 Activity 以启动新的 Activity。
  2. Activity B:依次调用 onCreate()onStart()onResume(),使 Activity B 进入前台并与用户交互。
  3. Activity A:如果 Activity B 是透明主题或没有完全覆盖 Activity A,Activity A 停留在 onPause() 状态;如果 Activity B 完全覆盖 Activity A,Activity A 继续调用 onStop()

当从 Activity B 返回 Activity A 时,生命周期方法调用顺序如下:

  1. Activity B:调用 onPause()
  2. Activity A:依次调用 onRestart()onStart()onResume(),重新回到前台。
  3. Activity B:调用 onStop() 和 onDestroy(),Activity B 被销毁。

2.Handler 及相关类

Handler 是 Android 中用于在不同线程之间传递消息和执行任务的机制。主要涉及以下几个类:

  • Handler:用于发送和处理消息。可以在主线程或子线程中创建 Handler 对象,通过 sendMessage() 或 post() 方法发送消息或任务。
  • Message:消息对象,用于封装要传递的数据。可以通过 Message.obtain() 方法获取 Message 对象,避免频繁创建对象造成内存开销。
  • MessageQueue:消息队列,用于存储 Handler 发送的消息。每个线程只有一个 MessageQueue,通过 Looper 来管理。
  • Looper:循环器,负责从 MessageQueue 中取出消息并分发给相应的 Handler 进行处理。每个线程只能有一个 Looper 对象,主线程默认已经创建了 Looper。

场景提问:

子线程使用 Handler

在子线程中使用 Handler 时,需要先调用 Looper.prepare() 方法为当前线程创建 Looper 对象,然后创建 Handler 对象,最后调用 Looper.loop() 方法启动消息循环。示例代码如下:

class MyThread extends Thread {
    private Handler mHandler;

    @Override
    public void run() {
        // 为当前线程创建 Looper 对象
        Looper.prepare();
        // 创建 Handler 对象
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // 处理消息
            }
        };
        // 启动消息循环
        Looper.loop();
    }

    public Handler getHandler() {
        return mHandler;
    }
}

3.事件分发

事件分发是 Android 中处理用户触摸事件的机制,主要涉及三个核心方法:dispatchTouchEvent()onInterceptTouchEvent() 和 onTouchEvent()。事件分发的流程如下:

  1. 当用户触摸屏幕时,事件首先传递到 Activity 的 dispatchTouchEvent() 方法。
  2. Activity 将事件传递给顶层的 ViewGroup,ViewGroup 调用 dispatchTouchEvent() 方法进行事件分发。
  3. ViewGroup 可以通过 onInterceptTouchEvent() 方法决定是否拦截事件。如果拦截,事件将不再向下传递,而是由 ViewGroup 自身的 onTouchEvent() 方法处理;如果不拦截,事件将继续向下传递给子 View。
  4. 子 View 调用 dispatchTouchEvent() 方法,最终调用 onTouchEvent() 方法处理事件。
  5. 如果子 View 没有处理事件,事件将向上回传给父 ViewGroup,由父 ViewGroup 的 onTouchEvent() 方法处理。

场景问题:

事件分发的方法

  • dispatchTouchEvent(MotionEvent ev):用于分发触摸事件,是事件分发的入口方法。在 Activity、ViewGroup 和 View 中都有该方法。
  • onInterceptTouchEvent(MotionEvent ev):用于拦截触摸事件,只有 ViewGroup 有该方法。返回 true 表示拦截事件,返回 false 表示不拦截事件。
  • onTouchEvent(MotionEvent event):用于处理触摸事件,在 Activity、ViewGroup 和 View 中都有该方法。返回 true 表示事件已处理,返回 false 表示事件未处理。

4.自定义 View 的绘制方法

自定义 View 时,通常需要重写以下几个绘制方法:

  • onMeasure(int widthMeasureSpec, int heightMeasureSpec):用于测量 View 的大小,根据父容器的测量规格和自身的布局参数确定 View 的宽高。
  • onLayout(boolean changed, int left, int top, int right, int bottom):用于确定 View 在父容器中的位置,通常在 ViewGroup 中使用。
  • onDraw(Canvas canvas):用于绘制 View 的内容,通过 Canvas 对象进行绘制操作,如绘制图形、文本等。

示例代码如下:

public class MyView extends View {
    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 测量 View 的大小
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 绘制内容
        Paint paint = new Paint();
        paint.setColor(Color.RED);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint);
    }
}

实战扩展:

RelativeLayout 中按钮 B 在 A 前面时的点击事件分发问题

现象描述
当 RelativeLayout 中有两个按钮 A 和 B,且 B 的布局参数设置为android:layout_above="@id/A"或通过android:layout_toRightOf等方式覆盖在 A 上方时,点击 B 区域会触发 B 的点击事件,而点击 A 区域可能无法触发 A 的事件(因为 B 在视觉上覆盖了 A)。

事件分发流程

  1. 事件传递路径

    • 事件从 Activity 的dispatchTouchEvent开始,传递到 RelativeLayout(ViewGroup)。
    • RelativeLayout 调用dispatchTouchEvent,遍历子 View(B 和 A)。
    • 由于 B 的布局层级在 A 上方,事件会优先传递给 B。
    • B 的onTouchEvent返回true(默认情况),事件被消费,A 无法接收到事件。
  2. 问题原因

    • B 的布局覆盖了 A 的区域,事件被 B 拦截并处理,导致 A 无法响应点击。

解决方法

  • 方案 1:调整布局顺序
    将 A 的布局参数设置为android:layout_above="@id/B",使 A 在视觉上位于 B 上方,此时点击 A 区域会触发 A 的事件。

  • 方案 2:设置 B 为透明
    在 B 的布局中添加android:clickable="false"android:alpha="0",使 B 不响应点击事件,事件穿透到下方的 A。

<Button
    android:id="@+id/buttonB"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:clickable="false"
    android:text="B" />

方案 3:自定义事件分发
重写 RelativeLayout 的onInterceptTouchEvent,强制将事件传递给 A。

public class CustomRelativeLayout extends RelativeLayout {
    public CustomRelativeLayout(Context context) {
        super(context);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // 不拦截事件,让子View处理
        return false;
    }
}

两个 RecyclerView 在 ScrollView 中的滑动冲突处理

问题现象
当两个 RecyclerView 嵌套在 ScrollView 中时,滑动时可能出现滚动不流畅、RecyclerView 无法独立滑动或与 ScrollView 冲突的问题。

解决方案

方案 1:使用 NestedScrollView 替代 ScrollView

NestedScrollView 是 Android 5.0(API 21)引入的增强型 ScrollView,支持嵌套滚动,可与 RecyclerView 协同工作。

<androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>
</androidx.core.widget.NestedScrollView>
方案2:禁用 RecyclerView 的滑动

设置 RecyclerView 的android:nestedScrollingEnabled="false",并通过setHasFixedSize(true)优化性能。

<androidx.recyclerview.widget.RecyclerView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:nestedScrollingEnabled="false"
    android:hasFixedSize="true" />
方案 3:重写 RecyclerView 的滑动逻辑

通过重写 RecyclerView 的onTouchEvent,手动控制滑动冲突。

 

public class CustomRecyclerView extends RecyclerView {
    public CustomRecyclerView(Context context) {
        super(context);
    }

    public CustomRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent e) {
        // 当RecyclerView滑动到顶部时,允许父容器(ScrollView)滚动
        if (getScrollY() == 0 && e.getAction() == MotionEvent.ACTION_MOVE) {
            getParent().requestDisallowInterceptTouchEvent(false);
        } else {
            getParent().requestDisallowInterceptTouchEvent(true);
        }
        return super.onTouchEvent(e);
    }
}
设置 ScrollView 的滑动方向

如果两个 RecyclerView 的滑动方向与 ScrollView 一致(如垂直方向),可通过以下方式优化:

  • 将 RecyclerView 的android:orientation="horizontal"设置为水平滑动。
  • 或在 ScrollView 内部使用 LinearLayout 包裹 RecyclerView,并设置android:orientation="horizontal"

 总结扩展:

   RelativeLayout 中按钮层级导致点击事件被覆盖时,可通过调整布局或设置点击属性解决;而嵌套在 ScrollView 中的两个 RecyclerView 滑动冲突,建议使用 NestedScrollView 或禁用嵌套滑动。

 感谢观看!!!


网站公告

今日签到

点亮在社区的每一天
去签到