Framework源码面试——Handler与事件传递机制面试集合
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() {@Overridepublic 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() {@Overridepublic void onClick(View v) {}});
- 内部类:
view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {}});
- 外部类:
view.setOnClickListener(new MyClickListener());public class MyClickListener implements View.OnClickListener {@Overridepublic void onClick(View v) {}}
- Activity实现
OnClickLister
接口的方式
public class TestViewActivity extends AppCompatActivity implements View.OnClickListener {MyView view;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_test_view);view = (MyView) findViewById(R.id.view);view.setOnClickListener(this);}@Overridepublic void onClick(View v) {}}
- 在xml中绑定的方式:
public class TestViewActivity extends AppCompatActivity{MyView view;@Overrideprotected 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.MyViewandroid: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 {@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.i("--------","MyView onTouchEvent "+MyAction.getActionType(event));return super.onTouchEvent(event);}
}
监听:
yelloe.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {Log.i("--------", "touch yelloe " + MyAction.getActionType(motionEvent));return false;}});yelloe.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Log.i("--------", "click yelloe ");}});
输出结果: 插图 优先级高低
优先级高低:
onTouchListener >>> onTouchEvent >>> setOnLongClickListener >>> OnClickListerner
1.3.3、如图有三個嵌套的控件,结构如下,其中黄色部分是一个继承于View的控件,绿色和红色都是继承于LinearLayout的控件: 插图:
代码简单如下:
public class MyView extends View {@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.i("--------","MyView onTouchEvent "+MyAction.getActionType(event));return super.onTouchEvent(event);}
}public class MyLinearLayoutRed extends LinearLayout {@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.i("--------","MyLinearLayoutRed onTouchEvent "+MyAction.getActionType(event));return super.onTouchEvent(event);}
}public class MyLinearLayoutGreen extends LinearLayout {@Overridepublic 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.MyLinearLayoutRedandroid:id="@+id/red"android:layout_width="300dip"android:layout_height="300dip"android:background="@color/red"><com.example.administrator.myviewevent.MyLinearLayoutGreenandroid:id="@+id/green"android:layout_width="200dip"android:layout_height="200dip"android:background="@color/green"><com.example.administrator.myviewevent.MyViewandroid: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() {@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {Log.i("--------", "touch yelloe " + MyAction.getActionType(motionEvent));return false;}});green.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View view, MotionEvent motionEvent) {Log.i("--------", "touch green " + MyAction.getActionType(motionEvent));return false;}});red.setOnTouchListener(new View.OnTouchListener() {@Overridepublic 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
相关文章:

Framework源码面试——Handler与事件传递机制面试集合
Handler面试题 Handler的作用: 当我们需要在子线程处理耗时的操作(例如访问网络,数据库的操作),而当耗时的操作完成后,需要更新UI,这就需要使用Handler来处理,因为子线程不能做更新…...

iOS开发-bugly符号表自动上传发布自动化shell
这里介绍的是通过build得到的app文件和dSYM文件来打包分发和符号表上传。 通过Archive方式打包和获得符号表的方式以后再说。 一:bugly工具jar包准备 bugly符号表工具下载地址:(下载完成后放入项目目录下,如不想加入git可通过gitIgnore忽略…...
MySQL OCP888题解046-哪些语句会被记录到binlog
文章目录1、原题1.1、英文原题1.2、中文翻译1.3、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3.1、知识点1:binlog_format选项3.2、知识点2:Performance Schema(性能模式)4、总结1、原题 1.1、英文原题 You enable binary logging on MySQL S…...

【前端学习】D5:CSS进阶
文章目录前言系列文章目录1 精灵图Sprites1.1 为什么需要精灵图?1.2 精灵图的使用2 字体图标iconfont2.1 字体图标的产生2.2 字体图标的优点2.3 字体文件格式2.4 字体图标的使用2.5 字体图标的引入2.6 字体图标的追加3 CSS三角3.1 普通三角3.2 案例4 CSS用户界面样式…...

【bioinfo】融合检测软件FusionMap分析流程和报告结果
文章目录写在前面FusionMap融合检测原理FusionMap与其他软比较FusionMap分析流程FusionMap结果文件说明FusionMap mono CUP设置图片来源: https://en.wikipedia.org/wiki/Fusion_gene写在前面 下面主要内容是关于RNA-seq数据分析融合,用到软件是FusionMap 【Fusion…...

C++基础了解-17-C++日期 时间
C日期 & 时间 一、C日期 & 时间 C 标准库没有提供所谓的日期类型。C 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C 程序中引用 头文件。 有四个与时间相关的类型:clock_t、time_t、size_t 和 …...

MOV压敏电阻的几种电路元件功能及不同优势讲解
压敏电阻,通常是电路为防护浪涌冲击电压而使用的一种电子元器件,相比其他的浪涌保护器来说,也有那么几个不一样的优势,那么,具体有哪些?以及关于它的作用,你都知道吗?以下优恩小编为…...

uniapp+uniCloud实战项目报修小程序开发
前言 本项目基于 uniapp uniCloud 云开发,简单易用,逻辑主要是云数据库的增删查改,页面大部分自写,部分使用uniUI, uView 组件库。大家可用于学习或者二次开发,有什么不懂的地方可联系 wechat:MrYe443。用…...

演唱会的火车票没了?Python实现12306查票以及zidong购票....
嗨害大家好!我是小熊猫~ 不知道大家抢到演唱会的门票没有呢? 不管抢到没有,火车票也是很重要的哇 24小时抢票不间断的那种喔~ ~ ~ 不然可就要走路去了喔~ 准备工作 环境 Python 3.8Pycharm 插件 谷歌浏览器驱动 模块 需要安装的第三方模块&am…...

Linux发行版本与发行版的简单的介绍
Linux linux下有很多发行的版本,或者称之为魔改版本。以下介绍一些常见的版本,以避免名词的混淆。 linux是提供了一个内核,就像是谷歌的内核一样,QQ浏览器就是使用的谷歌的内核,也算是一个发行版本。 Ubuntu&#x…...

前后端分离项目学习-vue+springboot 博客
前后端分离项目 文章总体分为2大部分,Java后端接口和vue前端页面 项目演示:www.markerhub.com:8084/blogs Java后端接口开发 1、前言 从零开始搭建一个项目骨架,最好选择合适,熟悉的技术,并且在未来易拓展…...

关于指针运算的一道题
目录 刚看到这道题的时候我也和大多数小白一样感到无从下手,但是在我写这篇博客的前几分钟开始我对这道题有了一点点的理解。所以我就想着趁热打铁,写一篇博客来记录一下我的想法。 题目如下: 画图: 逐一解答: 题一…...

【论文简述】Learning Optical Flow with Kernel Patch Attention(CVPR 2022)
一、论文简述 1. 第一作者:Ao Luo 2. 发表年份:2022 3. 发表期刊:CVPR 4. 关键词:光流、局部注意力、空间关联、上下文关联 5. 探索动机:现有方法主要将光流估计视为特征匹配任务,即学习在特征空间中将…...
Java学习-MySQL-列的数据类型
Java学习-MySQL-列的数据类型 数值 tinyint - 1个字节smallint - 2个字节mediumint - 3个字节int - 4个字节bigint - 8个字节float - 4个字节double - 8个字节decimal - 字符串形式的浮点数 字符串 char - 0~255varchar - 可变字符串 0~65535tinytext - 微型文本 2^8-1text…...

终端配色-Docker容器终端
20230309 - 0. 引言 平时使用SSH,通常都是使用securecrt来用,毕竟也算是之前windows下一种使用的工具,在mac下使用还算方便;进入终端后,可以通过调整配色来调整编程环境。平时经常使用屎黄色的那种配色,毕…...
SQL基础培训04-插入数据
知识点: 假设有订单表 CREATE TABLE SEOrder ( FID int identity(...

Apache HTTP Server <2.4.56 mod_proxy 模块存在请求走私漏洞(CVE-2023-25690)
漏洞描述 Apache HTTP Server是一款Web服务器。 该项目受影响版本存在请求走私漏洞。由于intro.xml中存在RewriteRule配置不当,当Apache启用mod_proxy且配置如 RewriteRule “^/here/(.*)” " http://example.com:8080/elsewhere?$1"; http://example.…...

SpringBoot 集成 elasticsearch 7.x和对应版本不一致异常信息处理
开源框架springboot框架中集成es。使用org.springframework.data.elasticsearch下的依赖,实现对elasticsearch的CURD,非常方便,但是springboot和elasticsearch版本对应很严格,对应版本不统一启动会报错。 文章目录开源框架Elasticsearch 7.x安装Elastics…...

求职季必看系列:Java如何高效面试?
先看看这些java高频的面试重点吧 以下是初级程序员面试经常问到的问题: ■ Spring的三大特性是什么? ■ Spring IOC和AOP 你是如何理解并且使用的? ■ 说一下ElasticSearch为什么查询的快?是如何存储的?在项目中…...

点云分割预研
文章目录激光雷达点云分割1.点云分割主流方案(模型角度)1.1 (a) 基于RGB-D图像1.2 (d) 基于点云1.3 (b) 基于投影图像1.4 (b) 基于投影图像 - SqueezeSeg/RangeNet1.4. 球映射2 点云分割主流方案(部署角度)3 点云分割常用指标4 点…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看
文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...