android传递view
A. 安卓编程问题。怎么让悬浮窗中的view不拦截触摸事件,并将触摸事件传递给手机桌面
可以这样。比如说你想传递给被遮住的View,那么遮住的View肯定会依附在某个Activity上,那就调用:
event.setLocation(event.getRawX(), event.getRawY());
((Activity) getContext()).dispatchTouchEvent(event);
前提是,你创建这个悬浮窗View的时候,是用的那个你想传递的Activity的Context来创建的。
B. Android Touch事件分发处理机制详解
Android应用的开发过程不可能不涉及到Touch事件的处理,简单地如设置OnClickListener、OnLongClickListener等监听器处理View的点击事件,复杂地如在自定义View中通过重写onTouchEvent来捕获用户交互事件以定制出各种效果,在使用的过程中或多或少会遇到一些奇怪的Bug,让你对Touch事件“从哪来,到哪去”产生迷之疑惑,经过多少次徘徊之后终于决定系统的分析下源码,本文就给大家分享下我的收获。
MotionEvent作为Touch事件的载体,采用时间片来管理Touch事件所有相关行为的数据,本文这样理解时间片这个概念:
通常MotionEvent会将触发当前事件的Pointer作为主要Pointer,其PointerIndex为0,而MotionEvent通过提供getX()这类不带index参数的接口以更方便的操作主要Pointer的数据。
了解了MotionEvent的组成结构之后,接下来就可以分析MotionEvent包含的事件类型了,MotionEvent通过getAction接口来获取事件Action,而Action中低8位地址存储的是事件类型(对于触摸事件来说,主要包括Down、Move、Up、Cancel、PointerDown、PointerUp),高8位地址存储的是PointerId(当事件类型为PointerDown、PointerUp时)。通常来说事件会以Down开始,以Up或Cancel结束,各事件所承担的角色以及各自的特点在分析事件分发与处理的过程时再详细说明。
另外,MotionEvent中的Flag需要说明一下:
本文仅分析Touch事件在Framework中java层的传递,因此从事件传递到Activity开始分析。当Touch事件传递给Activity时,会调用Activity.dispatchTouchEvent(MotionEvent),Activity会将事件传递给其Window进行处理,实际会调用PhoneWindow.superDispatchTouchEvent(MotionEvent),PhoneWindow会将该事件传递给Android中View层级前埋中的顶层View(即DecorView)进行处理:
在Window未设置Callback的情况下,会调用父类的dispatchTouchEvent,DecorView继承自FrameLayout,然后FrameLayout并未实现dispatchEvent,因此最终调用ViewGroup.dispatchTouchEvent,也就是Touch事件分发的核心逻辑所在,前文中提到MotionEvent中事件类型主要包括Down、Move、Up、Cancel、PointerDown、PointerUp,而dispatchTouchEvent根据事件的不同类型会做不同处理,因此这里分别进行分析:
Down事件处理
非异常情况下,Touch事件的事件周期总是以Down事件开始的,因此Down事件在整个事件分发逻辑中起关键作用,将决定了后续Move、Up及Cancel事件的处理主体,先看一张Down事件分发的流程图:
从流程图中可以看到,Down事件的分发逻辑主要目的在于寻找到能处理该Touch事件的View控件(该View为以当前ViewGroup为Root节点的View层级中的View,利用寻找到的View创建事件处理Target),整个处理逻辑主要包含以下渣悔陆几步:
Move、Up、Cancel事件处理
完成Down事件的分发逻辑后,就确定了该Down事件后续Move、Up及Cancel事件的处理主体(注意:这里并没有确定PointerDown事件的处理主体,关于PointerDown事件的分发逻辑稍后分析),先通过一张流程图来感受下Move、Up、Cancel事件的分发逻辑:
从流程图可以看出,对于Move、Up、Cancel事件的分发步骤如如顷下:
PointerDown事件处理
PointerDown事件是在支持多Pointer(调用将FLAG_SPLIT_MOTION_EVENTS置位)的环境下,当有新的Pointer按下时产生的,该事件处理的特殊性在于会重新遍历View层级,寻找可以处理新Pointer事件的Target,具体流程参考Down事件的分发逻辑;遍历结束若仍没有找到处理该事件的Target,则会将新Pointer的处理权设置给已有Target中最早被添加的Target。完成Target的寻找之后,会将该事件通过dispatchTransformedTouchEvent传递至所有已有Target进行处理,可以通过下面流程图,对PointerDown事件的处理有一个更全局的认识:
PointerUp事件处理
相对于Up事件来说,对于PointerUp事件的处理区别在于当传递至所有已有Target结束之后并不能标记以Down事件起始的整个事件周期结束,仅能标记其关联Pointer(以PointerDown事件起始)的事件周期结束,因此不会清除所有状态,而仅会从已有Target中移除掉与该Pointer相关的部分。
onInterceptTouchEvent
在ViewGroup进行事件分发的过程中,会调用该函数来确定是否需要拦截事件,当该函数返回true时该事件将会被拦截,即不会进行正常的View层级传递,而是直接由该ViewGroup来处理,而拦截后的操作需要根据拦截事件的类型不同而不同:
dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits)
在将事件传递给Target进行处理之前会调用该函数对MotionEvent进行处理:
MotionEvent.split(int idBits)
判断一个View控件是否消费一个事件,是由View.dispatchEvent的返回值来决定的,而View.dispatchEvent用于寻找事件的最终消费者,话不多说,还是通过一张流程图来个直观感受:
从流程图中可以看出,View会根据ouch事件对Scroll状态进行调整,并寻找该事件的最终处理器:
View.dispatchEvent将向其直接ViewGroup返回是否消费掉该事件,返回值将决定上级ViewGroup是否需要继续询问其他子View是否需要消费该事件。这就是View中分发事件的逻辑,真是简单粗暴!
从View.dispatchEvent的分析中可以发现当未对View设置mTouchListener或mTouchListener未消费掉该事件时,Touch事件最终将由View.onTouchEvent来决定是否消费,自定义View可以重写该方法实现自身的逻辑,此处仅分析View中的通用处理逻辑:
从上述分析可以很开心地发现熟悉的onClick及onLongClick事件的产生逻辑,若是之前没看过类似的文章,应该会有原来如此的感觉吧,哈哈~~
至此,Touch事件的分发与处理流程算是走通了,个人看完整个源码之后有种豁然开朗的感觉,能很清晰的分析向“为什么事件有时候传到某个View有时候却不传?”、“有时候只传前面几个事件后面却不传了?”等问题,也希望本文的分析能让你更清晰地感知Android中Touch事件的传递流程,如果发现文中有何错误,希望不吝赐教!
C. android 怎样把view事件传递出去
可以直接调用dispatchTouchEvent、onTouchEvent等类似的函数分发事件
D. android开发,请问下两个Activity之间如何传递textView上的值
在startActivity的会后,通过Intent中传值,比如
//启动B
Intentbintent=newIntent(A.this,B.class);
//设置bintent的Bundle的一个值
Stringbsay=textView.getText().toString();//获取textview的值
bintent.putExtra("listenB",bsay)
startActivity(bintent,0);
E. Android 点击事件传递(简单版)
Android点击事件的传递应该是属于“必考题”这一类型的了, 当然了解这一块对于平常开发也非常的有用。例如:滚动事件冲突的解决、自定义控件点击效果等。本篇就简单的来捋一下 Android的点击事件
Demo很简单,就是最外层是一个Activity 然后自定了一个ViewGroup 和 View
不可点击
通过Log可以看出 ACTION_DOWN 事件是从 Activity->ViewGroup->View->ViewGroup->Activity 中间没有事件消费 所以又返回到了Activity。 由于ACTION_DOWN事件没有消费 所以ACTION_UP不会再给你消费的机会
可点击 其他没有任何操作
根据Log可以看出事件是从最外层传入最底层,当最底层的View给消费后将不再返回
InterceptTouchEvent ACTION_DOWN返回为false,ACTION_UP 返回为true ,则View的 dispatchTouchEvent 和 TouchEvent会调用 ACTION_CANCEl
dispatchTouchEvent中 ACTION_DOWN 返回为false 其他返回为true
附上Demo地址: https://github.com/i-show/demos-AndroidView
F. android activity怎么把数据传递到view中
在Activity中把你处理好的察旅数据,放到intent对象中,如:intent.putExtra("bianliangkey_01",bianliang_01str);。在另一个Activity中,锋野获取view对象,再由该view获得银没喊其他Activity传来的数据,如:bianliang_01str=intent.getStringExtra("bianliangkey_01");
G. android自定义view的参数传递问题
android自定义view的参数传递,自定义View细分一下,分为两种
1) 自定义ViewGroup
2) 自定义View
其实ViewGroup最终还是继承之View,当然它内部做了许多操作;继承之ViewGroup的View我们一般称之为容器,而今天我们不讲这方面,后续有机会再讲。
来看看自定义View 需要掌握的几点,主要就是两点
一、重写 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {}方法。
二、重写 protected void onDraw(Canvas canvas) {}方法
空讲理论很难理解,还得用例子来说明, Android 微信6.1 tab栏图标和字体颜色渐变的实现 的博客,里面tab的每个item就是通过自定义View来实现的,那么接下来就通过此例子来说明问题。
把View理解为一张白纸,而自定义View就是在这张白纸上画上我们自己绘制的图案,可以在绘制任何图案,也可以在白纸的任何位置绘制,那么问题来了,白纸哪里来?图案哪里来?位置如何计算?
a)白纸好说,只要继承之View,在onDraw(Canvas canvas)中的canvas就是平常规裁军所说的白纸
/**
* Created by moon.zhong on 2015/2/13.
*/
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
H. 关于Android WebView的那些事
[TOC]
Webkit是一个开源浏览器项目,其中,对Android开发者来说,或多或少的都有些接触。 在应用层来看,最经常使用无非这么几个类:WebView(Android中最为复杂,也是最为简单的一个View,继承自AbsoluteLayout),WebViewClient、WebChromeClient(作为回调控制类)、WebSettings(进行设置项的配置)等;Webkit内部包含了网络请求、页面渲染、Js引擎等等。在Android4.4之前的版本中,系统使用的是Webkit内核,其后,切换到Google的Chromium内核。本文主要介绍的是在Android中,如何使用Webkit进行H5页面的展现,以及常见问题的分析手段。
下面的内容抄自网络 & 乱七八糟的地方,简单了解一下。
<b><i>前面都是吹牛逼的信息,如何使用Webkit来更好的搬砖? 且听如下分解</i></b>
XML布局中丢一个 <WebView> 标签,然后再 Activity 或者 Fragment 中 findViewById ,进而 loadUrl ,一般也没人这么简单的用,除非写Demo。很简单,它就是一个Layout,提供了一个调用加载页面的接口,不写范例了,能看到这篇文章的都看过Google的API说明。
主要涉及到WebView和WebSettings两个类。
例如:
其实就是WebView的父类ViewGroup和View的方法,不多说了。不过需要注意的是,不是所有的View或ViewGroup的方法对WebView都生效。
列举几类常用的,几乎所有App的 WebView 都会设置的属性:
</br>
如何处理页面跳转以及特殊 Scheme
这个回调可以说是最容易出问题的一个回调,表示什么? 字面意思,让你重写这个URL 的loading,比如点击html打电话的一个 <a href=“tel:110”> 标签,作为一个有节操、有责任心的浏览器,你需要处理 H5常用的几个Scheme :
除此之外,还有各个应用自定义的scheme ,举个例子,支付宝的支付Scheme : alipay: 。 这里的返回值,就代表你有没有能力处理这个url,没有的话Webkit就默认处理了。
需要注意的是,这个回调的触发的绝大多数情况是点击页面的 <a href="xxxx"> a标签,在Android中 loadUrl("http://www..com") ,是不会回调的,为什么不会回调,各位自行理解吧。
超链接 <a> 标签怎么写: 点我
特别说下窗口常见的两种打开方式:
针对单页模式的WebView框架(所有的html窗口均使用同一个WebView实例),不需要关注target的。
如果作为一个成熟的浏览器框架的话,是需要支持Html、JavaScript使用新窗口打开页面,需要实现如下回调:
还有一个相关设置项: WebSettings.
此时,系统将不会再回调 shouldOverrideUrlLoading 。新窗口逻辑的具体实现机制,可以参考系统browser实现逻辑。
<b> 这里有个坑 </b>
Android 4.4版本 ,如果实现了onCreateWindow,也就是说页面 <a> 标签是这么写的: <a href="http://www..com" target="_blank"> ,点击此链接打开的新WebView窗口,此窗口中的url点击,是不会触发 shouldOverrideUrlLoading 。 这是刚替换成Chrominum内核出的一个bug。本人并没在新版本上验证是否已经修复。
另外,根据不同的Rom,底层实现是不一样的,有的ROM会帮你处理各种调起scheme,也就是startActivity,有的ROM点一个url,就会抛一个intent出来,让用户选择系统浏览器进行加载。
系统默认,提供了一个接口:
有什么安全隐患呢?
戳这里
如果不知道Js怎么写, 请戳我
用PC的截图意思一下,看出区别了吧。 这里确定、取消点击以后就得调用 JsResult、JsPromptResult 的 confirm或者cancel。
因为安全问题,大一些的App Native与Js通信都不再用 WebView.addJavascriptInterface(Object) 了,都改用JsPrompt,因为JsPrompt中有message、有JsPromptResult可以返回给Js一些信息,所以桥选中了JsPrompt,另一个备选方案是JsConsole。
大体有这么几种方式进行传递
具体方案实现时,多方面考虑使用何种方式。
还有一个比较牛逼的
系统源码中均有方法注释,怎么用自己看吧。
那么问题来了
查了下,只有这两个相关的:
WebBackForwardList BackForwardList()
void clearHistory()
系统提供的关于历史记录的操作并不多,因为,不支持单条删除啊,啊啊啊!
WebViewClient中,还有一个相关callback,当系统更新历史记录时回调:
void doUpdateVisitedHistory(WebView view, String url, boolean isReload)
<b>相关问题分析法:历史栈回退错误的定位</b>
绝大多数回退错误是由于接口调用、回调中逻辑执行时序错误。
定位方法:利用 BackForwardList , doUpdateVisitedHistory 两个接口在 loadUrl、onPageStart、onPageFinish 以及逻辑相关的地方调用,打log,查看历史栈,这里注意下由于loarl是异步的,需要考虑是否加延迟等等保证调用时机的准确。
本人曾经遇到一个问题:在WebChromeClient中的 JsPrompt回调中,直接进行WebView.goBack操作,结果发现WebView确实回退到上一个页面,但是BackFowardList当前页面的index未更新的问题,具体见另一个篇blog。
网上有很多关于WebView内存泄露的讨论,据传,老版本的WebView在展示大量图片的时候,即使 WebView.destory() WebView=null ,也不会销毁。
在新版本上,实际测试结果:compileSDKVersion 23 不会泄露。
一般,我们如何销毁WebView比较保险?
这个问题好大。。。
暂时不介绍,另起blog进行说明。
解决方案:
实现回调 void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
首先,提几个需要注意的点:
个人归纳总结几点:
step1 进入开发者模式,勾选“显示布局边界”;
step 2,回到你想查看的界面; step 3 假如内容区只有一层基本就是H5 WebView的,多个层级,就是Native。
看到左右图的差异了吧。
还有另一种方法,RD屌丝们看这里,特别说明,这种方法不太适合浏览器。 (自有内核,可能会不准确)
好了,就介绍到这里,零零散散的几年前写的文章,第一篇blog,如有不对的地方,还恳请大家指正。
I. Android View 事件分发机制
Android 事件机制包含系统启动流程、输入管理(InputManager)、系统服务和 UI 的通信(WindowManagerService + ViewRootImpl + Window)、事件分发等一系列的环节。
Android 系统中将输入事件定义为 InputEvent,根据输入事件的类型又分为了 KeyEvent(键盘事件) 和 MotionEvent(屏幕触摸事件)。这些事件统一由系统输入管理器 InputManager 进行分发。
在系统启动的时候,SystemServer 会启动 WindowManagerService,WMS 在启动的时候通过 InputManager 来负责监控键盘消息。
InputManager 负责从硬件接收输入事件,并将事件通过 ViewRootImpl 分发给当前激活的窗口处理,进而分发给 View。
Window 和 InputManagerService 之间通过 InputChannel 来通信,底层通过 socket 进行通信。
Android Touch 事件的基础知识:
KeyEvent 对应了键盘的输入事件;MotionEvent 就是手势事件,鼠标、笔、手指、轨迹球等相关输入设备的事件都属于 MotionEvent。
InputEvent 统一由 InputManager 进行分发,负责与硬件通信并接收输入事件。
system_server 进程启动时会创建 InputManagerService 服务。
system_server 进程启动时同时会启动 WMS,WMS 在启动的时候就会通过 IMS 启动 InputManager 来监控键盘消息。
App 端与服务端建立了双向通信之后,InputManager 就能够将产生的输入事件从底层硬件分发过来,Android 提供了 InputEventReceiver 类,以接收分发这些消息:
Window 和 IMS 之间通过 InputChannel 通信。InputChannel 是一个 pipe,底层通过 socket 进行通信。在 ViewRootImpl.setView() 过程中注册 InputChannel。
Android 事件传递机制是 先分发再处理 ,先由外部的 View 接收,然后依次传递给其内层的 View,再从最内层 View 反向依次向外层传递。
三个方法的关系如下:
分发事件:
应用了树的 深度优先搜索算法 (Depth-First-Search,简称 DFS 算法),每个 ViewGroup 都持有一个 mFirstTouchTarget, 当接收到 ACTION_DOWN 时,通过递归遍历找到 View 树中真正对事件进行消费的 Child,并保存在 mFirstTouchTarget 属性中,依此类推组成一个完整的分发链。在这之后,当接收到同一事件序列的其它事件如 ACTION_MOVE、ACTION_UP 时,则会跳过递归流程,将事件直接分发给下一级的 Child。
ViewGroup 分发事件的主要的任务是找一个 Target,并且用这个 Target 处理事件,主要逻辑如下 :
为什么倒序查找 TouchTarget?
如果按添加顺序遍历,当 View 重叠时(FrameLayout),先添加的 View 总是能消费事件,而后添加的 View 不可能获取到事件。
拦截事件:
[1] Android 事件分发机制的设计与实现
[2] Android 事件拦截机制的设计与实现
J. android 如何获取一个界面最顶层的view并处理单击事件的分发机制
android事件分发机制 就是一个触摸事件发生了,从一个窗口传递到一个视图,再传递到另外一个视图,最后被消费的过程,在android中还是比较复杂的传递流程如下:
(1) 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。
(2) 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。