Handler面试题
Handler的作用:
当我们需要在子线程处理耗时的操作(例如访问网络,数据库的操作),而当耗时的操作完成后,需要更新UI,这就需要使用Handler来处理,因为子线程不能做更新UI的操作。Handler能帮我们很容易的把任务(在子线程处理)切换回它所在的线程。简单理解,Handler就是解决线程和线程之间的通信的。
1.Handler连环之说说Handler的作用,以及每个类让他们的角色
使用的handler的两种形式
1.在主线程使用handler;
2.在子线程使用handler。
Handler的消息处理主要有五个部分组成
- Message
- Handler
- Message Queue
- Looper
- ThreadLocal
首先简要的了解这些对象的概念
- Message:Message是在线程之间传递的消息,它可以在内部携带少量的数据,用于线程之间交换数据。Message有四个常用的字段,what字段,arg1字段,arg2字段,obj字段。what,arg1,arg2可以携带整型数据,obj可以携带object对象。
- Handler:它主要用于发送和处理消息的发送消息一般使用
sendMessage()
方法,还有其他的一系列sendXXX
的方法,但最终都是调用了sendMessageAtTime
方法,除了sendMessageAtFrontOfQueue()
这个方法 而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage
方法中。 - Message Queue:
MessageQueue
是消息队列的意思,它主要用于存放所有通过Handler发送的消息,这部分的消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue
对象。 - Looper:每个线程通过Handler发送的消息都保存在,
MessageQueue
中,Looper
通过调用loop()
的方法,就会进入到一个无限循环当中,然后每当发现Message Queue
中存在一条消息,就会将它取出,并传递到Handler的handleMessage()
方法中。每个线程中只会有一个Looper
对象。
ThreadLocal:MessageQueue
对象,和Looper
对象在每个线程中都只会有一个对象,怎么能保证它只有一个对象,就通过ThreadLocal
来保存。Thread Local
是一个线程内部的数据存储类,通过它可以在指定线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储到数据,对于其他线程来说则无法获取到数据。
2.Handler连环泡之 说说 Looper 死循环为什么不会导致应用卡死?
线程默认没有
Looper
的,如果需要使用Handler就必须为线程创建Looper
。我们经常提到的主线程,也叫UI线程,它就是ActivityThread
,ActivityThread
被创建时就会初始化Looper
,这也是在主线程中默认可以使用Handler的原因。
首先我们看一段代码
new Thread(new Runnable() {
@Override
public void run() {
Log.e("qdx", "step 0 ");
Looper.prepare();
Toast.makeText(MainActivity.this, "run on Thread", Toast.LENGTH_SHORT).show();
Log.e("qdx", "step 1 ");
Looper.loop();
Log.e("qdx", "step 2 ");
}
}).start();
我们知道Looper.loop()
;里面维护了一个死循环方法,所以按照理论,上述代码执行的应该是 step 0 –>step 1 也就是说循环在Looper.prepare()
;与Looper.loop()
;之间。
在子线程中,如果手动为其创建了
Looper
,那么在所有的事情完成以后应该调用quit
方法来终止消息循环,否则这个子线程就会一直处于等待(阻塞)状态,而如果退出Looper
以后,这个线程就会立刻(执行所有方法并)终止,因此建议不需要的时候终止Looper
。
执行结果也正如我们所说,这时候如果了解了ActivityThread
,并且在main
方法中我们会看到主线程也是通过Looper
方式来维持一个消息循环
public static void main(String[] args) {
Looper.prepareMainLooper();//创建Looper和MessageQueue对象,用于处理主线程的消息
ActivityThread thread = new ActivityThread();
thread.attach(false);//建立Binder通道 (创建新线程)
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
//如果能执行下面方法,说明应用崩溃或者是退出了...
throw new RuntimeException("Main thread loop unexpectedly exited");
}
那么回到我们的问题上,这个死循环会不会导致应用卡死,即使不会的话,它会慢慢的消耗越来越多的资源吗?
对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。而对于主线程,我们是绝不希望会被运行一段时间,自己就退出,那么如何保证能一直存活呢?简单做法就是可执行代码是能一直执行下去的,死循环便能保证不会被退出,例如,binder线程也是采用死循环的方法,通过循环方式不同与Binder驱动进行读写操作,当然并非简单地死循环,无消息时会休眠。但这里可能又引发了另一个问题,既然是死循环又如何去处理其他事务呢?通过创建新线程的方式。真正会卡死主线程的操作是在回调方法
onCreate/onStart/onResume
等操作时间过长,会导致掉帧,甚至发生ANR,looper.loop
本身不会导致应用卡死。
主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到
Linux pipe/epoll
机制,简单说就是在主线程的MessageQueue
没有消息时,便阻塞在loop
的queue.next()
中的nativePollOnce()
方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe
管道写端写入数据来唤醒主线程工作。这里采用的epoll
机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。 Gityuan–Handler(Native层)
3.Handler连环泡之 说说 Looper 死循环为什么不会导致应用卡死?
事实上,会在进入死循环之前便创建了新binder线程,在代码
ActivityThread.main()
中:
public static void main(String[] args) {
//创建Looper和MessageQueue对象,用于处理主线程的消息
Looper.prepareMainLooper();
//创建ActivityThread对象
ActivityThread thread = new ActivityThread();
//建立Binder通道 (创建新线程)
thread.attach(false);
Looper.loop(); //消息循环运行
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Activity
的生命周期都是依靠主线程的Looper.loop
,当收到不同Message
时则采用相应措施:一旦退出消息循环,那么你的程序也就可以退出了。 从消息队列中取消息可能会阻塞,取到消息会做出相应的处理。如果某个消息处理时间过长,就可能会影响UI线程的刷新速率,造成卡顿的现象。
thread.attach(false)
方法函数中便会创建一个Binder线程(具体是指ApplicationThread
,Binder的服务端,用于接收系统服务AMS发送来的事件),该Binder线程通过Handler将Message
发送给主线程。「Activity 启动过程」
比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()
等方法;
再比如收到msg=H.PAUSE_ACTIVITY
,则调用ActivityThread.handlePauseActivity()
方法,最终会执行Activity.onPause()
等方法。
事件传递机制
在Android开发中,事件分发机制是一块Android比较重要的知识体系,了解并熟悉整套的分发机制有助于更好的分析各种点击滑动失效问题,更好去扩展控件的事件功能和开发自定义控件,同时事件分发机制也是Android面试必问考点之一,如果你能把下面的一些事件分发图当场画出来肯定加分不少。
废话不多说,总结一句:面试时事件分发机制很重要。
1.1 Android 事件原理和 事件分发流
先弄清楚Android 事件原理后 我们再来看怎么面试
关于Android 事件分发机制网上的博文很多,但是很多都是写个Demo然后贴一下输出的Log或者拿源码分析,然后一堆的注释和说明,如果用心的去看肯定是收获不少但是确实很难把整个流程说清和记住。曾经也是拼命想记住整个流程,但是一段时间又忘了,最后觉得分析这种问题和事件流的走向,一张图来解释和说明会清晰很多,下面我根据画的一张事件分发流程图,说明的事件从用户点击之后,在不同函数不同返回值的情况的最终走向。
注:
- 仔细看的话,图分为3层,从上往下依次是
Activity
、ViewGroup
、View
- 事件从左上角那个白色箭头开始,由
Activity
的dispatchTouchEvent
做分发- 箭头的上面字代表方法返回值,(
return true
、return false
、return super.xxxxx()
,super
的意思是调用父类实现。dispatchTouchEvent
和onTouchEvent
的框里有个**【true---->消费】**的字,表示的意思是如果方法返回true,那么代表事件就此消费,不会继续往别的地方传了,事件终止。- 目前所有的图的事件是针对
ACTION_DOWN
的,对于ACTION_MOVE和ACTION_UP
我们最后做分析。- 之前图中的
Activity
的dispatchTouchEvent
有误(图已修复),只有return super.dispatchTouchEvent(ev)
才是往下走,返回true
或者false
事件就被消费了(终止传递)。
仔细看整个图,我们得出事件流 走向的几个结论(希望读者专心的看下图 1,多看几遍,脑子有比较清晰的概念。) 1、如果事件不被中断,整个事件流向是一个类U型图,我们来看下这张图,可能更能理解U型图的意思。
所以如果我们没有对控件里面的方法进行重写或更改返回值,而直接用super
调用父类的默认实现,那么整个事件流向应该是从Activity---->ViewGroup—>View
从上往下调用dispatchTouchEvent
方法,一直到叶子节点(View)的时候,再由View—>ViewGroup—>Activity
从下往上调用onTouchEvent
方法。
2、dispatchTouchEvent
和 onTouchEvent
一旦return true
,事件就停止传递了(到达终点)(没有谁能再收到这个事件)。看下图中只要return true
事件就没再继续传下去了,对于return true
我们经常说事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能再收到这个事件了。
3、dispatchTouchEvent
和 onTouchEvent return false
的时候事件都回传给父控件的onTouchEvent
处理。
看上图深蓝色的线,对于返回false
的情况,事件都是传给父控件onTouchEvent
处理。
- 对于
dispatchTouchEvent
返回false
的含义应该是:事件停止往子View
传递和分发同时开始往父控件回溯(父控件的onTouchEvent
开始从下往上回传直到某个onTouchEvent return true
),事件分发机制就像递归,return false
的意义就是递归停止然后开始回溯。
- 对于
onTouchEvent return false
就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。
4、dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent ViewGroup
和View
的这些方法的默认实现就是会让整个事件安装U型完整走完,所以 return super.xxxxxx()
就会让事件依照U型的方向的完整走完整个事件流动路径),中间不做任何改动,不回溯、不终止,每个环节都走到。
所以如果看到方法return super.xxxxx()
那么事件的下一个流向就是走U型下一个目标,稍微记住上面这张图,你就能很快判断出下一个走向是哪个控件的哪个函数。
5、onInterceptTouchEvent
的作用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O41055AD-1678365727254)(https://upload-images.jianshu.io/upload_images/23851953-9f65a8d72ef34aa6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]
Intercept
的意思就拦截,每个ViewGroup
每次在做分发的时候,问一问拦截器要不要拦截(也就是问问自己这个事件要不要自己来处理)如果要自己处理那就在onInterceptTouchEvent
方法中 return true
就会交给自己的onTouchEvent
的处理,如果不拦截就是继续往子控件往下传。**默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent
拦截器return super.onInterceptTouchEvent()
和return false
是一样的,是不会拦截的,事件会继续往子View
的dispatchTouchEvent
传递。
6、ViewGroup
和View
的dispatchTouchEvent
方法返回super.dispatchTouchEvent()
的时候事件流走向。
首先看下ViewGroup
的dispatchTouchEvent
,之前说的return true
是终结传递。return false
是回溯到父View的onTouchEvent
,
然后ViewGroup
怎样通过dispatchTouchEvent
方法能把事件分发到自己的onTouchEvent
处理呢,return true
和false
都不行,那么只能通过Interceptor
把事件拦截下来给自己的onTouchEvent
,所以ViewGroup dispatchTouchEvent
方法的super
默认实现就是去调用onInterceptTouchEvent
,
记住这一点。
那么对于View
的dispatchTouchEvent return super.dispatchTouchEvent()
的时候呢事件会传到哪里呢,很遗憾View没有拦截器。但是同样的道理return true
是终结。return false
是回溯会父类的onTouchEvent
,怎样把事件分发给自己的onTouchEvent
处理呢,那只能return super.dispatchTouchEvent
,View类的dispatchTouchEvent()
方法默认实现就是能帮你调用View自己的onTouchEvent
方法的
1.2 如何面试,以及面试官会出现什么考点
1.2.1、为什么会有事件分发机制?
我们知道,android的布局结构是树形结构,这就会导致一些View可能会重叠在一起,当我们手指点击的地方在很多个布局范围之内,也就是说此时有好多个布局可以响应我们的点击事件,这个时候该让哪个view来响应我们的点击事件呢?这就是事件分发机制存在的意义。
1.2.2、ViewGroup的事件分发涉及到哪些过程和方法?
-
public boolean dispatchTouchEvent(MotionEvent ev)
是事件分发机制中的核心,所有的事件调度都归它管 用来进行事件的分发,如果事件能够传递给当前View,那么此方法一定会被调用 -
public boolean onInterceptTouchEvent(MotionEvent ev)
在dispatchTouchEvent
中调用,用来判断是否拦截某个事件,返回结果表示是否拦截当前事件 -
public boolean onTouchEvent(MotionEvent event)
在dispatchTouchEvent
中调用,用来处理点击事件,返回结果表示是否消耗当前事件
1.2.3、View中为什么会有dispatchTouchEvent方法,它存在的意义是什么?
我们知道View可以注册很多监听事件(下文有详细),比如,触摸事件,单击事件,长按事件等,而且view也有自己的onTouchEvent
方法,那么这么多事件应该由谁来调度管理呢?这就是是View中dispatchTouchEvent
方法存在的意义。
1.2.4、View中为什么没有onInterceptTouchEvent
事件拦截方法?
View最为事件传递的最末端,要么消费掉事件,要么不处理进行回传,根本没必要进行事件拦截
1.2.5、用伪代码表示ViewGroup的事件分发过程并解释?
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
-
对于一个
ViewGroup
来说,点击事件产生后,首先会传递给它,这时她的dispatchTouchEvent
会被调用,如果这个ViewGroup的onInterceptTouchEvent
-
方法返回true表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的
onTouchEvent
就会被调用;如果这个这个ViewGroup的onInterceptTouchEvent
-
方法返回false就表示它不拦截当前事件,这时事件就会传递给子元素,接着子元素的
dispatchTouchEvent
方法就会被调用,如此反复直到事件最终被处理。
1.2.6、简述事件传递的流程
- 事件都是从
Activity.dispatchTouchEvent()
开始传递 - 一个事件发生后,首先传递给Activity,然后一层一层往下传,从上往下调用
dispatchTouchEvent
方法传递事件:activity --> ~~ --> ViewGroup --> View
- 如果事件传递给最下层的View还没有被消费,就会按照反方向回传给Activity,从下往上调用
onTouchEvent
方法,最后会到Activity的onTouchEvent()
函数,如果Activity也没有消费处理事件,这个事件就会被抛弃:View --> ViewGroup --> ~~ --> Activity
dispatchTouchEvent
方法用于事件的分发,Android中所有的事件都必须经过这个方法的分发,然后决定是自身消费当前事件还是继续往下分发给子控件处理。返回true表示不继续分发,事件没有被消费。返回false则继续往下分发,如果是ViewGroup则分发给onInterceptTouchEvent
进行判断是否拦截该事件。onTouchEvent
方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。onInterceptTouchEvent
是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent
进行处理。返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能还有子View,而在Android中View中是不能再包含子View的- 上层View既可以直接拦截该事件,自己处理,也可以先询问(分发给)子View,如果子View需要就交给子View处理,如果子View不需要还能继续交给上层View处理。既保证了事件的有序性,又非常的灵活。
- 事件由父View传递给子View,ViewGroup可以通过
onInterceptTouchEvent()
方法对事件拦截,停止其向子view传递 - 如果View没有对
ACTION_DOWN
进行消费,之后的其他事件不会传递过来,也就是说ACTION_DOWN
必须返回true,之后的事件才会传递进来
1.2.7、ViewGroup 和 View 同时注册了事件监听器(onClick等),哪个会执行?
事件优先给View,会被View消费掉,ViewGroup 不会响应。
1.2.8、当俩个或多个View重叠时,事件该如何分配?
当 View 重叠时,一般会分配给显示在最上面的 View,也就是后加载的View。
1.2.9、dispatchTouchEvent每次都会被调用吗?
是的,onInterceptTouchEvent
则不会。
1.2.10、一旦有事件传递给view,view的onTouchEvent一定会被调用吗?
View没有onInterceptTouchEvent
方法,一旦有事件传递给它,他的onTouchEvent
就一定会被调用。
1.2.11、ViewGroup 默认拦截事件吗?
ViewGroup默认不拦截任何事件;看源码可以知道ViewGroup的onInterceptTouchEvent
方法中只有一行代码:return false;
1.2.12、事件分为几个步骤?
down事件开头,up事件结尾,中间可能会有数目不定的move事件。
1.3 View事件的优先级会考哪些内容
1.3.1、基于监听的事件分发有哪些?怎么来设置监听?
我们常用的setOnClickListener
、OnLongClickListener
、setOnTouchListener
等都是基于监听的事件处理。 设置监听可以用如下几种方式:
- 匿名内部类:
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
- 内部类:
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
- 外部类:
view.setOnClickListener(new MyClickListener());
public class MyClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
}
}
- Activity实现
OnClickLister
接口的方式
public class TestViewActivity extends AppCompatActivity implements View.OnClickListener {
MyView view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_view);
view = (MyView) findViewById(R.id.view);
view.setOnClickListener(this);
}
@Override
public void onClick(View v) {
}
}
- 在xml中绑定的方式:
public class TestViewActivity extends AppCompatActivity{
MyView view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_view);
view = (MyView) findViewById(R.id.view);
}
public void MyClick(View view){
}
}
<com.art.chapter_3.MyView
android:id="@+id/view"
android:layout_width="100dip"
android:layout_height="100dip"
android:background="@color/colorPrimaryDark"
android:onClick="MyClick"/>
1.3.2、view的onTouchEvent,OnClickListerner和OnTouchListener的onTouch方法 三者优先级如何?
代码验证:
自定义view:
public class MyView extends View {
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("--------","MyView onTouchEvent "+MyAction.getActionType(event));
return super.onTouchEvent(event);
}
}
监听:
yelloe.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.i("--------", "touch yelloe " + MyAction.getActionType(motionEvent));
return false;
}
});
yelloe.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.i("--------", "click yelloe ");
}
});
输出结果: 插图 优先级高低
优先级高低:
onTouchListener >>> onTouchEvent >>> setOnLongClickListener >>> OnClickListerner
1.3.3、如图有三個嵌套的控件,结构如下,其中黄色部分是一个继承于View的控件,绿色和红色都是继承于LinearLayout的控件: 插图:
代码简单如下:
public class MyView extends View {
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("--------","MyView onTouchEvent "+MyAction.getActionType(event));
return super.onTouchEvent(event);
}
}
public class MyLinearLayoutRed extends LinearLayout {
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("--------","MyLinearLayoutRed onTouchEvent "+MyAction.getActionType(event));
return super.onTouchEvent(event);
}
}
public class MyLinearLayoutGreen extends LinearLayout {
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("--------","MyLinearLayoutRed onTouchEvent "+MyAction.getActionType(event));
return super.onTouchEvent(event);
}
}
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<com.example.administrator.myviewevent.MyLinearLayoutRed
android:id="@+id/red"
android:layout_width="300dip"
android:layout_height="300dip"
android:background="@color/red">
<com.example.administrator.myviewevent.MyLinearLayoutGreen
android:id="@+id/green"
android:layout_width="200dip"
android:layout_height="200dip"
android:background="@color/green">
<com.example.administrator.myviewevent.MyView
android:id="@+id/yellow"
android:layout_width="130dip"
android:layout_height="130dip"
android:background="@color/yellow" />
</com.example.administrator.myviewevent.MyLinearLayoutGreen>
</com.example.administrator.myviewevent.MyLinearLayoutRed>
</FrameLayout>
问题一:如果不在onTouchEvent
方法中做任何处理,只是Log输出每一层的Touch事件类型,现在用手指按下在黄色区域并移动后抬起.请问Log输出的结果是什么?
答:
- I/--------: MyView onTouchEvent ACTION_DOWN…
- I/--------: MyLinearLayoutGreen onTouchEvent ACTION_DOWN…
- I/--------: MyLinearLayoutRed onTouchEvent ACTION_DOWN…
问题二:如果不在onTouchEvent
方法和setOnTouchListener
的onTouch方法中做任何处理,只是Log输出每一层的Touch事件类型,现在用手指按下在黄色区域并移动后抬起.请问Log输出的结果是什么? 在Activity中增加setOnTouchListener
监听
yelloe.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.i("--------", "touch yelloe " + MyAction.getActionType(motionEvent));
return false;
}
});
green.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.i("--------", "touch green " + MyAction.getActionType(motionEvent));
return false;
}
});
red.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.i("--------", "touch red " + MyAction.getActionType(motionEvent));
return false;
}
});
答:
I/--------: touch yelloe ACTION_DOWN…
I/--------: MyView onTouchEvent ACTION_DOWN…
I/--------: touch green ACTION_DOWN… I/--------: MyLinearLayoutGreen onTouchEvent ACTION_DOWN…
I/--------: touch red ACTION_DOWN…
I/--------: MyLinearLayoutRed onTouchEvent ACTION_DOWN…
1.3.4、setOnTouchListener中onTouch的返回值表示什么意思?
onTouch
方法返回true表示事件被消耗掉了,不会继续传递了,此时获取不到到OnClick
和onLongClick
事件;onTouch
方法返回false表示事件没有被消耗,可以继续传递,此时,可以获取到OnClick
和onLongClick
事件; 同理 onTouchEvent
和 setOnLongClickListener
方法中的返回值表示的意义一样;
1.3.5、setOnLongClickListener的onLongClick的返回值表示什么?
返回false,长按的话会同时执行onLongClick
和onClick
;如果setOnLongClickListener
返回true,表示事件被消耗,不会继续传递,只执行longClick
;
1.3.6、onTouch和onTouchEvent的异同?
-
onTouch
方法是View的OnTouchListener
接口中定义的方法。当一个View绑定了OnTouchLister
后,当有touch事件触发时,就会调用onTouch
方法。(当把手放到View上后,onTouch
方法被一遍一遍地被调用) -
onTouchEvent
方法是override
的Activity
的方法。重新了Activity
的onTouchEvent
方法后,当屏幕有touch
事件时,此方法就会被调用。 -
onTouch
优先于onTouchEvent
执行,如果在onTouch
方法中通过返回true将事件消费掉,onTouchEvent
将不会再执行。 -
相同点是它们都是在在View的dispatchTouchEvent中调用的;
1.3.7、点击事件的传递过程?
Activity-Window-View
。 从上到下依次传递,当然了如果你最低的那个view onTouchEvent
返回false
那就说明他不想处理 那就再往上抛,都不处理的话最终就还是让Activity
自己处理了。
1.3.8、如果某个view 处理事件的时候 没有消耗down事件 会有什么结果?
假如一个view,在down事件来的时候 他的onTouchEvent
返回false
, 那么这个down事件 所属的事件序列 就是他后续的move 和up 都不会给他处理了,全部都给他的父view处理。
1.3.9、如果view 不消耗move或者up事件 会有什么结果?
那这个事件所属的事件序列就消失了,父view也不会处理的,最终都给activity 去处理了。
1.3.10、enable是否影响view的onTouchEvent返回值?
不影响,只要clickable
和longClickable
有一个为真,那么onTouchEvent
就返回true。
Android 面试题锦:https://qr18.cn/CKV8OZ