的课程管理系统技术分析分析技术其分析窗口是多长时间

据counterpoint发布的数据,2017年全球前六大手机芯片企……
2018年才刚过没多久,区块链概念股一片火热。沉寂已久的……
近日三星电子宣布开发出核心电池技术,其研究机构三星先……
&&2018新能源与智能联网汽车创新发展论坛成……
近几年,对于买车客户的需求有关注的话,会发现客户越来……
演讲人:孙彬时间: 10:00:00
演讲人:郝晓贝时间: 10:00:00
演讲人:王海莉时间: 10:00:00
预算:¥60000预算:¥10000
广东省北京市
Matlab强大的数组处理功能如何解决图像处理解析方案
[导读]基于COM组件的Matlab与C++混合编程方式因拥有独立的运行环境和两种语言的互补优势而被众多科研人员和编程人员所接受,同时也是MathWorks公司推荐使用的混编方式。但在程序设计过程中,通常会遇到两个难点问题:(1)二
基于COM组件的Matlab与C++混合编程方式因拥有独立的运行环境和两种语言的互补优势而被众多科研人员和编程人员所接受,同时也是MathWorks公司推荐使用的混编方式。但在程序设计过程中,通常会遇到两个难点问题:(1)二维数组参数的传递与输出; (2)Matlab生成图形嵌入VC工程界面中。对于第一个问题,Bruce McKinney[1]在MSDN上指出;&如果对一维数组进行操作,则SAFEARRAY函数变的简单且易操作。但是对于多维数组,同样的操作要复杂得多&,造成这一问题的根源在于Matlab与C++对多维数组元素的存储方式不同。而在本项目开发过程中回避了这个复杂的过程,转而利用Matlab强大的数组处理功能将其解决。Matlab完善的图形处理功能是其被科研人员所推崇的原因之一,但因其图形拥有独立的窗口,严重影响了整体界面美观和用户交互体验,所以Matlab图形嵌入是混合编程中不可回避的问题。对此将通过实例介绍两种图形嵌入方法并分析说明其优缺点和相关细节。本文引用地址:
本文示例均在Matlab R2008a和VC 6.0平台下完成,且通过调试能够正常运行。
1 Excel文件读取方法
VC平台下的Excel文件读取方式复杂难懂,但如果使用Matlab与VC的混合编程方式编写将更加简便,而且拥有较强的可扩展性。下面代码详细讲解了该过程。
(1)在Matlab平台下建立CExcelRead.m文件,代码如下:
function [output,row,col] = CExcelRead(filePath)
//filePath包含了指定文件的路径和文件名,由VC平台函数GetPathName()获取
[typ, desc] = xlsfinfo(filePath);
//xlsfinfo()可获取指定Excel文件中各个sheet工作区的名称,返回值存入名为desc的cell数组中
sheet1 = cell2mat(desc(1));
//默认打开&sheet1&(名称任意),并将其转换为字符串
output = xlsread(filePath,sheet1);
//返回该Excel文件中的数据,返回值存入output数组中
[row,col] = size(output);
//获取录入数据的行列值,返回到VC平台以供其拷贝指定内存大小的数据
output = reshape(output,row*col,1);
//将该二维数组转换为一维。这是第二部分二维数组处理的第一步
保存文件后,使用deploytool工具将其制作成名为FileOpen的COM(封装MCR),注册该控件并将相应的文件拷贝到MFC的工程中,对于基础操作此处不再赘述。
(2)在VC平台下创建名为PCA的基于对话框的MFC工程,添加一打开文件的按钮控件,ID为ID_FILE_OPEN。篇幅所限只给出部分核心代码,首先在Dlg类中添加3个私有成员变量用以保存Matlab读取的数据及行列值,即:
double *m_originD long m_row, m_
在响应函数中添加如下代码
CFileDialog fileDlg(TRUE);
fileDlg.m_ofn.lpstrTitle=&请选择你的excel数据&;
fileDlg.m_ofn.lpstrFilter=&Text Files(*.xls)*.xlsAll Files(*.*)*.*&;
if(IDOK==fileDlg.DoModal() {
CString fileN IFileOpenclass *
VARIANT filePath,output,rowOutput,colO
//m文件的输入参数
fileName = fileDlg.GetPathName();
//获取文件路径并存于fileName中
CoInitialize(NULL); //COM初始化
HRESULT hr = CoCreateInstance(CLSID_FileOpenclass,NULL,CLSCTX_ALL,IID_IFileOpenclass,(void**)&pfile);
//新建COM实例
VariantInit(&filePath); //VARIANT数据初始化
filePath.vt = VT_BSTR; //指定filePath变量类型
//将fileName中保存的指定文件路径名保存至filePath参数中
filePath.bstrVal = fileName.AllocSysString();
pfile-&CExcelRead(3,&output,&rowOutput, &colOutput,filePath);
m_row=(long) rowOutput.dblV
//取出行列值及录入数据
m_col = (long) colOutput.dblV
m_originData = (double *)malloc(sizeof(double)*m_row*m_
col); memcpy(m_originData,output.parray-&pvData,m_row*m_
col*sizeof(double));
//将matlab读入数据保存到m_originData供后面程序使用
上述C++代码中省略了异常处理和相关的内存、COM释放代码,由于代码比较简单所以不做进一步解析。请注意,下面将省略COM初始化及实例化等相同代码。
(3) 需要重点说明的是该方法的扩展性。通常Excel文件中保存多个工作区,有时用户可能需要打开同一Excel文件中不同工作区中的数据,常规方法实现过于复杂,但对于本文介绍的方法可以通过修改添加几条语句即可实现。首先,新建一个m文件用来处理工作区的选择,代码如下:
function [sheet,col] = CSheetSelected(filePath)
[typ, sheet] = xlsfinfo(filePath);
[row,col] = size(sheet);
由于返回值是一个cell数组,所以VC平台要使用CStringArray数据结构保存返回值,并显示各工作区名称供用户选择。然后,通过人机交互将用户选择的工作区参数保存并传递至CExcelRead.m,通过在CExcelRead.m增加一个工作区选择参数,并对代码稍作修改即可。
2 二维数组参数的传递与输出[2]
下面以主成分分析为例介绍基于COM的Matlab与VC混合编程中二维数组参数处理。
(1) 主成分分析pcamat.m代码如下:
function [eigenvector,eigenvalue] = pcamat(oriData,row,column)
//在Excel读入时已经完成了二维数组输出的关键步骤,即输出时将二维数组转换为一维数组。但在VC平台接收还原为二维时要注意,Matlab数组存储方式是按列存储,而VC平台下数组是按行存储,所以转换时0~row-1为第一列,row~2*row-1为第二列,以此类推。本文输入参数oriData是一维数组,所以要将其还原为二维数组使Matlab程序能够正常运行,即下一行代码所示。
oriData = reshape(oriData,row,column);
dataSTD=std(oriData,0,1); dataMean = mean(oriData);
dataSR = (oriData-dataMean(ones(row,1),:))./dataSTD(ones(row,1),:);
[eigenvector,newdata,eigenvalue,Exa]=princomp(dataSR);
//第三行至此处均为主成分分析内容
eigenvector = reshape(eigenvector,column*column,1);
//与Excel文件读取时类似,将二维输出转换为一维数组进行输出
(2) 保存后,封装打包为COM组件,并完成注册等相关操作。在PCA工程对话框上添加一个名为PCATest的按钮控件,核心代码如下:
VARIANT oriData,row,column,eigVector,eigV
VariantInit(&oriData); //参数初始化
oriData.vt = VT_R8|VT_ARRAY;
//定义SAFEARRAY类型的一维数组
SAFEARRAYBOUND rgsadound[1];
rgsadound[0].lLbound = 0;
rgsadound[0].cElements = m_row*m_
oriData.parray = SafeArrayCreate(VT_R8,1,rgsadound);
oriData.parray-&pvData = m_originD
//完成相关设置后,将第一步读入的数据录入到oriData中,即赋给pcamat的oriData。到此完成了二维数组的传递
row.vt = VT_I4; col.vt = VT_I4;
row.lVal = m_ col.lVal = m_
pca-&pcamat(2,&eigVector,&eigValue,oriData,row,col);
memcpy(result, eigVector.parray-&pvData,m_col*m_col*
sizeof(double));
综上,二维数组参数处理就是使用reshape()函数对输入输出数据维数进行变换来完成操作。
3 Matlab图像嵌入VC界面[3]
3.1 基于CWnd类的图像嵌入
在Windows操作系统下,所有应用程序的窗口都是基于MFC中的CWnd类。所以可以通过调用该类或其派生类中的方法实现图像嵌入。基本思想:在Matlab平台下用COM封装产生图形窗口的程序;在VC平台获取Figure窗口的句柄,将Figure窗口设为VC程序的子窗口;(3)将Figure窗口移动到指定显示位置。
程序实现如下:
(1)将原pcamat.m进行修改,添加生成图像的相关代码
function [eigenvector,eigenvalue] = pcamat(oriData,row,column,picName)
//增加picName参数,VC平台下hFig将通过该名称获取生成图像的句柄
figure(&NumberTitle&,&Off&,&MenuBar&,&None&,&ToolBar&,&Figure&,&Name&,picName,&Units&,&Points&);
//图像参数预设,保留工具栏。使用Matlab提供的工具栏的所有功能是使用该方法的最大优点
percent = 100*eigenvalue /sum(eigenvalue);
//计算贡献率
pareto(percent); //画图
xlabel(&主成分&);
ylabel(&方差占的比重(%)&);
(2)封装打包成名为figure的COM组件,并完成注册等相关操作。因为修改后的m文件运行结果包含输出结果和图像两部分,所以下面有关图像处理的代码依然在PCATest控件的响应函数中。
CString WNDName = &Demo&; //自定义窗口名称
Ifigureclass *
VARIANT oriData, row, col, picName,eigVector, eigV VariantInit(&picName);
picName.vt = VT_BSTR; //将自定义窗口名称赋予
Matlab生成图像
picName.bstrVal = WNDName.AllocSysString();
HWND hF int timer = 50;
//用死循环确保可以获取到图像句柄,注意此处必须使用sleep(),给予系统足够的响应时间
pic-&pcamat(2,&eigVector,&eigValue,oriData,row,col,
Sleep(timer);
hFig = ::FindWindow(NULL,FigName);
if(hFig != NULL){
timer += 10;
pic-&Release();
long lStyle = ::GetWindowLong(hFig,GWL_STYLE); //设置Figure窗口样式。
//注意SetWindowLong()和SetWindowPos()先后顺序,详见MSDN
::SetWindowLong(hFig,GWL_STYLE,lStyle&(~WS_CAPTION)&(~WS_THICKFRAME))
::SetWindowPos(hFig,NULL,0,0,0,0,SWP_NOMOVE|SWP_
NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE|SWP_
FRAMECHANGED);CRect PlotR
CWnd *PlotArea = GetDlgItem(IDC_STATIC_FIGURE); //设置图像显示区域
PlotArea-&GetWindowRect(&PlotRec);
long Width = PlotRec.right - PlotRec.
long Height = PlotRec.bottom - PlotRec.
::SetParent(hFig,PlotArea-&GetSafeHwnd());
//设置图像的父窗口
::SetWindowPos(hFig,NULL,0,0,Width,Height,SWP_NOZORDER|SWP_NOACTIVATE);
该方法的缺点是,在图像生成时会有闪烁现象。而优点是前面提到的可以继续使用Matlab提供的工具栏。鉴于该缺点影响整体美观,所以引入下面第二种方法。
3.2基于Bitmap类的图像嵌入
通过Bitmap类将Matlab生成的.bmp文件加载到VC工程中,使用Bitmap类中的成员函数对图像进行处理。由于相关函数可以通过帮助手册获取,所以此处不再给出相应代码,运行结果如图2所示。该方法避免了方法一生成图像时的闪烁现象,但是BMP图像显示效果较差而且无法使用Matlab提供的工具菜单,这是其不足之处。
使用COM组件进行混合编程时,往往习惯于在VC平台下思考所遇到的问题,但是这样不仅使得问题可能变得复杂化,或得不到妥善解决,而且也违背了&混合&的初衷。二维数组参数处理就是一个很好的例证。其次,充分利用Matlab特性可以使得程序具备良好的扩展性和稳定性,对Excel文件读取方式进行的扩展,明显使软件更加人性化。对于图像嵌入问题,虽然文中提出的两种嵌入方式可以满足基本需求,但是仍然存在一些瑕疵,还需要进一步研究。
MathWorks今日推出了Release 2017b(R2017b),其中包括MATLAB和Simulink的若干新功能、六款新产品以及对其他86款产品的更新和修复补丁。此发行版还添加了新的重要的深度学习功能,可简化工程师、研究人员及其他......关键字:
MathWorks今日宣布,增强了用于支持数字辅助 RF MIMO 系统设计的功能。......关键字:
利用基于模型的设计, 猎户座导航、制导和控制系统成功地完成了深空试验飞行21ic讯 MathWorks今日详细报告了它对于成功完成美国宇航局 (NASA)
猎户座飞船试验飞行的贡献。此次飞行于 2014 年 12 月 5 日结束,标志着......关键字:
21ic讯 MathWorks近日宣布推出2014b版本。该新版本包含全新的 MATLAB 图形和大数据功能,并且能够在 Simulink 中更快速地运行连续仿真。MATLAB 产品系列新增的 MATLAB 功能提供了全新的图形系统,增加了......关键字:
21ic讯 MathWorks 今日宣布,将混合整数线性规划 (MILP) 加入 MATLAB。这个新的求解器现在作为 Release 2014a
Optimization Toolbox 的组成部件提供,可让用户解决需要整数解的优化问题......关键字:
近日多个共享单车App用户反映,押金余额难退还,客服电话更难打通。记者亲身体验时,摩拜单车客服表示可能存在退款失败的情况,ofo和blue gogo的热线确实无法接通。......关键字:
用数字说话:10项编程技能让你在2017年实现薪酬提升......关键字:
这几天来,我(Eric Raymond)在思考那些正在挑战C语言的系统编程语言领袖地位的新潮语言,尤其是Go和Rust。思考的过程中,我意识到了一个让我震惊的事实――我有着35年的C语言经验。每周我都要写很多C代码,但是我已经记不清楚上一次......关键字:
我 要 评 论
热门关键词当前位置: >
暨大EMBA名师讲堂:如何用数量分析方法解决经济管理中的实际问题?――熊剑教授论数量分析与管理决策
来源: 责任编辑:mbazlc 日期:10-10-27 13:10:48 点击量:
EMBA名师讲堂:如何用数量分析方法解决经济管理中的实际问题?
――熊剑教授论数量分析与管理决策
&&& 你了解GDP吗?它与管理工作有何关系?测度投资风险为何要用标准差和离散系统?它们与投资决策何干?已学过的数量技术(如统计知识)对自己的专业学习和工作有何帮助?这些问题都涉及到指标量与综合评价等方面,体现出“量化”的思想。熊剑教授提出思考题,学员们在思考中进入到了“数量分析与管理决策”的课堂。
&&& 授课老师熊剑教授首先做了课程概述,他指出“数量分析与管理决策”是专门讲述如何应用数量分析方法揭示和解决经济管理(尤其是企业管理决策)领域中之实际问题的课程。课程着力解析经济管理与决策中定量分析方法的基本思想、主要方式和应用案例,并具体讲解管理决策中指标量化的描述与揭示方法、数量平均与差异分析法、复杂现象变动趋势与因素分析、关联性推断与检验等多种数量分析方法。熊老师用多媒体授课,同时采用案例讨论和课堂互动式问答的形式进行讲学。讨论式的课堂,师生交流,气氛非常活跃。
&&& “数量分析与管理决策”共分为了四讲,分别是“量化”、“差异”、“变化”和“关联”。在第一讲“量化”中,熊剑教授介绍了数量描述的基本工具――指标量度与综合评价。他说明了经济与管理问题应把握的3个关键点:首先是明确目的,其次是建立理念,再次是设计方法。他还具体阐述了指标及其功能、有关的指标体系和企业经济的综合评价。第二讲“差异”是此次课堂的重点部分,主要内容是差异的度量与分析――趋势计算与风险决策,这一部分有大量的数据分析实例以及各类指标计算的公式,先讲解集中趋势和离中趋势的测试与应用,然后再进入到企业投资风险的测评与决策,这些都是为企业投资组合决策提供数量分析的方法。“变化”与“关联”两讲都是谈它们的量度与分析。“变化”的内容是“因素分解与结构选择”,着重讲解现象变动的因素分析,再引申到指数扩展――多因素分析,最后介绍了指数的应用,熊剑教授列举了我国多种应用性指数,如CPI、RPI、PI等,还简介了股票价格指数,例如道?琼斯平均数、标准普尔指数、金融时报指数、日经指数、恒生股价指数。“关联性量度与分析”,即“总体推断与信度检验”。此讲先告知了学员4点:如何抽样;如何计算样本统计量;如何对总体作推断估计;必须懂得如何对总体估计作出检验判断。主要涉及了计量经济学的一些知识,比如抽样分布、样本均值、参数估计、随即抽样等。最后熊剑教授从基本思想、检验规则、检验步骤到常见的假设检验分析了信度检验。
&&& 数量分析与管理决策属于EMBA的核心课程,熊剑教授设计的教学课程条理清晰,最开始从小实例看“量化”思想与技术,然后从方法和案例看“量化”技术与决策思想,再从复杂总体变动看影响因素的“量化”反应,最后从部分认知总体的“量化”技术与误差控制。师生互动的方法要求学生参与课堂和案例讨论,使学员更好地掌握数量分析方法。通过学习此课程希望使成功的管理者切实掌握有关又有用的数量分析方法,并在实际的经济管理工作中将定性分析思想与定量分析技术结合起来处理问题,而且能恰当地应用数量分析思想和技术进行管理研究和决策。定量分析方法_数字化定量分析_管理定量分析-学习讨论及资料下载-人大经济论坛
什么是定量分析?
--定量分析专题
什么是定量分析法?定量分析法是对社会现象的数量特征、数量关系与数量变化进行分析的方法。在企业管理上,定量分析法是以企业财务报表为主要数据来源,按照某种数理方式进行加工整理,得出企业信用结果。定量分析是投资分析师使用数学模块对公司可量化数据进行的分析,通过分析对公司经营给予评价并做出投资判断。定量分析的对象主要为财务报表,如资金平衡表、损益表、留存收益表等。其功能在于揭示和描述社会现象的相互作用和发展趋势。
&&& 两位兄台高见。其实,国内存在太多的以定量分析作为幌子的所谓经济学研究,一味地标榜定量分析,利用一些很值得怀疑的数据和方法在一些所谓的核心甚至权威上发文章。误导了一代学子。定量分析很重要,这谁都知道,但是如果我们不是在定性的基础上去定量,恐怕就要误入歧途了。 我特别反对唯工具论,一味地夸大数学的作用,以至于大家对思想抛弃去追求工具,舍本逐末之举,缘木求鱼之为。我的很多同学就存在这样的问题,只学习数学去套一个个数理计量模型,静态还不懂就开始动态了,均衡还不懂就开始非均衡了。写出来的文章....
问题1:EG两步法做协整检验的思路?
&& &思路1:以两个变量为例,在变量满足同阶单整的条件下,对变量做OLS,对残差的平稳性做检验,如果残差平稳,那么认为两个变量之间存在协整关系,同时进一步做误差修正模型,并对误差修正模型的残差进行自相关检验,如果存在自相关,要加入变量的滞后项消除自相关。
&& &思路2:以两个变量为例,在变量满足同阶单整的前提下,对变量做OLS,先对回归方程的残差进行自相关检验,如果存在自相关先消除自相关,然后对消除自相关的回归方程的残差进行平稳性检验,如果残差平稳,则认为两个变量之间存在协整关系....
定量分析方法
【内容提要】 本书主要内容包括:绪论;分析研究的程序;逻辑方法;德尔菲法;层次分析法;回归分析法;时间序列分析法;决策法;优化决策共九部分。
我最近要写开题报告,老板催的比较急,我之前收集了一些关于房地产价格的资料,希望对其进行量化的研究,由于时间的关系,我没有看很多关于定量分析的书籍和模型,接触到的就是价格特征模型,数据包络分析和matlab,希望各位高手能帮我参谋一下,用哪种具体的分析方法比较适合我的这个主题,我还想在此基础上稍微做些优化,谢谢.
【报告下载】
数字化定量分析
金融市场本就是一个数字王国,我们每天都要跟各种数字来打交道,那么我们为什么不用数字的运行规律来进行分析和推理呢?《数字化定量分析》一书的核心思想就是把金融市场的分析角度从现象上升到数学。本书介绍的可能是您从没见过的,目前独一无二的从数字运行规律出发的方法。使用简单。内容新颖,实用性强,而且具备普遍的适用性,可以应用于股票、外汇、期货等多种金融市场。
如何创建自己的操作系统之百炼成?
从直觉性交易到系统性交易的过程中,你会遇到这样那样的困难,困难可以让一个人望而却步,磨难同样也可以强化一个人的意志。能否成功的建立起属于自己的操作系统,踏上通望金融王国的自由之路,在于你面对困难的态度。未经历磨难与考验往往会庸庸碌碌过一生
的人总是给一些资料挂高价钱,诚然对有的人没什么影响,但是对于我等论坛菜鸟而言,真的是难以抉择,动辄几十的金币对于我们真的不好弄,当然这也是获取金币的渠道之一,所以没什么攻击别人的意思,大家互助分享,大气谦和才是求学之本。没人交流,也就算了吧,不过此书值得推荐。此书《数字化定量分析》的核心思想就是把金融市场的分析角度从现象上升到数学。作者由数字运行规律出发。提出掌握市场波动的原理,使用简单、内容新颖、实用性强,而且具备普遍的适用性,可以应用于股票、外汇、期货等多种金融市场。
管理定量分析
查尔斯·博尼尼,沃伦·豪斯曼,小哈罗德·比尔曼合著,北美权威管理定量分析教材。英文原版,600页左右。内容全面,材料很新。
相关专题推荐
kpi绩效考核
企业价值观
精彩文章top10
资料下载top10
精华帖子top10
京ICP证090565号
京公网安备号热点搜索:
//管理案例分析方法
管理案例分析方法相关文章
关注管理案例分析方法领域的实践和动态。在这里,您既可体验鲜活而精彩的管理案例分析方法案例,聆听专家对管理案例分析方法难点问题的解答,又可掌握实用工具,并紧扣前沿,与广大经理人和精英切磋管理案例分析方法之道。
www.ceconlinebbs.com经理人论坛提供与卓越经理人互动交流的有效平台,分享自己的管理经验,共同推动中国管理与全球一流的管理实践接轨。
www.ceconline.com世界经理人网站致力于引导中国经理人实现卓越管理。在这里,你可以找到全球最先进,并切合中国实际的管理思想和经验。
&1...到第页
共享单车被毁究竟是公民素质低还是管理不到位呢?您怎么看?Android窗口管理服务WindowManagerService对输入法窗口的管理分析
在中,输入法窗口是一种特殊类型的窗口,它总是位于需要使用输入法的窗口的上面。也就是说,一旦WindowManagerService服务检测到焦点窗口需要使用输入法,那么它就会调整输入法窗口在窗口堆栈中的位置,使得输入法窗口位于在焦点窗口的上面,这样用户可以通过输入法窗口来录入字母或者文字。本文就将详细分析WindowManagerService服务是如何管理系统中的输入法窗口的。
& & & & 在Android系统中,除了输入法窗口之外,还有一种窗口称为输入法对话框,它们总是位于输入窗口的上面。Activity窗口、输入法窗口和输入法对话框的位置关系如图1所示:
图1 Activity窗口、输入法窗口和输入法对话框的位置关系
& & & & 在前面Android窗口管理服务WindowManagerService组织窗口的方式分析一文中提到,WindowManagerService服务是使用堆栈来组织系统中的窗口的,因此,如果我们在窗口堆栈中观察Activity窗口、输入法窗口和输入法对话框,它们的位置关系就如图2所示:
图2 Activity窗口、输入法窗口和输入法对话框在窗口堆栈中的位置关系
& & & & 图2中的对象的关系如下所示:
& & & & 1. 在ActivityManagerService服务内部的Activity堆栈顶端的ActivityRecord对象N描述的是系统当前激活的Activity组件。
& & & & 2. ActivityRecord对象N在WindowManagerService服务内部的窗口令牌列表顶端对应有一个AppWindowToken对象N。
& & & & 3. AppWindowToken对象N在WindowManagerService服务内部的窗口堆栈中对应有一个WindowState对象N,用来描述系统当前激活的Activity组件窗口。
& & & & 4. WindowState对象N上面有一个WindowState对象IMW,用来描述系统中的输入法窗口。
& & & & 5. WindowState对象IMW上面有三个WindowState对象IMD-1、IMD-2和IMD-3,它们用来描述系统中的输入法对话框。
& & & & 6. 系统中的输入法窗口以及输入法对话框在WindowManagerService服务内部中对应的窗口令牌是由WindowToken对象IM来描述的。
& & & & 7. WindowToken对象IM在InputMethodManagerService服务中对应有一个Binder对象。
& & & & 总的来说,就是图2描述了系统当前激活的Activity窗口上面显示输入法窗口,而输入法窗口上面又有一系列的输入法对话框的情景。WindowManagerService服务的职能之一就是要时刻关注系统中是否有窗口需要使用输入法。WindowManagerService服务一旦发现有窗口需要使用输入法,那么就会调整输入法窗口以及输入法对话框在窗口堆栈中的位置,使得它们放置在需要使用输入法的窗口的上面。
& & & & 接下来,我们就首先分析两个需要调整输入法窗口以及输入法对话框在窗口堆栈中的位置的情景,然后再分析它们是如何在窗口堆栈中进行调整的。
& & & & 第一个需要调整输入法窗口以及输入法对话框在窗口堆栈中的位置的情景是增加一个窗口到WindowManagerService服务去的时候。从前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文可以知道,增加一个窗口WindowManagerService服务最终是通过调用WindowManagerService类的成员函数addWindow来实现的。接下来我们就主要分析这个函数中与输入法窗口以及输入法对话框调整相关的逻辑,如下所示:
public class WindowManagerService extends IWindowManager.Stub & & &
& & & & implements Watchdog.Monitor { & &
& & ...... &
& & WindowState mInputMethodWindow = &
& & final ArrayList&WindowState& mInputMethodDialogs = new ArrayList&WindowState&(); &
& & ...... &
& & public int addWindow(Session session, IWindow client, &
& & & & & & WindowManager.LayoutParams attrs, int viewVisibility, &
& & & & & & Rect outContentInsets, InputChannel outInputChannel) { &
& & & & ...... &
& & & & synchronized(mWindowMap) { &
& & & & & & ...... &
& & & & & & WindowToken token = mTokenMap.get(attrs.token); &
& & & & & & if (token == null) { &
& & & & & & & & ...... &
& & & & & & & & if (attrs.type == TYPE_INPUT_METHOD) { &
& & & & & & & & & & ...... &
& & & & & & & & & & return WindowManagerImpl.ADD_BAD_APP_TOKEN; &
& & & & & & & & } &
& & & & & & & & ...... &
& & & & & & } & & & &
& & & & & & ...... &
& & & & & & win = new WindowState(session, client, token, &
& & & & & & & & & & attachedWindow, attrs, viewVisibility); &
& & & & & & ...... &
& & & & & & boolean imMayMove = &
& & & & & & if (attrs.type == TYPE_INPUT_METHOD) { &
& & & & & & & & mInputMethodWindow = &
& & & & & & & & addInputMethodWindowToListLocked(win); &
& & & & & & & & imMayMove = &
& & & & & & } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) { &
& & & & & & & & mInputMethodDialogs.add(win); &
& & & & & & & & addWindowToListInOrderLocked(win, true); &
& & & & & & & & adjustInputMethodDialogsLocked(); &
& & & & & & & & imMayMove = &
& & & & & & } &&
& & & & & & ...... &
& & & & & & boolean focusChanged = &
& & & & & & if (win.canReceiveKeys()) { &
& & & & & & & & focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS); &
& & & & & & & & if (focusChanged) { &
& & & & & & & & & & imMayMove = &
& & & & & & & & } &
& & & & & & } &
& & & & & & if (imMayMove) { &
& & & & & & & & moveInputMethodWindowsIfNeededLocked(false); &
& & & & & & } &
& & & & & & &&
& & & & & & ...... &
& & & & } & &&
& & & & &&
& & & & ...... &
& & ...... &
& & & & 这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
& & & & 如果当前增加到WindowManagerService服务来的是一个输入法窗口,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_INPUT_METHOD,那么就要求与该输入法窗口所对应的类型为WindowToken的窗口令牌已经存在,否则的话,WindowManagerService类的成员函数addWindow就会直接返回一个错误码WindowManagerImpl.ADD_BAD_APP_TOKEN给调用者。这个类型为WindowToken的窗口令牌是InputMethodManagerService服务请求WindowManagerService服务创建的,即调用WindowManagerService类的成员函数addWindowToken来创建的,具体可以参考前面Android窗口管理服务WindowManagerService组织窗口的方式分析一文。
& & & & 如果当前增加到WindowManagerService服务来的是一个输入法窗口,那么就会将前面为它所创建的一个WindowState对象win保存在WindowManagerService类的成员变量mInputMethodWindow中,接着还会调用WindowManagerService类的成员函数addInputMethodWindowToListLocked来将该WindowState对象插入到窗口堆栈的合适位置去。
& & & & 如果当前增加到WindowManagerService服务来的是一个输入法对话框,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_INPUT_METHOD_DIALOG,那么就会将前面为它所创建的一个WindowState对象win添加到WindowManagerService类的成员变量mInputMethodDialogs所描述的一个ArrayList中去,并且先后调用WindowManagerService类的成员函数addWindowToListInOrderLocked和adjustInputMethodDialogsLocked来将该WindowState对象插入到窗口堆栈的合适位置去。
& & & & 在上述两种情况中,由于用来描述输入法窗口或者输入法对话框的WindowState对象已经被插入到了窗口堆栈中的合适位置,因此,接下来就不再需要考虑移动该输入法窗口或者输入法对话框了,这时候变量imMayMove的值就会被设置为false。
& & & &另一方面,如果当前增加到WindowManagerService服务来的既不是一个输入法窗口,也不是一个输入法对话框,并且该窗口需要接收键盘事件,即前面所创建的WindowState对象win的成员函数canReceiveKeys的返回值为true,那么就可能会导致系统当前获得焦点的窗口发生变化,这时候就需要调用WindowManagerService类的成员函数updateFocusedWindowLocked来重新计算系统当前获得焦点的窗口。如果系统当前获得焦点的窗口发生了变化,那么WindowManagerService类的成员函数updateFocusedWindowLocked的返回值focusChanged就会等于true,同时系统的输入法窗口和输入法对话框在窗口堆栈中的位置也会得到调整,即位它们会位于系统当前获得焦点的窗口的上面,因此,这时候变量imMayMove的值也会被设置为false,表示接下来不再需要考虑移动系统中的输入法窗口或者输入法对话框在窗口堆栈中的位置。
& & & &最后,如果变量imMayMove的值保持为初始值,即保持为true,那么就说明当前增加的窗口可能会引发系统的输入法窗口和输入法对话框在窗口堆栈中的位置发生变化,因此,这时候就需要调用WindowManagerService类的成员函数moveInputMethodWindowsIfNeededLocked来作检测,并且在发生变化的情况下,将系统的输入法窗口和输入法对话框移动到窗口堆栈的合适位置上去。
& & & &从上面的分析就可以知道,在增加一个窗口的过程中,可能需要调用WindowManagerService类的成员函数addInputMethodWindowToListLocked、addWindowToListInOrderLocked、adjustInputMethodDialogsLocked和moveInputMethodWindowsIfNeededLocked来移动系统的输入法窗口和输入法对话框,其中,WindowManagerService类的成员函数addWindowToListInOrderLocked在前面Android窗口管理服务WindowManagerService组织窗口的方式分析一文已经分析过了,本文只要关注其余三个成员函数的实现。
& & & &第一个需要调整输入法窗口以及输入法对话框在窗口堆栈中的位置的情景是一个应用程序进程请求WindowManagerService服务重新布局一个窗口的时候。从前面Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析一文可以知道,应用程序进程请求WindowManagerService服务重新布局一个窗口最终是通过调用WindowManagerService类的成员函数relayoutWindow来实现的。接下来我们就主要分析这个函数中与输入法窗口以及输入法对话框调整相关的逻辑,如下所示:
public class WindowManagerService extends IWindowManager.Stub & & &
& & & & implements Watchdog.Monitor { & &
& & ...... &
& & public int relayoutWindow(Session session, IWindow client, &
& & & & & & WindowManager.LayoutParams attrs, int requestedWidth, &
& & & & & & int requestedHeight, int viewVisibility, boolean insetsPending, &
& & & & & & Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, &
& & & & & & Configuration outConfig, Surface outSurface) { &
& & & & ...... &
& & & & synchronized(mWindowMap) { &
& & & & & & WindowState win = windowForClientLocked(session, client, false); &
& & & & & & ...... &
& & & & & & int attrChanges = 0; &
& & & & & & int flagChanges = 0; &
& & & & & & if (attrs != null) { &
& & & & & & & & flagChanges = win.mAttrs.flags ^= attrs. &
& & & & & & & & attrChanges = win.mAttrs.copyFrom(attrs); &
& & & & & & } &
& & & & & & ...... &
& & & & & & boolean imMayMove = (flagChanges&( &
& & & & & & & & & & WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | &
& & & & & & & & & & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE)) != 0; &
& & & & & & boolean focusMayChange = win.mViewVisibility != viewVisibility &
& & & & & & & & & & || ((flagChanges&WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0) &
& & & & & & & & & & || (!win.mRelayoutCalled); &
& & & & & & ...... &
& & & & & & if (viewVisibility == View.VISIBLE && &
& & & & & & & & & & (win.mAppToken == null || !win.mAppToken.clientHidden)) { &
& & & & displayed = !win.isVisibleLw(); &
& & & & & & & & ...... &
& & & & & & & & if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) { &
& & & & & & & & & & // To change the format, we need to re-build the surface. &
& & & & & & & & & & win.destroySurfaceLocked(); &
& & & & & & & & & & displayed = &
& & & & & & & & } &
& & & & & & & & ...... &
& & & & & & & & if (win.mAttrs.type == TYPE_INPUT_METHOD &
& & & & & & & & & & & & && mInputMethodWindow == null) { &
& & & & & & & & & & mInputMethodWindow = &
& & & & & & & & & & imMayMove = &
& & & & & & & & } &
& & & & & & & & if (displayed) { &
& & & & & & & & & & focusMayChange = &
& & & & & & & & } &
& & & & & & & &
& & & & & & & & ...... &
& & & & & & } else { &
& & & & & & & & ...... &
& & & & & & & & if (win.mSurface != null) { &
& & & & & & & & & & ...... &
& & & & & & & & & & // If we are not currently running the exit animation, we &
& & & & & & & & & & // need to see about starting one. &
& & & & & & & & & & if (!win.mExiting || win.mSurfacePendingDestroy) { &
& & & & & & & & & & & & ...... &
& & & & & & & & & & & & if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() && &
& & & & & & & & & & & & & & & applyAnimationLocked(win, transit, false)) { &
& & & & & & & & & & & & & & focusMayChange = &
& & & & & & & & & & & & & & win.mExiting = &
& & & & & & & & & & & & } else if (win.isAnimating()) { &
& & & & & & & & & & & & & & // Currently in a hide animation... turn this into &
& & & & & & & & & & & & & & // an exit. &
& & & & & & & & & & & & & & win.mExiting = &
& & & & & & & & & & & & } else if (win == mWallpaperTarget) { &
& & & & & & & & & & & & & & // If the wallpaper is currently behind this &
& & & & & & & & & & & & & & // window, we need to change both of them inside &
& & & & & & & & & & & & & & // of a transaction to avoid artifacts. &
& & & & & & & & & & & & & & win.mExiting = &
& & & & & & & & & & & & & & win.mAnimating = &
& & & & & & & & & & & & } else { &
& & & & & & & & & & & & & & if (mInputMethodWindow == win) { &
& & & & & & & & & & & & & & & & mInputMethodWindow = &
& & & & & & & & & & & & & & } &
& & & & & & & & & & & & & & win.destroySurfaceLocked(); &
& & & & & & & & & & & & } &
& & & & & & & & & & } &
& & & & & & & & } &
& & & & & & & & ...... &
& & & & & & } &
& & & & & & if (focusMayChange) { &
& & & & & & & & ...... &
& & & & & & & & if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) { &
& & & & & & & & & & imMayMove = &
& & & & & & & & } &
& & & & & & & & ...... &
& & & & & & } &
& & & & & & // updateFocusedWindowLocked() already assigned layers so we only need to &
& & & & & & // reassign them at this point if the IM window state gets shuffled &
& & & & & & boolean assignLayers = &
& & & & & & if (imMayMove) { &
& & & & & & & & if (moveInputMethodWindowsIfNeededLocked(false) || displayed) { &
& & & & & & & & & & // Little hack here -- we -should- be able to rely on the &
& & & & & & & & & & // function to return true if the IME has moved and needs &
& & & & & & & & & & // its layer recomputed. &However, if the IME was hidden &
& & & & & & & & & & // and isn't actually moved in the list, its layer may be &
& & & & & & & & & & // out of data so we make sure to recompute it. &
& & & & & & & & & & assignLayers = &
& & & & & & & & } &
& & & & & & } &
& & & & & & ...... &
& & & & & & if (assignLayers) { &
& & & & & & & & assignLayersLocked(); &
& & & & & & } &
& & & & & & ...... &
& & & & } &
& & & & ...... &
& & & & return (inTouchMode ? WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE : 0) &
& & & & & & & & | (displayed ? WindowManagerImpl.RELAYOUT_FIRST_TIME : 0); &
& & ...... &
& & & & 这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
& & & & 应用程序进程在请求WindowManagerService服务重新布局一个窗口的时候,这个窗口的一些布局参数可能会发生变化,而这些变化可能会同时引发系统的输入法窗口以及输入法对话框在窗口堆栈中的位置发生变化。如果系统的输入法窗口以及输入法对话框在窗口堆栈中的位置发生了变化,那么就需要调整它们在窗口堆栈中的位置。
& & & & WindowManagerService类的成员函数relayoutWindow首先调用根据参数session和client来调用另外一个成员函数windowForClientLocked,以便可以获得用来描述要重新布局的窗口的一个WindowState对象win。
& & & & WindowState对象win的成员变量mAttrs指向的是一个WindowManager.LayoutParams对象,该WindowManager.LayoutParams对象的成员变量flags描述的是窗口上一次所设置的布局属性标志位,而参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量flags描述的是窗口当前被设置的布局属性标志位。WindowManagerService类的成员函数relayoutWindow通过对这两个标志位执行一个异或操作,就可以知道窗口的哪些布局属性标志位发生了变化,这些变化就记录在变量flagChanges中。
& & & & WindowManagerService类的成员函数relayoutWindow在对WindowState对象win所描述的窗口进行布局之前,还要将参数attrs指的是一个WindowManager.LayoutParams对象的内容拷贝到 WindowState对象win的成员变量mAttrs指向的是一个WindowManager.LayoutParams对象中去。在拷贝的过程中,如果发现这两个WindowManager.LayoutParams对象所描述的窗口布局属性有发生变化,那么这些变化就会记录在变量attrChanges中。
& & & & 在窗口的布局属性标志中,位WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE表示窗口是否可以获得焦点,另外一个位WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM是用来反转WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE位的作用的。一个窗口是否可以获得焦点意味着它是否需要与输入法窗口交互,即如果一个窗口是可以获得焦点的,那么就意味着它需要与输入法窗口交互,否则就不需要。当一个窗口的WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE位等于1,那么就表示窗口不可以获得焦点,即不需要与输入法窗口交互,但是如果该窗口的WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM位也等于1,那么就表示窗口仍然是需要与输入法窗口交互的。另一方面,如果一个窗口的WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM位等于1,但是该窗口的WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE位等于0,那么就表示窗口仍然是不可以与输入法窗口交互的。因此,当前面得到的变量flagChanges的WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE位或者WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM位发生了变化时,都意味着对WindowState对象win所描述的窗口进行重新布局会影响系统中的输入法窗口以及输入法对话框,即该窗口可能会由需要显示输入法窗口以及输入法对话框,到不需要显示输入法窗口以及输入法对话框,反之亦然。最后得到的变量imMayMove的值等于true就表示要移动系统中的输入法窗口以及输入法对话框在窗口堆栈中的位置。
& & & & 一个窗口由不可获得焦点到可以获得焦点,或者由可获得焦点到不可以获得焦点,即窗口布局属性标志中的WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE位发生了变化,那么就意味着要重新计算系统当前获得焦点的窗口。从前面分析增加窗口到WindowManagerService服务的情景可以知道,当系统当前获得焦点的窗口发生变化时,也意味着需要系统中的移动输入法窗口以及输入法对话框在窗口堆栈中的位置。除了窗口布局属性标志中的WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE位变化会引发系统当前获得焦点的窗口发生变化之外,还有另外两个因素会引发系统当前获得焦点的窗口发生变化。第一个因素是窗口的可见性发生变化。WindowState对象win的成员变量mViewVisibility描述的是窗口上一次布局时的可见性,而参数viewVisibility描述的是窗口当前的可见性,当它们的值不相等时,就意味着窗口的可见性发生了变化。第二个因素是窗口是第一次被应用程序进程请求WindowManagerService服务布局,这时候WindowState对象win的成员变量mRelayoutCalled的值就会等于false。最后得到的变量focusMayChange等于true,就表示需要重新计算系统当前获得焦点的窗口。
& & & & WindowState对象win所描述的窗口在此次重新布局中是否会引起移动系统中的输入法窗口以及输入法对话框在窗口堆栈中的位置,还取决于它在的可见性以及它的绘图表面属性等信息,接下来我们就按照 WindowState对象win所描述的窗口当前是可见还是不可见来分别分析。
& & & & 我们首先分析WindowState对象win所描述的窗口在此次重新布局中是可见的情景,即参数viewVisibility的值等于View.VISIBLE。注意,如果WindowState对象win所描述的是一个Activity窗口,而该Activity组件是不可见的,那么即使参数viewVisibility的值等于View.VISIBLE,那么WindowState对象win所描述的窗口在此次重新布局中也是认为不可见的。从前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文可以知道,当WindowState对象win的成员变量mAppToken的值不等于null时,那么该WindowState对象win描述的是一个Activity窗口,而当该成员变量所指向的一个AppWindowToken对象的成员变量clientHidden的值等于false时,就表示对应的Activity组件是可见的。
& & & & WindowState对象win所描述的窗口在上一次布局时的可见性可以调用它的成员函数isVisibleLw来获得。如果WindowState对象win所描述的窗口在上一次布局时是不可见的,那么现在就需要将它设置为可见的,即要将它显示出来,这时候变量displayed的值就会等于true。另一方面,如果WindowState对象win所描述的窗口的绘图表面的像素格式发生了变化,即变量attrChanges的WindowManager.LayoutParams.FORMAT_CHANGED位等于1,那么这时候就需要调用WindowState对象win的成员函数destroySurfaceLocked来销毁它所描述的窗口的绘图表面,以便接下来可以为它重新创建一个新的绘图表面,这时候也会将变量displayed的值设置为true,表示接下来是要显示WindowState对象win所描述的窗口的。如果最终得到的变量displayed的值设置为true,那么就相当于说明WindowState对象win所描述的窗口经历一个由不可见到可见的状态变化,因此就可能会导致系统当前获得焦点的窗口发生变化,这时候就会将变量focusMayChange的值设置为true。
& & & & 如果WindowState对象win描述的是一个输入法窗口,即它的成员变量mAttrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_INPUT_METHOD,并且系统中的输入法窗口尚未设置,即WindowManagerService类的成员变量mInputMethodWindow的值等于null,那么就说明接下来要显示的其实是输入法窗口,这情况会导致需要移动系统中的输入法窗口以及输入法对话框在窗口堆栈中的位置,因此,这时候除了需要将WindowState对象win保存在WindowManagerService类的成员变量mInputMethodWindow之外,还需要将变量imMayMove的值设置为true。
& & & & 我们接下来再分析WindowState对象win所描述的窗口在此次重新布局中是不可见的情景。一个窗口变得不可见了,就意味着可能要销毁它的绘图表面,取决于它的绘图表面是否存在,以及它的退出动画是否已经显示结束。WindowState对象win所描述的窗口的绘图表面保存在它的成员变量mSurface中,因此,当WindowState对象win的成员变量mSurface不等于null的时候,就意味着可能会销毁它所描述的绘图表面。
& & & & 如果WindowState对象win的成员变量mExiting等于false时,那么就说明该WindowState对象win所描述的窗口的退出动画可能尚未开始,也可能已经结束。另一方面,如果WindowState对象win的成员变量mSurfacePendingDestroy的值等于true,那么就说明该WindowState对象win所描述的窗口的绘图表面正在等待销毁。这两种情况都需要进一步确定接下来是要开始WindowState对象win所描述的窗口的退出动画,还是要销毁WindowState对象win所描述的窗口的绘图表面。
& & & & 如果WindowState对象win的成员变量mSurfacePendingDestroy的值等于false,那么同时也意味着它所描述的窗口还未开始显示退出动画,因而它的绘图表面就没有进入正在等待销毁的状态。在这种情况下,如果WindowState对象win所描述的窗口是可见的,即它的成员函数isWinVisibleLw的返回值等于true,那么就意味要开始该窗口的退出动画了,这是通过调用WindowManagerService类的成员函数applyAnimationLocked来实现的。WindowState对象win描述的窗口开始退出动画之后,就意味要重新计算系统当前获得焦点的窗口,因此,这时候就会将变量focusMayChange的值设置为true,同时还会将WindowState对象win的成员变量mExiting的值设置为true,表示它描述的窗口正在退出的过程中。
& & & & 如果WindowState对象win所描述的窗口正在处于退出动画的过程中,即它的成员函数isAnimating的返回值等于true,那么这时候需要确保WindowState对象win的成员变量mExiting的值为true。
& & & & 如果WindowState对象win所描述的窗口已经结束退出动画,但是它仍然是壁纸窗口的目标,即WindowManagerService类的成员变量mWallpaperTarget的值不等于null,并且它的值就等于WindowState对象win,那么这时候就需要等待壁纸窗口也退出之后,才销毁WindowState对象win所描述的窗口,因此,这时候就需要将WindowState对象win的成员变量mExiting和mAnimating的值设置为true,即假装它所描述的窗口还处于正在退出的过程,这样做是为了等待壁纸窗口退出完成。
& & & & 如果WindowState对象win所描述的窗口已经结束退出动画,并且它不是壁纸窗口的目标,那么这时候就需要调用它的成员函数destroySurfaceLocked来销毁它的绘图表面了。在销毁WindowState对象win所描述的窗口之前,还会判断它是否就是系统当前的输入法窗口,即WindowManagerService类的成员变量mInputMethodWindow的值是否等于win。如果等于的话,那么就说明系统当前的输入法窗口被销毁了,因此,就需要将WindowManagerService类的成员变量mInputMethodWindow的值设置为null。
& & & & 经过上面的一系列操作之后,如果最终得到的变量focusMayChange的值等于true,那么就说明需要重新计算系统当前获得焦点的窗口了,这是通过调用WindowManagerService类的成员函数updateFocusedWindowLocked来实现的。一旦WindowManagerService类的成员函数updateFocusedWindowLocked的返回值为true,那么就说明统当前获得焦点的窗口发生了变化,并且系统中的输入法窗口以及输入法对话框也移动到窗口堆栈中的正确位置了,因此,这时候就会将变量imMayMove的值设置为false。
& & & & 经过上面的一系列操作之后,如果最终得到的变量imMayMove的值等于true,那么就说明有可能需要移动系统中的输入法窗口以及输入法对话框在窗口堆栈中的位置,这是通过调用WindowManagerService类的成员函数moveInputMethodWindowsIfNeededLocked来实现的。一旦系统中的输入法窗口以及输入法对话框在窗口堆栈中的位置发生了移动,那么WindowManagerService类的成员函数moveInputMethodWindowsIfNeededLocked的返回值就等于true,这时候就需要将变量assignLayers的值设置为true,表示要重新计算系统中的窗口的Z轴位置,以便可以同步到SurfaceFlinger服务中去。注意,如果系统中的输入法窗口以及输入法对话框在窗口堆栈中的位置没有发生变化,但是前面得到的变量displayed的值等于true,那么也是需要将变量assignLayers的值设置为true的,因为这个变量displayed的值等于true意味着WindowState对象win所描述的窗口经历了从不可见到可见的状态变化,因此也需要重新计算系统中的窗口的Z轴位置。
& & & & 经过上面的一系列操作之后,如果最终得到的变量assignLayers的值等于true,那么就需要调用WindowManagerService类的成员函数assignLayersLocked来执行重新计算统中的窗口的Z轴位置的操作了。在后面的文章中,我们再详细分析WindowManagerService服务是如何计算系统中的窗口的Z轴位置的。
& & & & 从上面的分析就可以知道,在布局一个窗口的过程中,可能需要调用WindowManagerService类的成员函数moveInputMethodWindowsIfNeededLocked来移动系统的输入法窗口和输入法对话框。再结合前面增加窗口的情景,我们就可以知道,在WindowManagerService类中,与输入法窗口以及输入法对话框相关的成员函数有addInputMethodWindowToListLocked、adjustInputMethodDialogsLocked和moveInputMethodWindowsIfNeededLocked,它们的作用如下所示:
& & & & A. 成员函数addInputMethodWindowToListLocked用来将输入法窗口插入到窗口堆栈的合适位置,即插入到需要显示输入法窗口的窗口的上面。
& & & & B. 成员函数adjustInputMethodDialogsLocked用来移动输入法对话框到窗口堆栈的合适位置,即移动到输入法窗口的上面。
& & & & C. 成员函数moveInputMethodWindowsIfNeededLocked用来检查是否需要移动输入法窗口以及输入法对话框。如果需要的话,那么就将它们移动到窗口堆栈的合适位置去,即将输入法窗口移动到需要显示输入法窗口的窗口的上面,而将输入法对话框移动到输入法窗口的上面。
& & & & 在分析这三个成员函数的实现之前,我们首先分析WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked和moveInputMethodDialogsLocked,它们是两个基本的操作,其中:
& & & & D. &成员函数findDesiredInputMethodWindowIndexLocked用来查找输入法窗口在窗口堆栈的正确位置,这个位置刚好就是在需要显示输入法窗口的窗口在窗口堆栈中的上一个位置。
& & & & E. 成员函数moveInputMethodDialogsLocked用来将移动输入法对话框移动到输入法窗口的上面去。
& & & & 接下来我们开始分析上述五个函数的实现。
& & & & 1. 计算输入法窗口在窗口堆栈中的位置
& & & & 输入法窗口在窗口堆栈中的位置是通过调用WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked来获得的,它首先找到需要显示输入法的窗口在窗口堆栈中的位置,然后再将这个位置加1,就可以得到输入法窗口在窗口堆栈中的位置。
& & & & WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中,它的实现比较长,我们分段来:
public class WindowManagerService extends IWindowManager.Stub &
& & & & implements Watchdog.Monitor { &
& & ...... &
& & int findDesiredInputMethodWindowIndexLocked(boolean willMove) { &
& & & & final ArrayList&WindowState& localmWindows = mW &
& & & & final int N = localmWindows.size(); &
& & & & WindowState w = &
& & & & int i = N; &
& & & & while (i & 0) { &
& & & & & & i--; &
& & & & & & w = localmWindows.get(i); &
& & & & & & ...... &
& & & & & & if (canBeImeTarget(w)) { &
& & & & & & & & ...... &
& & & & & & & & // Yet more tricksyness! &If this window is a &starting& &
& & & & & & & & // window, we do actually want to be on top of it, but &
& & & & & & & & // it is not -really- where input will go. &So if the caller &
& & & & & & & & // is not actually looking to move the IME, look down below &
& & & & & & & & // for a real window to target... &
& & & & & & & & if (!willMove &
& & & & & & & & & & & & && w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING &
& & & & & & & & & & & & && i & 0) { &
& & & & & & & & & & WindowState wb = localmWindows.get(i-1); &
& & & & & & & & & & while (i & 1 && wb.mAppToken == w.mAppToken && !canBeImeTarget(wb)) { &
& & & & & & & & & & & & i--; &
& & & & & & & & & & & & wb = localmWindows.get(i-1); &
& & & & & & & & & & } &
& & & & & & & & & & if (wb.mAppToken == w.mAppToken && canBeImeTarget(wb)) { &
& & & & & & & & & & & & i--; &
& & & & & & & & & & & & w = &
& & & & & & & & & & } &
& & & & & & & & } &
& & & & & & & & &
& & & & & & } &
& & & & } &
& & & & mUpcomingInputMethodTarget = &
& & & & 这段代码从上到下遍历WindowManagerService服务内部的窗口堆栈,即WindowManagerService类的成员变量mWindows所描述的一个ArrayList。如果发现有一个窗口是可见的,并且需要显示输入法窗口,那么整个查找过程就会结束。检查一个窗口是否可见以及需要显示输入法窗口是通过调用WindowManagerService类的成员函数canBeImeTarget来实现的。最后得到的需要显示输入法的窗口就使用WindowState对象w中,这个WindowState对象w接下来还会保存在WindowManagerService类的成员变量mUpcomingInputMethodTarget中,表示它即将要成为输入法窗口的目标窗口。
& & & & 参数willMove表示调用者计算输入法窗口在窗口堆栈中的位置的目的。如果它的值等于true,那么就说明调用者获得了输入法窗口在窗口堆栈中的位置之后,接下来就会将输入法窗口移动到需要显示输入法窗口的窗口的上面去,否则的话,就说明调用者只是为了知道输入法窗口在窗口堆栈中的位置,而不打算移动输入法窗口。
& & & & 在从上到下查找需要显示输入法的窗口的过程中,如果找到一个WindowState对象w,它所描述的窗口需要显示输入法窗口,但是这个窗口其实是一个Activity窗口的启动窗口,即该WindowState对象w的成员变量mAttrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于WindowManager.LayoutParams.TYPE_APPLICATION_STARTING,那么由于调用WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked的目的不是用来移动输入法窗口,而是用来查找输入法窗口在窗口堆栈中的确切位置,因此就不能前面所找到的启动窗口看作是一个需要输入法的窗口,因为这个启动窗口只是Activity窗口在显示过程中出现的一个临时窗口。在这种情况下,这段代码就会继续沿着窗口堆栈往下查找另外一个窗口,该窗口一方面是需要显示输入法窗口的,另一方面要与前面所找到的启动窗口对应的是同一个窗口令牌的。如果能找到这样的一个窗口,那么就会将用来描述它的一个WindowState对象wb保存在变量w中。如果找不到这样的一个窗口,那么这段代码就会继续沿着窗口堆栈往下查找另外一个需要显示输入法的窗口。
& & & & 我们继续往下阅读代码:
if (willMove && w != null) { &
& & final WindowState curTarget = mInputMethodT &
& & if (curTarget != null && curTarget.mAppToken != null) { &
& & & & // Now some fun for dealing with window animations that &
& & & & // modify the Z order. &We need to look at all windows below &
& & & & // the current target that are in this app, finding the highest &
& & & & // visible one in layering. &
& & & & AppWindowToken token = curTarget.mAppT &
& & & & WindowState highestTarget = &
& & & & int highestPos = 0; &
& & & & if (token.animating || token.animation != null) { &
& & & & & & int pos = 0; &
& & & & & & pos = localmWindows.indexOf(curTarget); &
& & & & & & while (pos &= 0) { &
& & & & & & & & WindowState win = localmWindows.get(pos); &
& & & & & & & & if (win.mAppToken != token) { &
& & & & & & & & & & &
& & & & & & & & } &
& & & & & & & & if (!win.mRemoved) { &
& & & & & & & & & & if (highestTarget == null || win.mAnimLayer & &
& & & & & & & & & & & & & & highestTarget.mAnimLayer) { &
& & & & & & & & & & & & highestTarget = &
& & & & & & & & & & & & highestPos = &
& & & & & & & & & & } &
& & & & & & & & } &
& & & & & & & & pos--; &
& & & & & & } &
& & & & } &
& & & & if (highestTarget != null) { &
& & & & & & ...... &
& & & & & & if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) { &
& & & & & & & & // If we are currently setting up for an animation, &
& & & & & & & & // hold everything until we can find out what will happen. &
& & & & & & & & mInputMethodTargetWaitingAnim = &
& & & & & & & & mInputMethodTarget = highestT &
& & & & & & & & return highestPos + 1; &
& & & & & & } else if (highestTarget.isAnimating() && &
& & & & & & & & & & highestTarget.mAnimLayer & w.mAnimLayer) { &
& & & & & & & & // If the window we are currently targeting is involved &
& & & & & & & & // with an animation, and it is on top of the next target &
& & & & & & & & // we will be over, then hold off on moving until &
& & & & & & & & // that is done. &
& & & & & & & & mInputMethodTarget = highestT &
& & & & & & & & return highestPos + 1; &
& & & & & & } &
& & & & } &
& & & & 这段代码用来处理一种特殊情况,即参数willMove的值等于true,并且前面找到了一个需要显示输入法的窗口w,但是当前输入法窗口已经存在一个目标窗口,并且该目标窗口正在切换的过程中。在这种情况下,调用WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked的函数就需要等到当前输入法窗口的目标窗口的切换过程结束之后,再将输入法窗口移动到窗口w的上面去,换句话说,就是要保持输入法窗口在它当前的目标窗口的上面,直到它当前的目标窗口的切换过程结束为止。这样WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked就需要找到当前输入法窗口的目标窗口在窗口堆栈中的位置,然后再将该位置加1后返回给调用者。
& & & & 当WindowManagerService类的成员变量mInputMethodTarget的值不等于null,并且它描述的是一个Activity窗口时,即它的成员变量mAppToken的值不等于null时,那么就说明当前输入法窗口已经存在一个目标窗口,而这个目标窗口就是使用WindowManagerService类的成员变量mInputMethodTarget所指向的一个WindowState对象来描述的。接下来这段代码就检查该目标窗口是否正在切换的过程中,即是否正在显示切换动画。如果是的话,那么WindowState对象curTarget的成员变量animating的值就会等于true,或者另外一个成员变量animation的值不等于null,这时候就需要在与该目标窗口所对应的窗口令牌token所描述的一组窗口中,找到一个Z轴位置最大的并且不是已经被移除的窗口。WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked的调用者最后就是需要将输入法窗口移动到这个Z轴位置最大的并且不是已经被移除的窗口的上面的。
& & & & 一个窗口的Z轴位置是记录在用描述它的一个WindowState对象的成员变量mAnimLayer中的,而它是否是已经被移除是记录在这个WindowState对象的成员变量mRemoved中的,因此,如果在窗口令牌token所描述的一组WindowSate对象中,能找到一个WindowSate对象,它的成员变量mAnimLayer的值最大,并且它的成员变量mRemoved不等于true,那么这段代码就会将它保存在变量highestTarget中,并且将它描述的窗口在窗口堆栈中的位置保存在变量highestPos中。
& & & & 经过前面的一系列计算之后,如果变量highestTarget的值不等于null,那么就说明我们碰到前面所说的特殊的情况,这时候又要分为两种情况来讨论。
& & & & 第一种情况是当前输入法窗口的目标窗口即将要进入到切换过程,但是这个切换过程尚开始,即WindowManagerService类的成员变量mNextAppTransition的值不等于WindowManagerPolicy.TRANSIT_UNSET。这时候就需要将WindowManagerService类的成员变量mInputMethodTargetWaitingAnim的值设置为true,表示当前输入法窗口的目标窗口正在等待进入切换动画中,并且需要将WindowManagerService类的成员变量mInputMethodTarget修正为变量highestTarget所描述的一个WindowState对象,因为这个WindowState对象才是真正用来描述当前输入法窗口的目标窗口的。
& & & & 第二种情况是当前输入法窗口的目标窗口已经处于切换的过程了,即变量highestTarget所描述的一个WindowState对象的成员函数isAnimating的返回值为true,并且该目标窗口的Z轴位置大于前面所找到的需要显示输入法窗口的窗口的Z轴,即变量highestTarget所描述的一个WindowState对象的成员变量mAnimLayer的值大于变量w所描述的一个WindowState对象的成员变量mAnimLayer的值。这时候就需要将WindowState对象highestTarget所描述的窗口维持为当前输入法窗口的目标窗口,即将WindowManagerService类的成员变量mInputMethodTarget设置为变量highestTarget,直到WindowState对象highestTarget所描述的窗口的切换过程结束为止。
& & & &上述两种情况最后都需要将WindowState对象highestTarget所描述的窗口在窗口堆栈中的位置highestPos加1,然后再返回给调用者,以便调用者接下来可以输入法窗口移动在窗口堆栈的第(highestPos+1)个位置上。
& & & & 如果我们没有碰到前面所说的特殊的情况,那么WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked就会继续往下执行:
if (w != null) { &
& & if (willMove) { &
& & & & ...... &
& & & & mInputMethodTarget = &
& & & & if (w.mAppToken != null) { &
& & & & & & setInputMethodAnimLayerAdjustment(w.mAppToken.animLayerAdjustment); &
& & & & } else { &
& & & & & & setInputMethodAnimLayerAdjustment(0); &
& & & & } &
& & return i+1; &
& & & & 如果变量w的值不等于null,那么就说明WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked在前面找到了一个需要显示输入法窗口的窗口。这个窗口是使用WindowState对象w来描述的,并且它在窗品堆栈中的位置记录在变量i中。这时候WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked就会执行以下三个操作:
& & & & A. 将WindowState对象w保存在WindowManagerService类的成员变量mInputMethodTarget中,以便WindowManagerService服务可以知道当前输入法窗口的目标窗口是什么。
& & & & B. 检查WindowState对象w描述的窗口是否是Activity窗口,即检查WindowState对象w的成员变量mAppToken的值是否不等于null。如果WindowState对象w描述的窗口是Activity窗口的话,那么就需要根据WindowState对象w的成员变量mAppToken所描述的一个AppWindowToken对象的成员变量animLayerAdjustment来调整系统中的输入法窗口以及输入法对话框的Z轴位置,即在系统中的输入法窗口以及输入法对话框的现有Z轴位置的基础上再增加一个调整量,这个调整量就保存在WindowState对象w的成员变量mAppToken所描述的一个AppWindowToken对象的成员变量animLayerAdjustment中。这个调整的过程是通过调用WindowManagerService类的成员函数setInputMethodAnimLayerAdjustment来实现的。如果WindowState对象w描述的窗口不是Activity窗口,那么就不需要调整系统中的输入法窗口以及输入法对话框的Z轴位置,但是仍然需要调用WindowManagerService类的成员函数setInputMethodAnimLayerAdjustment来将系统中的输入法窗口以及输入法对话框的Z轴位置调整量设置为0,即将WindowManagerService类的成员变量mInputMethodAnimLayerAdjustment的值设置为0。
& & & &C. 将变量i的值加1之后返回给调用者,以便调用者可以将系统中的输入法窗口移动到窗口堆栈中的第(i+1)个位置上。
& & & & 如果变量w的值不等于null,那么就说明WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked在前面没有找到一个需要显示输入法窗口的窗口,我们继续往下阅读它的代码,以便可以了解它是如何处理这种情况的:
& & & & if (willMove) { &
& & & & & & ...... &
& & & & & & mInputMethodTarget = &
& & & & & & setInputMethodAnimLayerAdjustment(0); &
& & & & } &
& & & & return -1; &
& & ...... &
& & & &WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked对在前面没有找到一个需要显示输入法窗口的窗口的情况的处理很简单。它判断参数willMove的值是否等于true。如果等于true的话,那么就会将WindowManagerService类的成员变量mInputMethodTarget的值设置为null,并且调用WindowManagerService类的成员函数setInputMethodAnimLayerAdjustment来将系统中的输入法窗口以及输入法对话框的Z轴位置调整量设置为0。这实际上是用来通知WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked的调用者,系统当前没有需要显示输入法窗口的窗口。
& & & & 最后,WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked返回一个-1值给调用者,也是表明系统当前没有需要显示输入法窗口的窗口。
& & & & 2. 移动输入法对话框移动到输入法窗口的上面
& & & & 系统中的输入法对话框是需要位于输入法窗口的上面的,因此,我们就需要有一个函数来将输入法对话框移动到输入法窗口的上面去。这个函数就是WindowManagerService类的成员函数moveInputMethodDialogsLocked,它的实现如下所示:
public class WindowManagerService extends IWindowManager.Stub &
& & & & implements Watchdog.Monitor { &
& & ...... &
& & void moveInputMethodDialogsLocked(int pos) { &
& & & & ArrayList&WindowState& dialogs = mInputMethodD &
& & & & final int N = dialogs.size(); &
& & & & ...... &
& & & & for (int i=0; i&N; i++) { &
& & & & & & pos = tmpRemoveWindowLocked(pos, dialogs.get(i)); &
& & & & } &
& & & & ...... &
& & & & if (pos &= 0) { &
& & & & & & final AppWindowToken targetAppToken = mInputMethodTarget.mAppT &
& & & & & & if (pos & mWindows.size()) { &
& & & & & & & & WindowState wp = mWindows.get(pos); &
& & & & & & & & if (wp == mInputMethodWindow) { &
& & & & & & & & & & pos++; &
& & & & & & & & } &
& & & & & & } &
& & & & & & ...... &
& & & & & & for (int i=0; i&N; i++) { &
& & & & & & & & WindowState win = dialogs.get(i); &
& & & & & & & & win.mTargetAppToken = targetAppT &
& & & & & & & & pos = reAddWindowLocked(pos, win); &
& & & & & & } &
& & & & & & ...... &
& & & & & & &
& & & & } &
& & & & for (int i=0; i&N; i++) { &
& & & & & & WindowState win = dialogs.get(i); &
& & & & & & win.mTargetAppToken = &
& & & & & & reAddWindowToListInOrderLocked(win); &
& & & & & & ...... &
& & & & } &
& & ...... &
& & & & 这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
& & & & 在调用WindowManagerService类的成员函数moveInputMethodDialogsLocked之前,必须要保证系统中的输入法窗口已经被移动到窗口堆栈的正确位置,即已经被移动到需要显示输入法窗口的窗口的上面。这时候参数pos描述的或者是输入法窗口在窗口堆栈中的位置,或者是输入法窗口在窗口堆栈的位置的上一个位置,即输入法对话框在窗口堆栈中的起始位置。参数pos的值还可以小于0,这时候就表示系统当前没有需要显示输入法窗口的窗口。
& & & & 在移动输入法对话框到输入法窗口的上面之前,首先要将输入法对话框从窗口堆栈中移除,以便接下来可以重新将它们插入到窗口堆栈中。系统中的输入法对话框都保存在WindowManagerService类的成员变量mInputMethodDialogs所描述的一个ArrayList中,通过调用WindowManagerService类的成员函数来tmpRemoveWindowLocked来移除保存在这个ArrayList中的每一个WindowState对象,就可以将系统中的输入法对话框从窗口堆栈中移除中。注意,将一个WindowState对象从窗口堆栈中移除之后,可能会影响参数pos的值。例如,如果参数pos的值大于被移除的WindowState对象原来在窗口堆栈中的位置值,那么在该WindowState对象被移除之后,参数pos的值就要相应地减少1,这样它才能正确地反映输入法窗口在窗口堆栈中的位置,或者输入法对话框在窗口堆栈中的起始位置。WindowManagerService类的成员函数来tmpRemoveWindowLocked在将一个WindowState对象从窗口堆栈中移除的过程中,会正确处理好参数pos的值,这一点可以参考前面Android窗口管理服务WindowManagerService组织窗口的方式分析一文。
& & & & 接下来,我们就分为两种情况来分析输入法对话框在窗口是如何移动到输入法窗口的上面去的。
& & & & 第一种情况是参数pos的值大于等于0,这表明系统当前存在一个需要显示输入法窗口的窗口,这个窗口是通过WindowManagerService类的成员变量mInputMethodTarget所指向的一个WindowState对象来描述的。
& & & & 前面提到,参数pos描述的或者是输入法窗口在窗口堆栈中的位置,或者是输入法对话框在窗口堆栈中的起始位置,我们首先要将它统一描述为输入法对话框在窗口堆栈中的起始位置。这时候就需要检查保存在窗口堆栈的第pos个位置的WindowState对象wp,是否就是WindowManagerService类的成员变量mInputMethodWindow所指向的那个WindowState对象。如果是的话,那么就说明参数pos描述的或者是输入法窗口在窗口堆栈中的位置,这时候将它的值增加1,就可以让它表示为输入法对话框在窗口堆栈中的起始位置。
& & & & 得到了输入法对话框在窗口堆栈中的起始位置pos之后,接下来只需要调用WindowManagerService类的成员函数reAddWindowLocked来依次地将保存在WindowManagerService类的成员变量mInputMethodDialogs所描述的一个ArrayList中的第i个WindowState对象保存在窗口堆栈中的第(pos+i)个以位置上即可,这样就可以将输入法对话框都移动到输入法窗口的上面去了。
& & & &注意,在移动的过程中,用来描述每一个输入法对话框的每一个WindowState对象的成员变量mTargetAppToken的值设置为WindowManagerService类的成员变量mInputMethodTarget所描述的一个WindowState对象的成员变量mAppToken的值,以便可以将输入法对话框和输入法窗口的目标窗口设置为同一个窗口。
& & & & 第二种情况是参数pos的值小于0,这表明系统当前不存在一个需要显示输入法窗口的窗口。这时候就需要根据输入法窗口自身的属性来将它们移动到窗口堆栈的合适的位置上去,这是通过调用WindowManagerService类的成员函数reAddWindowToListInOrderLocked来实现的。WindowManagerService类的成员函数reAddWindowToListInOrderLocked的实现可以参考前面Android窗口管理服务WindowManagerService组织窗口的方式分析一文,这里不再详细。
& & & & 注意,在移动的过程中,用来描述每一个输入法对话框的每一个WindowState对象的成员变量mTargetAppToken的值会被设置为null,这是因为系统当前不存在一个需要显示输入法窗口的窗口,即这时候每一个输入法对话框都没有目标窗口。
& & & & 理解了WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked和moveInputMethodDialogsLocked的实现之后,对WindowManagerService类的另外三个成员函数addInputMethodWindowToListLocked、adjustInputMethodDialogsLocked和moveInputMethodWindowsIfNeededLocked的实现就很有帮助,接下来我们就继续分析这三个成员函数的实现。
& & & & 3. 插入输入法窗口到需要显示输入法窗口的窗口上面
& & & & 插入输入法窗口到窗口堆栈的合适位置,使得它位于需要显示输入法窗口的窗口上面,这是通过调用WindowManagerService类的成员函数addInputMethodWindowToListLocked来实现的,它的实现如下所示:
public class WindowManagerService extends IWindowManager.Stub &
& & & & implements Watchdog.Monitor { &
& & ...... &
& & void addInputMethodWindowToListLocked(WindowState win) { &
& & & & int pos = findDesiredInputMethodWindowIndexLocked(true); &
& & & & if (pos &= 0) { &
& & & & & & win.mTargetAppToken = mInputMethodTarget.mAppT &
& & & & & & ...... &
& & & & & & mWindows.add(pos, win); &
& & & & & & mWindowsChanged = &
& & & & & & moveInputMethodDialogsLocked(pos+1); &
& & & & & & &
& & & & } &
& & & & win.mTargetAppToken = &
& & & & addWindowToListInOrderLocked(win, true); &
& & & & moveInputMethodDialogsLocked(pos); &
& & ...... &
& & & & 这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
& & & & 参数win描述的是要添加到窗口堆栈中去的输入法窗口。
& & & & WindowManagerService类的成员函数addInputMethodWindowToListLocked首先调用另外一个成员函数findDesiredInputMethodWindowIndexLocked来计算输入法窗口在窗口堆栈中的位置,并且保存在变量pos。
& & & & 如果变量pos的值大于等于0,那么就说明WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked在窗口堆栈中找到了一个合适的位置来放置输入法窗口,于是接下来就会参数win所描述的输入法窗口插入在WindowManagerService类的成员变量mWIndows所描述的窗口堆栈的第pos个位置上。由于系统中的输入法对话框要保持在输入法窗口的上面,因此,WindowManagerService类的成员函数addInputMethodWindowToListLocked还需要继续调用另外一个成员函数moveInputMethodDialogsLocked来将系统中的输入法对话框在窗口堆栈中的起始位置设置为(pos+1)。
& & & & 还有一个地方需要注意的是,前面在调用WindowManagerService类的成员函数addInputMethodWindowToListLocked来计算输入法窗口在窗口堆栈中的位置的时候,已经将用来描述需要显示输入法窗口的Activity窗口的一个WindowState对象保存了WindowManagerService类的成员变量mInputMethodTarget中,因此,这里就需要这个WindowState对象的成员变量mAppToken所指向的一个AppWindowToken对象保存在用来描述输入法窗口的WindowState对象的win的成员变量mTargetAppToken中,以便WindowManagerService服务可以知道当前输入法窗口的目标窗口是什么。
& & & & 如果变量pos的值小于0,那么就说明WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked没有找一个需要输入法窗口的窗口,因此,这时候就需要调用另外一个成员函数addWindowToListInOrderLocked来将参数win所描述的输入法窗口插入到窗口堆栈中去。WindowManagerService类的成员函数addWindowToListInOrderLocked会根据要目标窗口所对应的窗口令牌在窗口令牌列表中的位置以及是否在窗口堆栈中存在其它窗口等信息来在窗口堆栈中找到一个合适的前位置来放置目标窗口,它的具体实现可以参考前面Android窗口管理服务WindowManagerService组织窗口的方式分析一文。将参数win所描述的输入法窗口插入到窗口堆栈中去之后,WindowManagerService类的成员函数addInputMethodWindowToListLocked还需要继续调用另外一个成员函数moveInputMethodDialogsLocked来调整系统中的输入法对话框。
& & & & 注意,在调用WindowManagerService类的成员函数moveInputMethodDialogsLocked的时候,传递进去的参数pos的值等于-1,这时候WindowManagerService类的成员函数moveInputMethodDialogsLocked就不是直接调整输入法对话框在窗口堆栈中的位置的,而是调用另外一个成员函数reAddWindowToListInOrderLocked来调整的。
& & & & 还有另外一个地方需要注意的是,由于前面在调用WindowManagerService类的成员函数findDesiredInputMethodWindowIndexLocked的时候,没有找到一个需要输入法窗口的窗口,因此,这里就需要将参数win所描述的一个WindowState对象的成员变量mTargetAppToken的值设置为null,以便WindowManagerService服务可以知道当前输入法窗口的没有目标窗口。
& & & & 4. 调整输入法对话框在窗口堆栈的位置
& & & & 一旦系统中存在需要显示输入法窗口的窗口,那么就需要系统中的输入法对话框在窗口堆栈中的位置,使得它们放置在输入法窗品的上面,这是通过调用WindowManagerService类的成员函数adjustInputMethodDialogsLocked来实现的,如下所示:
public class WindowManagerService extends IWindowManager.Stub &
& & & & implements Watchdog.Monitor { &
& & ...... &
& & void adjustInputMethodDialogsLocked() { &
& & & & moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true)); &
& & ...... &
& & & & 这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
& & & & WindowManagerService类的成员函数adjustInputMethodDialogsLocked的实现很简单,它首先调用成员函数findDesiredInputMethodWindowIndexLocked来找到输入法窗口在窗口堆栈中的位置,然后再调用成员函数moveInputMethodDialogsLocked来将输入法对话框保存在这个位置之上。
& & & & 5. 调整输入法窗口在窗口堆栈的位置
& & & & 当系统中的窗口布局发生了变化之后,例如,当前获得焦点的窗口发生了变化,或者新增了一个窗口,那么都可能需要调整输入法窗口在窗口堆栈中的位置,以便它可以痊于需要显示输入法窗口的窗口的上面,这是通过调用WindowManagerService类的成员函数moveInputMethodWindowsIfNeededLocked来实现的,如下所示:
public class WindowManagerService extends IWindowManager.Stub &
& & & & implements Watchdog.Monitor { &
& & ...... &
& & boolean moveInputMethodWindowsIfNeededLocked(boolean needAssignLayers) { &
& & & & final WindowState imWin = mInputMethodW &
& & & & final int DN = mInputMethodDialogs.size(); &
& & & & if (imWin == null && DN == 0) { &
& & & & & & &
& & & & } &
& & & & int imPos = findDesiredInputMethodWindowIndexLocked(true); &
& & & & if (imPos &= 0) { &
& & & & & & // In this case, the input method windows are to be placed &
& & & & & & // immediately above the window they are targeting. &
& & & & & & // First check to see if the input method windows are already &
& & & & & & // located here, and contiguous. &
& & & & & & final int N = mWindows.size(); &
& & & & & & WindowState firstImWin = imPos & N &
& & & & & & & & & & ? mWindows.get(imPos) : &
& & & & & & // Figure out the actual input method window that should be &
& & & & & & // at the bottom of their stack. &
& & & & & & WindowState baseImWin = imWin != null &
& & & & & & & & & & ? imWin : mInputMethodDialogs.get(0); &
& & & & & & if (baseImWin.mChildWindows.size() & 0) { &
& & & & & & & & WindowState cw = baseImWin.mChildWindows.get(0); &
& & & & & & & & if (cw.mSubLayer & 0) baseImWin = &
& & & & & & } &
& & & & & & if (firstImWin == baseImWin) { &
& & & & & & & &

我要回帖

更多关于 实验技术与管理 的文章

 

随机推荐