必须知道的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 时,生命周期方法的调用顺序如下:
- Activity A:调用
onPause()
,因为要暂停当前 Activity 以启动新的 Activity。 - Activity B:依次调用
onCreate()
、onStart()
、onResume()
,使 Activity B 进入前台并与用户交互。 - Activity A:如果 Activity B 是透明主题或没有完全覆盖 Activity A,Activity A 停留在
onPause()
状态;如果 Activity B 完全覆盖 Activity A,Activity A 继续调用onStop()
。
当从 Activity B 返回 Activity A 时,生命周期方法调用顺序如下:
- Activity B:调用
onPause()
。 - Activity A:依次调用
onRestart()
、onStart()
、onResume()
,重新回到前台。 - 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()
。事件分发的流程如下:
- 当用户触摸屏幕时,事件首先传递到 Activity 的
dispatchTouchEvent()
方法。 - Activity 将事件传递给顶层的 ViewGroup,ViewGroup 调用
dispatchTouchEvent()
方法进行事件分发。 - ViewGroup 可以通过
onInterceptTouchEvent()
方法决定是否拦截事件。如果拦截,事件将不再向下传递,而是由 ViewGroup 自身的onTouchEvent()
方法处理;如果不拦截,事件将继续向下传递给子 View。 - 子 View 调用
dispatchTouchEvent()
方法,最终调用onTouchEvent()
方法处理事件。 - 如果子 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)。
事件分发流程:
事件传递路径:
- 事件从 Activity 的
dispatchTouchEvent
开始,传递到 RelativeLayout(ViewGroup)。 - RelativeLayout 调用
dispatchTouchEvent
,遍历子 View(B 和 A)。 - 由于 B 的布局层级在 A 上方,事件会优先传递给 B。
- B 的
onTouchEvent
返回true
(默认情况),事件被消费,A 无法接收到事件。
- 事件从 Activity 的
问题原因:
- 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 或禁用嵌套滑动。
感谢观看!!!