`
810364804
  • 浏览: 846695 次
文章分类
社区版块
存档分类
最新评论

Android WebView开发问题及优化汇总

 
阅读更多

我们在native与网页相结合开发的过程中,难免会遇到关于WebView一些共通的问题。就我目前开发过程中遇到的问题以及最后得到的优化方案都将在这里列举出来。有些是老生常谈,有些则是个人摸索得出解决方法。下面就是整理得到的些干货。

1.加快HTML网页装载完成的速度

默认情况html代码下载到WebView后,webkit开始解析网页各个节点,发现有外部样式文件或者外部脚本文件时,会异步发起网络请求下载文件,但如果在这之前也有解析到image节点,那势必也会发起网络请求下载相应的图片。在网络情况较差的情况下,过多的网络请求就会造成带宽紧张,影响到css或js文件加载完成的时间,造成页面空白loading过久。解决的方法就是告诉WebView先不要自动加载图片,等页面finish后再发起图片加载。

故在WebView初始化时设置如下代码:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 7 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">int</span> () { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span>(Build.VERSION.SDK_INT &gt;= <span class="number" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">19</span>) { webView.getSettings().setLoadsImagesAutomatically(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">true</span>); } <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">else</span> { webView.getSettings().setLoadsImagesAutomatically(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">false</span>); } } </pre></td> </tr></tbody></table></figure>

同时在WebView的WebViewClient实例中的onPageFinished()方法添加如下代码:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">onPageFinished</span>(WebView view, String url) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span>(!webView.getSettings().getLoadsImagesAutomatically()) { webView.getSettings().setLoadsImagesAutomatically(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">true</span>); } } </pre></td> </tr></tbody></table></figure>

从上面的代码,可以看出我们对系统API在19以上的版本作了兼容。因为4.4以上系统在onPageFinished时再恢复图片加载时,如果存在多张图片引用的是相同的src时,会只有一个image标签得到加载,因而对于这样的系统我们就先直接加载。

2.自定义出错界面

当WebView加载页面出错时(一般为404 NOT FOUND),安卓WebView会默认显示一个卖萌的出错界面。但我们怎么能让用户发现原来我使用的是网页应用呢,我们期望的是用户在网页上得到是如原生般应用的体验,那就先要从干掉这个默认出错页面开始。当WebView加载出错时,我们会在WebViewClient实例中的onReceivedError()方法接收到错误,我们就在这里做些手脚:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">onReceivedError</span> (WebView view, <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">int</span> errorCode, String description, String failingUrl) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">super</span>.onReceivedError(view, errorCode, description, failingUrl); loadDataWithBaseURL(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">null</span>, <span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">""</span>, <span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"text/html"</span>, <span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"utf-8"</span>, <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">null</span>); mErrorFrame.setVisibility(View.VISIBLE); } </pre></td> </tr></tbody></table></figure>

从上面可以看出,我们先使用loadDataWithBaseURL清除掉默认错误页内容,再让我们自定义的View得到显示(mErrorFrame为蒙在WebView之上的一个LinearLayout布局,默认为View.GONE)。

3.是否存在滚动条

当我们做类似上拉加载下一页这样的功能的时候,页面初始的时候需要知道当前WebView是否存在纵向滚动条,如果有则不加载下一页,如果没有则加载下一页直到其出现纵向滚动条。首先继承WebView类,在子类添加下面的代码:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">existVerticalScrollbar</span> () { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span> computeVerticalScrollRange() &gt; computeVerticalScrollExtent(); } </pre></td> </tr></tbody></table></figure>

computeVerticalScrollRange得到的是可滑动的最大高度,computeVerticalScrollExtent得到的是滚动把手自身的高,当不存在滚动条时,两者的值是相等的。当有滚动条时前者一定是大于后者的。

4.是否已滚动到页面底部

同样我们在做上拉加载下一页这样的功能时,也需要知道当前页面滚动条所处的状态,如果快到底部,则要发起网络请求数据更新网页。同样继承WebView类,在子类覆盖onScrollChanged方法,具体如下:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 7 8 9 10 11 12 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">protected</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">onScrollChanged</span>(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">int</span> newX, <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">int</span> newY, <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">int</span> oldX, <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">int</span> oldY) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">super</span>.onScrollChanged(newX, newY, oldX, oldY); <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span> (newY != oldY) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">float</span> contentHeight = getContentHeight() * getScale(); <span class="comment" style="margin: 0px; padding: 0px; color: rgb(147, 161, 161); font-style: italic;">// 当前内容高度下从未触发过, 浏览器存在滚动条且滑动到将抵底部位置</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span> (mCurrContentHeight != contentHeight &amp;&amp; newY &gt; <span class="number" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">0</span> &amp;&amp; contentHeight &lt;= newY + getHeight() + mThreshold) { <span class="comment" style="margin: 0px; padding: 0px; color: rgb(147, 161, 161); font-style: italic;">// TODO Something...</span> mCurrContentHeight = contentHeight; } } } </pre></td> </tr></tbody></table></figure>

上面mCurrContentHeight用于记录上次触发时的网页高度,用来防止在网页总高度未发生变化而目标区域发生连续滚动时会多次触发TODO,mThreshold是一个阈值,当页面底部距离滚动条底部的高度差<=这个值时会触发TODO。

5.远程网页需访问本地资源

当我们在WebView中加载出从web服务器上拿取的内容时,是无法访问本地资源的,如assets目录下的图片资源,因为这样的行为属于跨域行为(Cross-Domain),而WebView是禁止的。解决这个问题的方案是把html内容先下载到本地,然后使用loadDataWithBaseURL加载html。这样就可以在html中使用file:///android_asset/xxx.png的链接来引用包里面assets下的资源了。示例如下:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">private</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">loadWithAccessLocal</span>(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">final</span> String htmlUrl) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">new</span> Thread(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">new</span> Runnable() { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">run</span>() { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">try</span> { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">final</span> String htmlStr = NetService.fetchHtml(htmlUrl); <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span> (htmlStr != <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">null</span>) { TaskExecutor.runTaskOnUiThread(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">new</span> Runnable() { <span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">run</span>() { loadDataWithBaseURL(htmlUrl, htmlStr, <span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"text/html"</span>, <span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"UTF-8"</span>, <span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">""</span>); } }); <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span>; } } <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">catch</span> (Exception e) { Log.e(<span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"Exception:"</span> + e.getMessage()); } TaskExecutor.runTaskOnUiThread(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">new</span> Runnable() { <span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">run</span>() { onPageLoadedError(-<span class="number" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">1</span>, <span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"fetch html failed"</span>); } }); } }).start(); } </pre></td> </tr></tbody></table></figure>

上面有几点需要注意:

  • 从网络上下载html的过程应放在工作线程
  • html下载成功后渲染出html的步骤应放在UI主线程,不然WebView会报错
  • html下载失败则可以使用我们前面讲述的方法来显示自定义错误界面

6.ViewPager里非首屏WebView点击事件不响应

如果你的多个WebView是放在ViewPager里一个个加载出来的,那么就会遇到这样的问题。ViewPager首屏WebView的创建是在前台,点击时没有问题;而其他非首屏的WebView是在后台创建,滑动到它后点击页面会出现如下错误日志:


20955-20968/xx.xxx.xxx E/webcoreglue﹕ Should not happen: no rect-based-test nodes found

解决这个问题的办法是继承WebView类,在子类覆盖onTouchEvent方法,填入如下代码:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 7 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">onTouchEvent</span>(MotionEvent ev) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span> (ev.getAction() == MotionEvent.ACTION_DOWN) { onScrollChanged(getScrollX(), getScrollY(), getScrollX(), getScrollY()); } <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">super</span>.onTouchEvent(ev); } </pre></td> </tr></tbody></table></figure>

该方法的最先提出在WebView in ViewPager not receive user inputs

7.WebView硬件加速导致页面渲染闪烁

4.0以上的系统我们开启硬件加速后,WebView渲染页面更加快速,拖动也更加顺滑。但有个副作用就是,当WebView视图被整体遮住一块,然后突然恢复时(比如使用SlideMenu将WebView从侧边滑出来时),这个过渡期会出现白块同时界面闪烁。解决这个问题的方法是在过渡期前将WebView的硬件加速临时关闭,过渡期后再开启,代码如下:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span> (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB) { webview.setLayerType(View.LAYER_TYPE_SOFTWARE, <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">null</span>); } </pre></td> </tr></tbody></table></figure>

8.避免addJavaScriptInterface带来的安全问题

使用开源项目Safe Java-JS WebView Bridge可以很好替代addJavaScriptInterface方法,同时增加了异步回调等支持,并且不存在了安全风险。

9.WebView与上层父元素的TouchMove事件冲突

在开发过程中你可能会遇到这样一种情况。端里面使用ViewPager嵌套了多个WebView页面,同时某一个WebView中的页面元素需要响应TouchMove事件。正如下图所示的情景:

image

这时你就会发现上层(ViewPager)阻断了下层(WebView)接收TouchMove事件,即使你的WebView在TouchDown时返回true也无效,因为上层直接使用了onInterceptTouchEvent对后续的TouchMove进行了拦截。针对这个问题的解决,简单做法是在重写WebView onTouchEvent方法,如下:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">onTouchEvent</span>(MotionEvent ev) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> ret = <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">super</span>.onTouchEvent(ev); <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span> (mPreventParentTouch) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">switch</span> (ev.getAction()) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">case</span> MotionEvent.ACTION_MOVE: requestDisallowInterceptTouchEvent(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">true</span>); ret = <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">true</span>; <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">break</span>; <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">case</span> MotionEvent.ACTION_UP: <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">case</span> MotionEvent.ACTION_CANCEL: requestDisallowInterceptTouchEvent(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">false</span>); mPreventParentTouch = <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">false</span>; <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">break</span>; } } <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span> ret; } <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">preventParentTouchEvent</span> () { mPreventParentTouch = <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">true</span>; } </pre></td> </tr></tbody></table></figure>

代码控制的关键在于mPreventParentTouch这个变量,mPreventParentTouch默认为false,当用户touchdown页面元素时通知该WebView将mPreventParentTouch设置为true。示意代码如下:

<figure class="highlight JAVASCRIPT" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 7 8 9 10 11 12 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">&lt;script type=<span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"text/javascript"</span>&gt; document.getElementById(<span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"targetEle"</span>).addEventListener(<span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"touchstart"</span>, <span class="function" style="margin: 0px; padding: 0px;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">function</span><span class="params" style="margin: 0px; padding: 0px;">(ev)</span> {</span> HostApp.preventParentTouchEvent(); <span class="comment" style="margin: 0px; padding: 0px; color: rgb(147, 161, 161); font-style: italic;">// 通知WebView阻止祖先对其Touch事件的拦截</span> } ); document.getElementById(<span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"targetEle"</span>).addEventListener(<span class="string" style="margin: 0px; padding: 0px; color: rgb(42, 161, 152);">"touchmove"</span>, <span class="function" style="margin: 0px; padding: 0px;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">function</span><span class="params" style="margin: 0px; padding: 0px;">(ev)</span> {</span> <span class="comment" style="margin: 0px; padding: 0px; color: rgb(147, 161, 161); font-style: italic;">// todo something on this page</span> } ); <span class="xml" style="margin: 0px; padding: 0px;">&lt;/<span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">script</span>&gt;</span> </pre></td> </tr></tbody></table></figure>

关于web页面如何通知WebView(即调用Java方法)请参看第8条

刚提到了上面是一种简单的做法,并不能很好的解决手指滑动过快带来的误操作问题,即当用户快速地滑动时,还是有一定机率会出现ViewPager拦截TouchMove事件而发生了Tab切换而非页面元素做出了响应。要完美解决此问题,就要用到稍微复杂一点的方法(仅是整体消息传递流程复杂一点)。

首先假设在ViewPager之上还有一个父元素叫做ParentViewOnViewPager,当我们接收到页面preventParentTouchEvent通知时就先于ViewPager而进行拦截。如下:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><figcaption style="margin: 0px 0px 5px; padding: 0px; color: rgb(153, 153, 153); text-shadow: rgb(255, 255, 255) 0px 0px 1px;"><span style="margin: 0px; padding: 0px;">ParentViewOnViewPager.java</span></figcaption><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="class" style="margin: 0px; padding: 0px;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">class</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(181, 137, 0);">ParentViewOnViewPager</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">extends</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(181, 137, 0);">FrameLayout</span> {</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">private</span> MineWebView mDispatchWebView; <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">preventParentTouchEvent</span> (WebView view) { mDispatchWebView = (MineWebView)view; } <span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">onInterceptTouchEvent</span>(MotionEvent ev) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span> (ev.getAction() == MotionEvent.ACTION_MOVE &amp;&amp; mDispatchWebView != <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">null</span>) { mDispatchWebView.ignoreTouchCancel(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">true</span>); <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">true</span>; } <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">false</span>; } <span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">onTouchEvent</span>(MotionEvent ev) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">if</span> (mDispatchWebView != <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">null</span>){ <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">switch</span> (ev.getAction()) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">case</span> MotionEvent.ACTION_MOVE: mDispatchWebView.onTouchEvent(ev); <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">break</span>; <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">default</span>: mDispatchWebView.ignoreTouchCancel(<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">false</span>); mDispatchWebView.onTouchEvent(ev); mDispatchWebView = <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">null</span>; <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">break</span>; } <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">true</span>; } <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">super</span>.onTouchEvent(ev); } } </pre></td> </tr></tbody></table></figure>

即当ParentViewOnViewPager接收到通知时,发起TouchEvent拦截,将拦截到的Touch事件转嫁到装载页面的mDispatchWebView进行事件派发。这样就直接跳过了ViewPager这一层。这里需要注意的是当ParentViewOnViewPager发起拦截时,WebView会接收到一个TouchCancel事件,WebView应该忽略这个事件,以避免页面接收到这个事件而打断整个处理流程。如下代码所示:

<figure class="highlight JAVA" style="margin: 15px 0px 0px; padding: 7px 15px; border: 1px solid rgb(153, 153, 153); line-height: 1.6; overflow: auto; position: relative; font-size: 0.9em; color: rgb(68, 68, 68); font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; text-align: justify; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><figcaption style="margin: 0px 0px 5px; padding: 0px; color: rgb(153, 153, 153); text-shadow: rgb(255, 255, 255) 0px 0px 1px;"><span style="margin: 0px; padding: 0px;">MineWebView.java</span></figcaption><table style="margin: 0px; padding: 0px; border-spacing: 0px;"><tbody style="margin: 0px; padding: 0px;"><tr style="margin: 0px; padding: 0px;"> <td class="gutter" style="margin: 0px; padding: 0px 15px 0px 0px; color: rgb(153, 153, 153); border-right-width: 1px; border-right-style: solid; border-right-color: rgb(153, 153, 153); text-align: right;"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;">1 2 3 4 5 6 7 8 9 10 11 12 </pre></td> <td class="code" style="margin: 0px; padding: 0px 0px 0px 15px; color: rgb(102, 102, 102);"><pre style="margin-top: 0px; margin-bottom: 0px; padding: 0px; font-family: Monaco, Menlo, Consolas, 'Courier New', monospace; border: none; background-color: rgb(238, 238, 238); background-position: initial initial; background-repeat: initial initial;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="class" style="margin: 0px; padding: 0px;"><span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">class</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(181, 137, 0);">MineWebView</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">extends</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(181, 137, 0);">WebView</span> {</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> mIgnoreTouchCancel; <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">void</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">ignoreTouchCancel</span> (<span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> val) { mIgnoreTouchCancel = val; } <span class="annotation" style="margin: 0px; padding: 0px;">@Override</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">public</span> <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">boolean</span> <span class="title" style="margin: 0px; padding: 0px; color: rgb(38, 139, 210);">onTouchEvent</span>(MotionEvent ev) { <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">return</span> ev.getAction() == MotionEvent.ACTION_CANCEL &amp;&amp; mIgnoreTouchCancel || <span class="keyword" style="margin: 0px; padding: 0px; color: rgb(133, 153, 0);">super</span>.onTouchEvent(ev); } } </pre></td> </tr></tbody></table></figure>

另外针对这种解决方案,页面端的JS脚本不用做任何变动。


原文:http://www.pedant.cn/2014/09/10/webview-optimize-points/

谢谢分享


分享到:
评论

相关推荐

    Android中WebView常见问题及解决方案汇总

    在Android开发中,WebView是一个非常重要的组件,它允许开发者在应用程序内部加载和显示网页内容。WebView结合了HTML5和原生应用的优点,使得开发者能够构建混合型应用,从而实现更灵活的用户界面和更快的内容更新。...

    android开发资料大全

    android用户界面之WebView教程实例汇总 android用户界面之GridView教程实例汇总 android用户界面之SurfaceView教程实例汇总 android用户界面之Notification教程实例汇总 android用户界面之TextView教程实例汇总 ...

    50个优秀的Android项目源码

    在Android开发领域,掌握高质量的项目源码是提升技能的关键步骤。这个压缩包包含了50个精心挑选的Android优秀项目源码,对于正在学习或已经在从事Android开发的人员来说,这是一个不可多得的学习资源。 首先,...

    Android开发:集成多家大数据源的疫情信息服务新闻APP

    为了实现这些功能,作者使用了WebView、DrawerLayout、Fragment等Android组件,并对其进行了适当的配置与优化,确保数据加载速度快,用户体验良好。此外,应用还包括了分享功能与欢迎界面,提升了用户体验。 适用...

    android资料汇总

    16. **android开发技巧与性能优化** - **概述**:专门针对性能优化的技术指南。 - **核心知识点**: - 内存管理与垃圾回收。 - CPU与GPU负载控制。 - 能耗管理策略。 17. **Android上开发手机浏览器原理** - ...

    Android开发技巧总汇(5).pdf

    以下是一些从"Android开发技巧总汇(5)"中提取的关键知识点: 1. **坐标传递到Google Map**: 这个技巧涉及如何将地理位置坐标(如经度和纬度)传递给Google Maps API,以便在地图上显示特定的位置。开发者通常会使用...

    Android开发技巧总汇(4)

    ### Android开发技巧总汇(4) #### 1. 通过添加按钮来触发Menu按键 在Android应用中,可以通过编程方式来触发Menu按键的功能。如果希望用户在特定情况下手动调用菜单,可以添加一个按钮,并在按钮的点击事件中触发`...

    andorid播放gif汇总。包括单张图、webview、第三方库播放、jni方式播放gif

    在Android平台上,开发人员经常需要处理GIF动态图的显示,因为它们在各种应用场景中都很常见,如表情、动画效果等。以下是对标题和描述中提及的几个关键知识点的详细解释: 1. **单张图播放GIF**: 在Android中,...

    拍照和选择图片框架

    在移动应用开发中,"拍照和选择图片框架"是一个关键功能,它允许用户通过拍照或从设备相册中选择图片,然后进行分享、编辑或上传等操作。在本链接(由于无法直接访问,这里将根据常见实践进行解释)中,可能会详细...

    Android面试题总汇

    【Android面试题总汇】 在Android开发者面试过程中,面试官通常会关注候选人的基础理论知识、实战经验以及对新技术的理解。以下是对标题和描述中提到的一些关键知识点的详细阐述: 1. **四大组件**: - **...

    S1030基于安卓的疫情资讯新闻app.zip

    为了实现这些功能,作者使用了WebView、DrawerLayout、Fragment等Android组件,并对其进行了适当的配置与优化,确保数据加载速度快,用户体验良好。此外,应用还包括了分享功能与欢迎界面,提升了用户体验。 适用...

    java心得分享技术文章总结.docx

    - "WebView 开车指南"、"Android 录音功能"、"Android NFC 技术解析"分别涉及Web视图集成、音频录制和近场通信的使用。 - "Android 手机 全面屏(18:9 屏幕)适配指南"针对屏幕适配问题提供了解决方案。 10. **...

    phoneGap-cordova1.7完整实例

    3. **实例汇总**: 这个实例集成了Cordova 1.7的所有API实例,开发者可以通过查看和运行这些实例来学习如何使用PhoneGap和Cordova来开发应用。每个实例都展示了如何调用特定的设备功能,并且在iPhone 4S上进行了实际...

    react-native-newsletter:我最喜欢的React Native时事通讯

    它允许开发者使用JavaScript和React库来创建iOS和Android应用,同时享受Web开发的灵活性和效率。"react-native-newsletter" 指的是一种专门针对React Native的时事通讯,由Jon Major Condon主持,定期提供关于这个...

Global site tag (gtag.js) - Google Analytics