download:计算机根底更适宜程序员的编程必备基础知识2022新版

Android完满解决了输入框被遮挡的问题。

前段时间呈现了webview的输入框被软键盘挡住的问题。通过解决,顺便做了一些输入框栏目的汇总。
在失常状况下,输入框被阻塞
个别状况下,输入框被输入法屏蔽。个别能够通过设置softInputMode为window来解决。
window.getAttributes()。softInputMode = WindowManager。LayoutParams.XXX
有三种状况:
(1)SOFT_INPUT_ADJUST_RESIZE:布局将由软键盘置顶。
(2)SOFT_INPUT_ADJUST_PAN:只向上推输入框(即只向上推一部分间隔)
(3)SOFT_INPUT_ADJUST_NOTHING:什么都不做(就是什么都不做)
SOFT_INPUT_ADJUST_PAN和SOFT_INPUT_ADJUST_RESIZE的区别在于,SOFT_INPUT_ADJUST_PAN只是把输入框放在下面,而SOFT_INPUT_ADJUST_RESIZE会把整个布局放在下面,会有一种输入框显示和暗藏时布局高度动态变化的视觉效果。
如果你的输入框梗塞,个别能够通过设置SOFT_INPUT_ADJUST_PAN来解决。如果你的输入框没有被屏蔽,然而软键盘弹出来了,布局就会被推上去。如果不想上推,能够设置SOFT_INPUT_ADJUST_NOTHING。
SoftInputMode是window的属性。你在Mainifest中为Activity设置,也为window设置。如果是Dialog或者popupwindow,能够通过getWindow()间接设置。失常状况下,设置该属性能够解决问题。
Webview的输入框被阻止
然而,如果Webview的输入框被阻止,则设置该属性可能有效。
在Webview的状况下,SOFT_INPUT_ADJUST_PAN将不起作用。而后,如果是Webview,并且你依然关上沉迷模式,则SOFT_INPUT_ADJUST_RESIZE和SOFT_INPUT_ADJUST_PAN都将不起作用。
我去查资料,发现这是经典的5497期。许多在线解决方案都是通过androidbug 5497解决办法。这个解决办法很好找,我就不贴了。原理是监控视图树的变动,而后计算高度,再动静设置。这个计划的确能够解决问题,然而我感觉这个操作有很多不可控因素。说白了,某些模型或者状况下会有其余bug,会导致你写一些判断逻辑来应答非凡状况。
解决办法是不必沉迷模式,而后用SOFT_INPUT_ADJUST_RESIZE就能够解决。然而有时候这个窗口显示的时候须要沉迷模式,特地是一些适宜刘海温和水滴屏的场景。
setSystemUiVisibility(视图。SYSTEM UI FLAG _ LAYOUT _全屏)
复制代码
我的第一反馈是扭转布局。
窗户。setLayout(ViewGroup。LayoutParams.MATCH_PARENT,ViewGroup。layout params . WRAP _ CONTENT);
复制代码
这样能够失常向上推子弹框,然而控件外部也应用了WRAP_CONTENT,导致SOFT_INPUT_ADJUST_RESIZE扭转了布局,而后就不能复原原样了,也就是会变形。而SOFT_INPUT_ADJUST_RESIZE如果WRAP_CONTENT不应用固定高度也是有效的。
没关系,还有方法。在MATCH_PARENT的状况下,咱们把fitSystemWindows设置为true,然而这个属性会在顶部让出一个平安间隔,成果就是向下偏移状态栏的高度。
在这种状况下,能够设置边距来解决顶部偏移的问题。
params . top margin = status height = = 0?-120:-status height;
view . setlayoutparams(params);
复制代码
此操作能够解决顶部偏移的问题,但布局可能会被垂直压缩。这个我还没有齐全测试过。我感觉你的布局高度固定的话,可能不会受影响。然而我的webview是自适应的,webview里的内容也是自适应的,所以我呈现了版面垂直压缩的状况。例如,视图的高度是800,状态栏的高度是100。设置fitSystemWindows后,成果是视图显示700,paddingTop 100。对于这种成果,设置params.topMargin =-100,而后视图显示700和paddingTop 100。它能够在视觉上打消顶部偏移,但没有解决布局的垂直压缩问题。
所以最终的解决方案是扭转WindowInsets的Rect(我稍后会解释这意味着什么)
具体操作是将以下两种办法增加到您的自定义视图中
@笼罩
public void setFitsSystemWindows(boolean fitSystemWindows){
fitSystemWindows = true
super . setfitssystemwindows(fitSystemWindows);
}

@笼罩
受爱护的布尔fitSystemWindows(矩形插入){
Log.v("mmp ","测试顶部偏移量:"+inserts . top);
insets . top = 0;
返回super . fitsystemwindows(insets);
}
复制代码
总结
WebView+沉迷模式下解决输入框被软键盘遮挡问题的步骤:

Window.getattributes()。软输出模式设置为软输出调整大小。
将视图的fitSystemWindows设置为true,我的webview中的输入框被屏蔽,因而设置webview而不是父视图。
重写fitSystemWindows办法,并将insets的顶部设置为0。

窗口镶嵌
依照下面3个步骤,就能够解决webview输入框梗塞的问题了,然而如果你想晓得为什么,原理是什么。你须要理解WindowInsets。咱们的沉迷式操作setSystemUiVisibility和设置fitSystemWindows属性,以及重写fitSystemWindows办法,都与WindowInsets无关。
WindowInsets是利用于窗口的零碎视图的插入。例如状态栏STATUS_BAR和导航栏NAVIGATION_BAR。会被视图援用,所以咱们要做的具体操作,就是操作视图。
还有一个重要的问题。不同版本的WindowInsets有肯定的差别。Android28、Android29和Android30都有肯定的差别。比方29里有一个android.graphics.Insets类,28里没有。咱们能够在29中获取而后查看top、left等四个属性,然而只能查看。是最终的,不能间接拿进去批改。
不过这段WindowInsets其实能够讲很多内容,当前能够拿进去独自做一篇。上面简略介绍一下。你只须要指定咱们如何解决上述问题的原理,就是这个货色。
源代码剖析
在理解了WindowInsets之后,我将带您简略浏览一下setFitsSystemWindows的源代码。置信你会印象更粗浅。
public void setFitsSystemWindows(boolean fitSystemWindows){
setFlags(fitSystemWindows?FITS_SYSTEM_WINDOWS : 0,FITS SYSTEM WINDOWS);
}
复制代码
它只是在这里设置了一个标记。如果你看看它的正文(我不会贴在这里),它会带你到受爱护的布尔fitsystemwindows (rectinserts)的办法(我稍后会说为什么我去这个办法)
@已弃用
受爱护的布尔fitSystemWindows(矩形插入){
if((mprivateflags 3 & pflag 3 APPLYING INSETS)= = 0){
if (insets == null) {
//依据定义,Null insets曾经被应用。
//此调用无奈利用插入,因为没有可利用的插入,
//所以返回false。
返回false
}
//如果咱们不在分派较新的apply insets调用的过程中,
//这意味着咱们不在兼容门路中。差遣到新的
//利用insets门路并从那里获取内容。
尝试{
mprivateflags 3 | = pf lag 3 FITTING SYSTEM _ WINDOWS;
返回dispatchapplywindowsets(new window insets(insets))。is consumed();
}最初{
mprivateflags 3 & = ~ pflag 3 FITTING SYSTEM _ WINDOWS;
}
}否则{
//咱们是从较新的利用插入门路调用的。
//执行规范回退行为。
返回fitSystemWindowsInt(insets);
}
}
复制代码
(mprivateflags 3 & pflag 3 applying inserts)= = 0这个判断前面会简略形容。你只须要晓得失常状况是执行fitSystemWindowsInt(insets)。
还有fitSystemWindows叫什么?向前跳转,能够看到调用了onapplywindowsets,而onapplywindowsets是由dispatchApplyWindowInsets调用的。其实这里没必要往前看。可见这是一种分配机制。没错,这就是WindowInsets的散发机制,相似于View的事件散发机制。向前看被viewgroup称为。如前所述,这里不详细描述windowinserts,所以这里也不开展windowinserts的散发机制。你只须要先晓得有这么一个货色。
public window insets dispatchapplywindowsets(window insets insets){
尝试{
mprivateflags 3 | = pf lag 3 APPLYING INSETS;
if (mListenerInfo!= null & & mlistenerinfo . monapplywindowsetslistener!= null) {
返回mlistenerinfo . monapplywindowsetslistener . onapplywindowsets(this,insets);
}否则{
返回onapplywindowsets(insets);
}
}最初{
mprivateflags 3 & = ~ pflag 3 APPLYING INSETS;
}
}
复制代码
假如mPrivateFlags3为0,pflag3 applying inserts为20,0和20做OR运算,也就是20。而后判断是否存在mOnApplyWindowInsetsListener。这个听者是不是咱们在里面做过。
setonapplywindowinsets listener(new onapplywindowsinsetslistener(){
@笼罩
ApplyWindowInsets上的公共WindowInsets(视图v,WindowInsets insets) {
......
返回insets
}
});
复制代码
假如没有,调用onApplyWindowInsets。
ApplyWindowInsets上的公共WindowInsets(WindowInsets insets){
if((mprivateflags 3 & pflag 3 FITTING SYSTEM _ WINDOWS)= = 0){
//咱们不是从对fitSystemWindows的间接调用中被调用的,
//调用它作为后备,以防咱们在重写它的类中
//并且具备要执行的逻辑。
if(fitSystemWindows(insets . getsystemwindowinsetsarrect()){
返回insets . consumesystemwindowinsets();
}
}否则{
//咱们是从对fitSystemWindows的间接调用中被调用的。
if(fitSystemWindowsInt(insets . getsystemwindowinsetsarrect()){
返回insets . consumesystemwindowinsets();
}
}
返回insets
}
复制代码
rivate flags 3 & pflag 3 fitting system _ windows是20和40的AND运算,也就是0,所以调用fitSystemWindows。
而fitSystemWindows(mprivateflags 3 & pflag 3 applying inserts)= = 0)是20和20的And运算,不是0,所以调用fitSystemWindowsInt。
在剖析的这一点上,咱们须要联合下面的思路来解决bug。事实上,咱们须要获取rectinserts参数并批改它的top。
setonapplywindowinsets listener(new onapplywindowsinsetslistener(){
@笼罩
ApplyWindowInsets上的公共WindowInsets(视图v,WindowInsets insets) {
......
返回insets
}
});
复制代码
setOnApplyWindowInsetsListener回调中的Insets能够失去类android.graphics.Insets,然而只能看到top是什么,没有方法批改。当然,你能够看看下面是什么,而后像我下面那样设置,边距。
params . top margin =-top;

复制代码
如果你的布局没有垂直变形,也没多大关系。如果变形了,就不能用这个办法了。从源代码来看,这个过程次要波及三种办法。咱们能够看到,最好的终点是fitSystemWindows。因为onApplyWindowInsets和dispatchApplyWindowInsets是散发机制的办法,如果要从这里开始,可能会呈现过程凌乱等问题。
所以咱们这样做是为了解决fitSystemWindows = true的顶部偏移量。
@笼罩
public void setFitsSystemWindows(boolean fitSystemWindows){
fitSystemWindows = true
super . setfitssystemwindows(fitSystemWindows);
}

@笼罩
受爱护的布尔fitSystemWindows(矩形插入){
Log.v("mmp ","测试顶部偏移量:"+inserts . top);
insets . top = 0;
返回super . fitsystemwindows(insets);
}
复制代码
倒退
以上曾经解决了问题,这里是拓展思路。
fitSystemWindows办法受到爱护,这将导致您重写它。然而如果咱们不能通过继承来实现这个过程呢?
其实这是一个解决问题的思路。咱们须要晓得为什么会这样,原理是什么。比方这里咱们晓得这个fitSystemWindows造成的顶部偏移是insets的顶部造成的。这个你得先晓得,不然你就不晓得怎么解决这个问题了。只能去网上找他人的办法,一个一个试。那我怎么晓得是insets的顶部?这须要肯定的浏览源代码的能力,也要晓得这个货色的设计思路是什么。当你晓得有这样一个货色的时候,想方法去获取它,扭转数据。
这里,咱们应用继承受爱护办法的属性来获取insets。如果这个过程无奈通过传承实现呢?比方这是因为fitSystemWindows是view的办法,而咱们的自定义视图只是继承了view。如果是外部写的实现这个操作的类呢?
在这种状况下,通常有两种操作与万金油进行比拟:

你写一个类来继承它的类,而后在你写的类中批改insets,而后通过反射注入View。
动静代理

事实上,我最后的想法是应用动静代理,所以我立刻推出了代码。
公共类WebViewProxy实现InvocationHandler {

公有对象relObj

公共对象new proxy instance(Object Object){
this.relObj = object
返回proxy . newproxyinstance(rel obj . getclass()。getClassLoader(),relObj.getClass()。getInterfaces(),this);
}

@笼罩
公共对象调用(对象代理、办法办法、对象[]参数)抛出Throwable {
尝试{
if ("fitSystemWindows "。equals(method.getName()) && args!= null && args.length == 1){
Log.v("mmp ","测试代理成果"+args);
}
}catch(异样e){
e . printstacktrace();
}
返回代理;
}

}
复制代码
webview proxy proxy = new webview proxy();
View View proxy =(View)proxy . newproxyinstance(mWebView);
复制代码
而后我发现fitSystemWindows不是接口办法,白费力气。然而如果fitSystemWindows是一个接口办法,我能够通过动静代理增加反射来批改这里的insets。尽管我不会用,但也是个想法。最初发现能够间接重写这个办法,反而把问题复杂化了。