<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Barry&#39;s Blog</title>
  <subtitle>张新强的博客</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://barryhappy.github.io/"/>
  <updated>2017-06-05T15:49:36.000Z</updated>
  <id>http://barryhappy.github.io/</id>
  
  <author>
    <name>Barry Zhang</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Kotlin 跟 findViewById 的类型推导冲突问题</title>
    <link href="http://barryhappy.github.io/2017/06/05/kotlin-and-findviewbyid/"/>
    <id>http://barryhappy.github.io/2017/06/05/kotlin-and-findviewbyid/</id>
    <published>2017-06-05T15:42:22.000Z</published>
    <updated>2017-06-05T15:49:36.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>记录一个小坑 </p>
</blockquote>
<h1 id="1-描述"><a href="#1-描述" class="headerlink" title="1 描述"></a>1 描述</h1><p>从 version 26 开始，com.android.support:appcompat-v7 中的 <code>findViewById</code> 方法的返回值从 <code>View</code> 改成了 <code>&lt;T extends View&gt;</code>。 </p>
<p>对于开发者来说，喜大普奔的好处当然是以后终于可以不用在每个 finViewById 方法前面加个丑陋的类型强转了。 但是福兮祸兮，好事的背后也难免会有一些不如意的地方。<br><a id="more"></a><br>比如，如果你在用 Kotlin 的话，项目中可能会有很多类似这样的代码：</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 代码 1 </span></div><div class="line"><span class="keyword">val</span> textView = findViewById(R.id.textView) <span class="keyword">as</span> TextView</div></pre></td></tr></table></figure>
<p>这是 Kotlin 的习惯写法，种写法实际上是跟下面这种写法是等价的</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 代码 2</span></div><div class="line"><span class="keyword">val</span> textView : TextView = findViewById(R.id.textView) <span class="keyword">as</span> TextView</div></pre></td></tr></table></figure>
<p>由于 Kotlin 的<code>类型推导</code>特性，我们可以在声明 textView 变量的时候不必显式说明，系统会自动从后面的赋值语句中推测出它的类型是 TextView 。</p>
<p>但是在 version 26 之后，<em>代码 1</em> 的这种写法就会报错了：</p>
<blockquote>
<p>Type inference failed: Not enough information to infer parameter T in<br><strong>fun <t: view!=""> findViewById ( id: Int ) : T!</t:></strong><br>Please specify it explicitly</p>
</blockquote>
<p><img src="/images/14963205007134.jpg" alt="报错信息"></p>
<p>意思是没有足够的信息来推断 findViewById 的返回类型。</p>
<h1 id="2-原因"><a href="#2-原因" class="headerlink" title="2 原因"></a>2 原因</h1><p>上述错误的本质是<code>类型推导</code>的冲突。 </p>
<p>如上所说，我们对 textView 的定义并没有说明其类型，它的类型是从后面的赋值语句中推导出来的。<br>而新版本的 findViewById ，其返回类型是<code>&lt;T extends View&gt;</code>，这是一个泛型的声明，具体类型则是根据所赋值的变量类型来确定的。</p>
<p>—— 等号的左右两边互相依赖，互相还都没有指明，可不就冲突报错了么！</p>
<h1 id="3-解决方案："><a href="#3-解决方案：" class="headerlink" title="3 解决方案："></a>3 解决方案：</h1><p>既然是因为『两个相互依赖的类行推导都没有指明类型』，那解决方案自然就是选其中一个指明类型咯。 </p>
<h2 id="3-1"><a href="#3-1" class="headerlink" title="3.1"></a>3.1</h2><p>在等号左边声明类型：</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 代码 3</span></div><div class="line"><span class="keyword">val</span> textView : TextView = findViewById(R.id.textView)</div></pre></td></tr></table></figure>
<h2 id="3-2"><a href="#3-2" class="headerlink" title="3.2"></a>3.2</h2><p>在等号右边表明类型。<br>诸如这种带泛型签名的函数也是可以在调用时显式地指明类型的：</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 代码 4</span></div><div class="line"><span class="keyword">val</span> textView = findViewById&lt;TextView&gt;(R.id.textView)</div></pre></td></tr></table></figure>
<h1 id="4-总结"><a href="#4-总结" class="headerlink" title="4 总结"></a>4 总结</h1><ol>
<li><p>这只是个很简单的小问题，很好解决，但是了解其本质的过程才是更让人享受的过程~</p>
</li>
<li><p>有意思的是：As 默认支持 Kotlin 跟 findViewById 更新这两件事 —— 都是在这次的 IO 大会上宣布的。而且现在（2017.06.05）用 AS 新建一个项目并开启 Kotlin 支持，然后把 support-v7 包升级到 26，就会发现默认的页面就会报这个错 🙈…… 希望 Google 能早日改正~</p>
</li>
</ol>
<hr>
<p>关于作者 :<br><a href="http://www.barryzhang.com" target="_blank" rel="external">http://www.barryzhang.com</a><br><a href="https://github.com/barryhappy" target="_blank" rel="external">https://github.com/barryhappy</a><br><a href="http://www.jianshu.com/users/e4607fd59d0d" target="_blank" rel="external">http://www.jianshu.com/users/e4607fd59d0d</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;记录一个小坑 &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;1-描述&quot;&gt;&lt;a href=&quot;#1-描述&quot; class=&quot;headerlink&quot; title=&quot;1 描述&quot;&gt;&lt;/a&gt;1 描述&lt;/h1&gt;&lt;p&gt;从 version 26 开始，com.android.support:appcompat-v7 中的 &lt;code&gt;findViewById&lt;/code&gt; 方法的返回值从 &lt;code&gt;View&lt;/code&gt; 改成了 &lt;code&gt;&amp;lt;T extends View&amp;gt;&lt;/code&gt;。 &lt;/p&gt;
&lt;p&gt;对于开发者来说，喜大普奔的好处当然是以后终于可以不用在每个 finViewById 方法前面加个丑陋的类型强转了。 但是福兮祸兮，好事的背后也难免会有一些不如意的地方。&lt;br&gt;
    
    </summary>
    
    
      <category term="kotlin android findViewById" scheme="http://barryhappy.github.io/tags/kotlin-android-findViewById/"/>
    
  </entry>
  
  <entry>
    <title>如何用Canvas画一个正多边形</title>
    <link href="http://barryhappy.github.io/2016/12/12/how-to-draw-ploygon-with-canvas/"/>
    <id>http://barryhappy.github.io/2016/12/12/how-to-draw-ploygon-with-canvas/</id>
    <published>2016-12-11T16:47:47.000Z</published>
    <updated>2016-12-11T16:49:10.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="场景"><a href="#场景" class="headerlink" title="场景"></a>场景</h1><p>给定一个指定的正方形的区域，要求在该区域内画一个正N边形（正三角形、正方形、正五边形……）<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">drawPolygon</span> <span class="params">(RectF rect, Canvas canvas, Paint p, <span class="keyword">int</span> n)</span> </span>&#123;</div><div class="line">    <span class="comment">// draw……</span></div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<a id="more"></a>
<h1 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h1><p>要用到一些三角函数的知识，于是我画了一幅灵魂画作👻：<br><img src="http://upload-images.jianshu.io/upload_images/422451-c95bb6d9c5b7064c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="灵魂画作"></p>
<p>分析：</p>
<ul>
<li>计算出每个顶点的坐标，然后把它们连起来，就是一个正多边形啦~</li>
<li>圆心角a的度数为<code>360/n</code>，弧度计算为<code>2π/n</code>。</li>
<li>如果把圆心的坐标为(0,0)，那么顶点P1的坐标为<code>[X1=cos(a),Y1=sin(a)]</code>。</li>
<li>以此类推，顶点Pn坐标为<code>[Xn=cos(a*n),Yn=sin(a*n)]</code>。</li>
<li>圆心的实际坐标是外接矩形的中心：<code>[Ox=(rect.right+rect.left)/2 , Oy=(rect.top+rect.bottom)/2]</code>。</li>
<li>所以Pn的实际坐标是<code>[Xn+Ox,Yn+Oy]</code>。</li>
<li>把P0-P1…Pn连起来就是我们要的结果了。 </li>
<li>Java中可以使用<code>Path</code>来保存路径，最后使用<code>canvas.drawPath</code>来绘制出来。</li>
</ul>
<h1 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h1><p>简化的伪代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">float</span> a = <span class="number">2</span>π / n ; <span class="comment">// 角度</span></div><div class="line">Path path = <span class="keyword">new</span> Path();</div><div class="line"><span class="keyword">for</span>( <span class="keyword">int</span> i = <span class="number">0</span>; i &lt;= n; i++ )&#123;</div><div class="line">    <span class="keyword">float</span> x = R * cos(a * i); </div><div class="line">    <span class="keyword">float</span> y = R * sin(a * i);</div><div class="line">    <span class="keyword">if</span> (i = <span class="number">0</span>)&#123;</div><div class="line">        path.moveTo(x,y); <span class="comment">// 移动到第一个顶点   </span></div><div class="line">    &#125;<span class="keyword">else</span>&#123;</div><div class="line">        path.lineTo(x,y); <span class="comment">//    </span></div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line">drawPath(path);</div></pre></td></tr></table></figure>
<p>Java代码最终的完整代码，可以直接拿去用：</p>
<pre><code class="java">
<span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">drawPolygon</span> <span class="params">(RectF rect, Canvas canvas, Paint paintByLevel, <span class="keyword">int</span> number)</span> </span>{
    <span class="keyword">if</span>(number &lt; <span class="number">3</span>) {
        <span class="keyword">return</span>;
    }
    <span class="keyword">float</span> r = (rect.right - rect.left) / <span class="number">2</span>;
    <span class="keyword">float</span> mX = (rect.right + rect.left) / <span class="number">2</span>;
    <span class="keyword">float</span> my = (rect.top + rect.bottom) / <span class="number">2</span>;
    Path path = <span class="keyword">new</span> Path();
    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt;= number; i++) {
        <span class="comment">// - 0.5 : Turn 90 ° counterclockwise</span>
        <span class="keyword">float</span> alpha = Double.valueOf(((<span class="number">2f</span> / number) * i - <span class="number">0.5</span>) * Math.PI).floatValue();
        <span class="keyword">float</span> nextX = mX + Double.valueOf(r * Math.cos(alpha)).floatValue();
        <span class="keyword">float</span> nextY = my + Double.valueOf(r * Math.sin(alpha)).floatValue();
        <span class="keyword">if</span> (i == <span class="number">0</span>) {
            path.moveTo(nextX, nextY);
        } <span class="keyword">else</span> {
            path.lineTo(nextX, nextY);
        }
    }
    canvas.drawPath(path, paintByLevel);
}
</code></pre>
<p>#DEMO</p>
<p>这个项目里用到了这个函数，可以点进去看以及下载demo。<br><a href="https://github.com/barryhappy/TContributionsView" target="_blank" rel="external">https://github.com/barryhappy/TContributionsView</a>      </p>
<p><img src="http://upload-images.jianshu.io/upload_images/422451-16c3e1e997944f51.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="DEMO"></p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;场景&quot;&gt;&lt;a href=&quot;#场景&quot; class=&quot;headerlink&quot; title=&quot;场景&quot;&gt;&lt;/a&gt;场景&lt;/h1&gt;&lt;p&gt;给定一个指定的正方形的区域，要求在该区域内画一个正N边形（正三角形、正方形、正五边形……）&lt;br&gt;&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;1&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;2&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;3&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;code&quot;&gt;&lt;pre&gt;&lt;div class=&quot;line&quot;&gt;&lt;span class=&quot;function&quot;&gt;&lt;span class=&quot;keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;title&quot;&gt;drawPolygon&lt;/span&gt; &lt;span class=&quot;params&quot;&gt;(RectF rect, Canvas canvas, Paint p, &lt;span class=&quot;keyword&quot;&gt;int&lt;/span&gt; n)&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;    &lt;span class=&quot;comment&quot;&gt;// draw……&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;line&quot;&gt;&amp;#125;&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/p&gt;
    
    </summary>
    
    
      <category term="Android Canvas" scheme="http://barryhappy.github.io/tags/Android-Canvas/"/>
    
  </entry>
  
  <entry>
    <title>彻底搞懂startActivityForResult在FragmentActivity和Fragment中的异同</title>
    <link href="http://barryhappy.github.io/2016/11/19/difference-of-startactivityforresult-bettwen-fragment-activity/"/>
    <id>http://barryhappy.github.io/2016/11/19/difference-of-startactivityforresult-bettwen-fragment-activity/</id>
    <published>2016-11-19T05:03:05.000Z</published>
    <updated>2016-11-19T05:07:57.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-前言"><a href="#1-前言" class="headerlink" title="1. 前言"></a>1. 前言</h1><p>Activity、FragmentActivity、Fragment中都有<code>startActivityForResult()</code>方法，也都有用以接收结果的<code>onActivityResult()</code>方法，那他们有什么区别吗？用法上有什么不同吗？</p>
<p>之所以注意到这个问题，是因为最近一次在Fragment中使用了getActivity().startActivityForResult()去调用图片选择器，结果发现在Fragment的onActivityResult无法接收到返回的结果。</p>
<p>仔细研究了一下原因，发现了一些以前没注意到的问题，于是写出来分享给大家。</p>
<a id="more"></a>
<h1 id="2-表现"><a href="#2-表现" class="headerlink" title="2. 表现"></a>2. 表现</h1><p>假设有一个FragmentActivity中嵌套一个Fragment，它们各自使用startActivityForResult发起数据请求。<br>经测，目标所返回结果数据，能否被它们各自的onActivityResult方法所接收的情况如下：</p>
<p><img src="http://ww1.sinaimg.cn/large/006tNc79jw1f9xbehy112j30ks02tjrv.jpg" alt="image1">￼</p>
<ul>
<li>Fragment和FragmentActivity都能接收到自己的发起的请求所返回的结果</li>
<li>FragmentActivity发起的请求，Fragment完全接收不到结果</li>
<li>Fragment发起的请求，虽然在FragmentActivity中能获取到结果，但是requestCode完全对应不上</li>
</ul>
<p>为什么会有这种表现呢？往下看。</p>
<h1 id="3-找原因：Show-me-your-code"><a href="#3-找原因：Show-me-your-code" class="headerlink" title="3. 找原因：Show me your code !"></a>3. 找原因：Show me your code !</h1><p>仔细看文档的话，发现了一个以前没注意到的点：<strong>FragmentActivity相对于它的父类Activity，对startActivityForResult的描述是有些改动的。</strong></p>
<p>FragmentActivity.startActivityForResult的文档是这样的：</p>
<blockquote>
<p>修改了标准行为，以使它能够把结果传递到Fragment。<br><strong>添加了一个限制：requestCode必须&lt;=0xffff</strong></p>
</blockquote>
<p>这里的<code>标准行为</code>，自然指的是正常的Activity.startActivityForResult的功能。而新增加的对requestCode的大小限制看起来很蹊跷，估计是有什么猫腻在里面了。   </p>
<p>OK，不卖关子，直接看源码！</p>
<h2 id="3-1-Fragment-startActivityForResult"><a href="#3-1-Fragment-startActivityForResult" class="headerlink" title="3.1 Fragment.startActivityForResult"></a>3.1 <code>Fragment.startActivityForResult</code></h2><p>从Fragment的startActivityForResult开始：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivityForResult</span><span class="params">(Intent intent, <span class="keyword">int</span> requestCode, @Nullable Bundle options)</span> </span>&#123;</div><div class="line">    <span class="keyword">if</span> (mHost == <span class="keyword">null</span>) &#123;</div><div class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Fragment "</span> + <span class="keyword">this</span> + <span class="string">" not attached to Activity"</span>);</div><div class="line">    &#125;</div><div class="line">    mHost.onStartActivityFromFragment(<span class="keyword">this</span> <span class="comment">/*fragment*/</span>, intent, requestCode, options);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>Fragment.startActivityForResult本身的代码很简单，就是调用了一个mHost.onStartActivityFromFragment的方法。<br>—— Fragment被添加到一个FragmentActivity中之后，这里的mHost即是当前FragmentActivity的一个内部类FragmentActivity.HostCallbacks，它持有对FragmentActivity的引用，mHost.onStartActivityFromFragment被简单转发到当前FragmentActivity的<br>startActivityFromFragment()方法。</p>
<blockquote>
<p>Fragment.startActivityForResult<br>↓<br>FragmentActivitymHost.HostCallbacks.onStartActivityFromFragment<br>↓<br>FragmentActivity.startActivityFromFragment</p>
</blockquote>
<p>接下来到FragmentActivity.startActivityFromFragment：</p>
<h2 id="3-2-FragmentActivity-startActivityFromFragment"><a href="#3-2-FragmentActivity-startActivityFromFragment" class="headerlink" title="3.2 FragmentActivity.startActivityFromFragment"></a>3.2 <code>FragmentActivity.startActivityFromFragment</code></h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivityFromFragment</span><span class="params">(Fragment fragment, Intent intent,</span></span></div><div class="line">        <span class="keyword">int</span> requestCode, @Nullable Bundle options) &#123;</div><div class="line">    mStartedActivityFromFragment = <span class="keyword">true</span>;</div><div class="line">    <span class="keyword">try</span> &#123;</div><div class="line">        <span class="keyword">if</span> (requestCode == -<span class="number">1</span>) &#123;</div><div class="line">            ActivityCompat.startActivityForResult(<span class="keyword">this</span>, intent, -<span class="number">1</span>, options);</div><div class="line">            <span class="keyword">return</span>;</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">if</span> ((requestCode&amp;<span class="number">0xffff0000</span>) != <span class="number">0</span>) &#123;</div><div class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Can only use lower 16 bits for requestCode"</span>);</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">int</span> requestIndex = allocateRequestIndex(fragment);</div><div class="line">        ActivityCompat.startActivityForResult(</div><div class="line">            <span class="keyword">this</span>, intent, ((requestIndex+<span class="number">1</span>)&lt;&lt;<span class="number">16</span>) + (requestCode&amp;<span class="number">0xffff</span>), options);</div><div class="line">    &#125; <span class="keyword">finally</span> &#123;</div><div class="line">        mStartedActivityFromFragment = <span class="keyword">false</span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>分析一下这段代码：<br>1，<code>mStartedActivityFromFragment = true</code>首先标记一下请求是来自于Fragment。<br>2，<code>if(requestCode == 1)</code>的内容不用管，它是来自于startActivity（没有ForResult）的情况。<br>3，然后的代码添加了对requestCode必须小于0xffff的限制 <code>if((requestCode&amp;0xffff0000) ！= 0){/*抛异常*/}</code><br>    我们是从Fragment.startActivityForResult追踪到这里的，所以虽然文档没有明确说，但是从这里可以看出：<strong>Fragment.startActivityForResult的requestCode也是必须要&lt;=0xffff的。</strong></p>
<p><strong>然后，下面是关键点了：</strong><br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">ActivityCompat.startActivityForResult(</div><div class="line">            <span class="keyword">this</span>, intent, ((requestIndex+<span class="number">1</span>)&lt;&lt;<span class="number">16</span>) + (requestCode&amp;<span class="number">0xffff</span>), options);</div></pre></td></tr></table></figure></p>
<p>——其中ActivityCompat是一个帮助类，ActivityCompat.startActivityForResult最终还是调用的Activity.startActivityForResult，这个先不表。<br>这里的关键点就是，通过一个<code>requestCode</code>=&gt;<code>((requestIndex+1)&lt;&lt;16)+(requestCode&amp;0xffff)</code>的映射，Fragment.startActivityForResult最终还是调用了Activity.startActivityForResult。 </p>
<p>调用了Activity.startActivityForResult其实是意料之中的事情，只是从<code>requestCode</code>到<code>((requestIndex+1)&lt;&lt;16)+(requestCode&amp;0xffff)</code>是做了什么呢？</p>
<p>通过分析，得知requestIndex是请求的序号，值为从0递增的整数值。<br>又从前面得知，requestCode的本身的值是小于0xffff的，所以((requestIndex+1)&lt;&lt;16)+(requestCode&amp;0xffff)简化一下就是：<code>(requestIndex+1)*65536+requestCode</code>。<br><strong>——所以这个值是必定大于0xffff的。</strong></p>
<p>在看一下FragmentActivity.startActivityForResult的代码：</p>
<h2 id="3-3-FragmentActivity-startActivityForResult"><a href="#3-3-FragmentActivity-startActivityForResult" class="headerlink" title="3.3 FragmentActivity.startActivityForResult"></a>3.3 <code>FragmentActivity.startActivityForResult</code></h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">startActivityForResult</span><span class="params">(Intent intent, <span class="keyword">int</span> requestCode)</span> </span>&#123;</div><div class="line">    <span class="comment">// If this was started from a Fragment we've already checked the upper 16 bits were not in</span></div><div class="line">    <span class="comment">// use, and then repurposed them for the Fragment's index.</span></div><div class="line">    <span class="keyword">if</span> (!mStartedActivityFromFragment) &#123;</div><div class="line">        <span class="keyword">if</span> (requestCode != -<span class="number">1</span> &amp;&amp; (requestCode&amp;<span class="number">0xffff0000</span>) != <span class="number">0</span>) &#123;</div><div class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Can only use lower 16 bits for requestCode"</span>);</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">super</span>.startActivityForResult(intent, requestCode);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>可以看到，判断了一下如果请求不是来自于Fragment，也就是来自于FragmentActivity自身，就限制requestCode不能大于0xffff。</p>
<p>再加上前文所说的，Fragment.startActivityForResult最终映射的requestCode值必定大于0xffff，所以，现在可以得出了一个初步的结果：<br><strong>SDK把Fragment和FragmentActivity的的ruquestCode都限制在了0xffff以内，然后对于Fragment所发起的请求，都通过一个映射，把最终的requestCode变成了一个大于0xffff的值。</strong></p>
<p>——到现在，已经可以推测到：<strong>在获取的结果的时候，也是会通过跟0xffff这个数值来比较，来区分是要把结果交给FragmentActivity还是Fragment来处理。</strong></p>
<p>来验证一下看看：</p>
<h2 id="3-4-FragmentActivity-onActivityResult"><a href="#3-4-FragmentActivity-onActivityResult" class="headerlink" title="3.4 FragmentActivity.onActivityResult"></a>3.4 <code>FragmentActivity.onActivityResult</code></h2><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onActivityResult</span><span class="params">(<span class="keyword">int</span> requestCode, <span class="keyword">int</span> resultCode, Intent data)</span> </span>&#123;</div><div class="line">    mFragments.noteStateNotSaved();</div><div class="line">    <span class="keyword">int</span> requestIndex = requestCode&gt;&gt;<span class="number">16</span>;</div><div class="line">    <span class="keyword">if</span> (requestIndex != <span class="number">0</span>) &#123;</div><div class="line">        requestIndex--;</div><div class="line"></div><div class="line">        String who = mPendingFragmentActivityResults.get(requestIndex);</div><div class="line">        mPendingFragmentActivityResults.remove(requestIndex);</div><div class="line">        <span class="keyword">if</span> (who == <span class="keyword">null</span>) &#123;</div><div class="line">            Log.w(TAG, <span class="string">"Activity result delivered for unknown Fragment."</span>);</div><div class="line">            <span class="keyword">return</span>;</div><div class="line">        &#125;</div><div class="line">        Fragment targetFragment = mFragments.findFragmentByWho(who);</div><div class="line">        <span class="keyword">if</span> (targetFragment == <span class="keyword">null</span>) &#123;</div><div class="line">            Log.w(TAG, <span class="string">"Activity result no fragment exists for who: "</span> + who);</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">            targetFragment.onActivityResult(requestCode&amp;<span class="number">0xffff</span>, resultCode, data);</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">super</span>.onActivityResult(requestCode, resultCode, data);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>OK，一目了然，<strong>证实了我们上面的推论</strong>。<br>在FragmentActivity.onActivityResult中，只有requestCode&gt;0xffff时，这里得到的requestIndex才能满足<code>requestIndex != 0</code>，然后进入下面的逻辑：把requestCode通过反向之前的映射关系，还原成最初Fragment所指定的requestCode，交给Fragment.onActivityResult进行处理。</p>
<h1 id="4-解释最初的问题"><a href="#4-解释最初的问题" class="headerlink" title="4. 解释最初的问题"></a>4. 解释最初的问题</h1><p>所以，现在也能明白了为什么会有前面说的这几个表现：</p>
<ol>
<li><p>Fragment和FragmentActivity都能接收到自己的发起的请求所返回的结果</p>
<blockquote>
<p>那当然，就是这么设计的。</p>
</blockquote>
</li>
<li><p>FragmentActivity发起的请求，Fragment完全接收不到结果</p>
<blockquote>
<p>被FragmentActivity拦截了，没有转发到Fragment。</p>
</blockquote>
</li>
<li><p>Fragment发起的请求，虽然在FragmentActivity中能获取到结果，但是requestCode完全对应不上 </p>
<blockquote>
<p>如果是Fragment发起的请求，那么在FragmentActivity.onActivityResult获取到的requestCode，其实是经过映射之后一个的大于0xffff的值，已经不是最初Fragment发请求时的requestCode了。</p>
</blockquote>
</li>
</ol>
<h1 id="5-思考"><a href="#5-思考" class="headerlink" title="5. 思考"></a>5. 思考</h1><p>为什么要用映射requestCode的方法来区分请求是否来自Fragment呢？绕这么一个弯子，直接使用一个变量来标记不行么？</p>
<blockquote>
<p>直接使用一个变量来标记还真不行：</p>
<ul>
<li>因为我们自己最终写业务代码MyFragmentActivity肯定是继承自FragmentActivity的，而MyFragmentActivity.onActivityResult的调用会先于FragmentActivity.onActivityResult。</li>
<li>所以无论是Fragment还是MyFragmentActivity所发起的startActivityForResult请求，最终在获取结果的时候是一定是会通过MyFragmentActivity.onActivityResult的。</li>
<li>如果在这里使用一个变量来标记请求的来源，那实质上就是依赖于开发者自己来判断——这是繁琐而且不可控的。</li>
<li>而相比较而言，使用一个简单的映射规则，就能把来自Fragment的请求和来自FragmentActivity自身请求区分开来——十分简单可靠。</li>
</ul>
</blockquote>
<h1 id="6-总结"><a href="#6-总结" class="headerlink" title="6. 总结"></a>6. 总结</h1><ol>
<li>使用startActivityForResult的时候，requestCode一定不要大于0xffff(65535)。</li>
<li>如果希望在Fragment的onActivityResult接收数据，就要调用Fragment.startActivityForResult，而不是Fragment.getActivity().startActivityForResult。</li>
<li>看源码果然是学习的好方法~</li>
<li>Google的工程师果然牛逼。</li>
</ol>
<hr>
<p>关于作者 :<br><a href="http://www.barryzhang.com" target="_blank" rel="external">http://www.barryzhang.com</a><br><a href="https://github.com/barryhappy" target="_blank" rel="external">https://github.com/barryhappy</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;1-前言&quot;&gt;&lt;a href=&quot;#1-前言&quot; class=&quot;headerlink&quot; title=&quot;1. 前言&quot;&gt;&lt;/a&gt;1. 前言&lt;/h1&gt;&lt;p&gt;Activity、FragmentActivity、Fragment中都有&lt;code&gt;startActivityForResult()&lt;/code&gt;方法，也都有用以接收结果的&lt;code&gt;onActivityResult()&lt;/code&gt;方法，那他们有什么区别吗？用法上有什么不同吗？&lt;/p&gt;
&lt;p&gt;之所以注意到这个问题，是因为最近一次在Fragment中使用了getActivity().startActivityForResult()去调用图片选择器，结果发现在Fragment的onActivityResult无法接收到返回的结果。&lt;/p&gt;
&lt;p&gt;仔细研究了一下原因，发现了一些以前没注意到的问题，于是写出来分享给大家。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Android Fragment startActivityForResult" scheme="http://barryhappy.github.io/tags/Android-Fragment-startActivityForResult/"/>
    
  </entry>
  
  <entry>
    <title>Android爬坑之旅：软键盘挡住输入框问题的终极解决方案</title>
    <link href="http://barryhappy.github.io/2016/10/26/what-if-keyboard-hide-input-view/"/>
    <id>http://barryhappy.github.io/2016/10/26/what-if-keyboard-hide-input-view/</id>
    <published>2016-10-26T15:26:43.000Z</published>
    <updated>2016-10-27T11:07:43.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>本文由<a href="https://github.com/barryhappy" target="_blank" rel="external">BarryZhang</a>原创，同时首发于diycode.cc、barryzhang.com 、github.com/barryhappy，非商业转载请注明作者和原文链接。</p>
</blockquote>
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>开发做得久了，总免不了会遇到各种坑。<br>而在Android开发的路上，『软键盘挡住了输入框』这个坑，可谓是一个旷日持久的巨坑——来来来，我们慢慢看。</p>
<a id="more"></a> 
<h1 id="入门篇"><a href="#入门篇" class="headerlink" title="入门篇"></a>入门篇</h1><p><img src="http://barryzhang.qiniudn.com/QQ20161024-0.png" alt="Base"></p>
<p>最基本的情况，如图所示：在页面底部有一个EditText，如果不做任何处理，那么在软键盘弹出的时候，就有可能会挡住EditText。<br>对于这种情况的处理其实很简单，只需要在AndroidManifest文件中对activity设置：<code>android:windowSoftInputMode</code>的值<code>adjustPan</code>或者<code>adjustResize</code>即可，像这样：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">activity</span></span></div><div class="line">	<span class="attr">android:name</span>=<span class="string">".MainActivity"</span></div><div class="line">	<span class="attr">android:windowSoftInputMode</span>=<span class="string">"adjustPan"</span>  &gt;</div><div class="line">    ...</div><div class="line"><span class="tag">&lt;/<span class="name">activity</span>&gt;</span></div></pre></td></tr></table></figure>
<p>一般来说，他们都可以解决问题，当然，<code>adjustPan</code>跟<code>adjustResize</code>的效果略有区别。 </p>
<ul>
<li><code>adjustPan</code>是把整个界面向上平移，使输入框露出，不会改变界面的布局；</li>
<li><code>adjustResize</code>则是重新计算弹出软键盘之后的界面大小，相当于是用更少的界面区域去显示内容，输入框一般自然也就在内了。</li>
</ul>
<p>↑↑↑ OK，这只是入门，基本上地球上所有的Android工程师都能搞定。<br>别急，看下面~</p>
<h1 id="加上WebView试试看？坑来了……"><a href="#加上WebView试试看？坑来了……" class="headerlink" title="加上WebView试试看？坑来了……"></a>加上WebView试试看？坑来了……</h1><h2 id="情况描述"><a href="#情况描述" class="headerlink" title="情况描述"></a>情况描述</h2><p>上面的入门篇中，软键盘是由原生的EditText触发弹出的。而在H5、Hybrid几乎已经成为App标配的时候，我们经常还会碰到的情况是：<strong>软键盘是由WebView中的网页元素所触发弹出的</strong>。  </p>
<p>这时候，情况就会变得复杂了: </p>
<ol>
<li>首先，页面是<code>非全屏模式</code>的情况下，给activity设置<code>adjustPan</code>会失效。</li>
<li>其次，页面是<code>全屏模式</code>的情况，<code>adjustPan</code>跟<code>adjustResize</code>都会失效。</li>
</ol>
<p>——解释一下，这里的<code>全屏模式</code>即是页面是全屏的，包括Application或activity使用了Fullscreen主题、使用了『状态色着色』、『沉浸式状态栏』、『Immersive Mode』等等——总之，基本上只要是App自己接管了状态栏的控制，就会产生这种问题。 </p>
<p>下面这个表格可以简单列举了具体的情况。 </p>
<p><img src="http://barryzhang.qiniudn.com/14773156950939.jpg" alt="表格"></p>
<h2 id="为什么说它是个坑？”issue-5497”"><a href="#为什么说它是个坑？”issue-5497”" class="headerlink" title="为什么说它是个坑？”issue 5497”"></a>为什么说它是个坑？”issue 5497”</h2><p>上面表格的这种情况并非是Google所期望的，理想的情况当然是它们都能正常生效才对——所以这其实是Android系统本身的一个BUG。 </p>
<p>为什么文章开头说这是个坑呢？<br>——因为这个BUG从Android1.x时代（2009年）就被报告了，而一直到了如今的Android7.0（2016年）还是没有修复……/(ㄒoㄒ)/<br>可以说这不仅是个坑，而且还是个官方挖的坑~</p>
<p>“issue 5497”，详情传送门 ☞ <a href="https://code.google.com/p/android/issues/detail?id=5497" target="_blank" rel="external">Issue 5497 - android -WebView adjustResize windowSoftInputMode breaks when activity is fullscreen - Android Open Source Project - Issue Tracker - Google Project Hosting</a></p>
<p>当然了，不管坑是谁挖的，最终还是要开发者来解决。</p>
<p>遇到坑之后，有两种方法可以过去：躲，或者填。</p>
<h1 id="躲坑姿势"><a href="#躲坑姿势" class="headerlink" title="躲坑姿势"></a>躲坑姿势</h1><p>如前文所示，出现坑的条件是：带有WebView的activity使用了<code>全屏模式</code>或者<code>adjustPan</code>模式。<br>那么躲坑的姿势就很简单了——<br>    <strong>如果activity中有WebView，就不要使用<code>全屏模式</code>，并且把它的windowSoftInputMode值设为<code>adjustResize</code>就好了嘛</strong></p>
<p>怎么样，是不是很简单？😑<br><img src="http://barryzhang.qiniudn.com/20130927092846557.jpg" alt="20130927092846557"></p>
<h1 id="填坑姿势"><a href="#填坑姿势" class="headerlink" title="填坑姿势"></a>填坑姿势</h1><p>但总有些时候，是需要<code>全屏模式</code>跟WebView兼得的，这时候，躲坑就不行了，我们需要一个新的填坑的姿势。幸好，开发者的智慧是无穷的，这个坑出现了这么多年，还是有人找到了一些解决方案的。 </p>
<h2 id="AndroidBug5497Workaround"><a href="#AndroidBug5497Workaround" class="headerlink" title="AndroidBug5497Workaround"></a>AndroidBug5497Workaround</h2><p>我个人认为最好的解决方案是这个：<a href="http://stackoverflow.com/a/19494006" target="_blank" rel="external">AndroidBug5497Workaround</a>，只需要一个神奇的<code>AndroidBug5497Workaround</code>类。</p>
<p>使用步骤：</p>
<ol>
<li>把<code>AndroidBug5497Workaround</code>类复制到项目中</li>
<li>在需要填坑的activity的onCreate方法中添加一句<code>AndroidBug5497Workaround.assistActivity(this)</code>即可。  </li>
</ol>
<p>经过测试，基本在各个Android版本上都可用，效果基本与设置了<code>adjustResize</code>相当。<br>看一个对比图：<br><img src="http://barryzhang.qiniudn.com/webview-compare.jpg" alt="效果对比图"></p>
<p>来自我厂App的某个使用WebView的<code>全屏模式</code>Activity页面，从左到右分别是：没有软键盘的样式、软键盘挡住输入框的效果、以及使用AndroidBug5497Workaround之后的最终效果。</p>
<h2 id="它的原理是什么？"><a href="#它的原理是什么？" class="headerlink" title="它的原理是什么？"></a>它的原理是什么？</h2><p>这个炫酷AndroidBug5497Workaround类，其实并不是很复杂，只有几十行代码，先贴在这里：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AndroidBug5497Workaround</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">// For more information, see https://code.google.com/p/android/issues/detail?id=5497</span></div><div class="line">    <span class="comment">// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.</span></div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">assistActivity</span> <span class="params">(Activity activity)</span> </span>&#123;</div><div class="line">        <span class="keyword">new</span> AndroidBug5497Workaround(activity);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">private</span> View mChildOfContent;</div><div class="line">    <span class="keyword">private</span> <span class="keyword">int</span> usableHeightPrevious;</div><div class="line">    <span class="keyword">private</span> FrameLayout.LayoutParams frameLayoutParams;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="title">AndroidBug5497Workaround</span><span class="params">(Activity activity)</span> </span>&#123;</div><div class="line">        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);</div><div class="line">        mChildOfContent = content.getChildAt(<span class="number">0</span>);</div><div class="line">        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(<span class="keyword">new</span> ViewTreeObserver.OnGlobalLayoutListener() &#123;</div><div class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onGlobalLayout</span><span class="params">()</span> </span>&#123;</div><div class="line">                possiblyResizeChildOfContent();</div><div class="line">            &#125;</div><div class="line">        &#125;);</div><div class="line">        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">possiblyResizeChildOfContent</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="keyword">int</span> usableHeightNow = computeUsableHeight();</div><div class="line">        <span class="keyword">if</span> (usableHeightNow != usableHeightPrevious) &#123;</div><div class="line">            <span class="keyword">int</span> usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();</div><div class="line">            <span class="keyword">int</span> heightDifference = usableHeightSansKeyboard - usableHeightNow;</div><div class="line">            <span class="keyword">if</span> (heightDifference &gt; (usableHeightSansKeyboard/<span class="number">4</span>)) &#123;</div><div class="line">                <span class="comment">// keyboard probably just became visible</span></div><div class="line">                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;</div><div class="line">            &#125; <span class="keyword">else</span> &#123;</div><div class="line">                <span class="comment">// keyboard probably just became hidden</span></div><div class="line">                frameLayoutParams.height = usableHeightSansKeyboard;</div><div class="line">            &#125;</div><div class="line">            mChildOfContent.requestLayout();</div><div class="line">            usableHeightPrevious = usableHeightNow;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">computeUsableHeight</span><span class="params">()</span> </span>&#123;</div><div class="line">        Rect r = <span class="keyword">new</span> Rect();</div><div class="line">        mChildOfContent.getWindowVisibleDisplayFrame(r);</div><div class="line">        <span class="keyword">return</span> (r.bottom - r.top);<span class="comment">// 全屏模式下： return r.bottom</span></div><div class="line">    &#125;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>代码大致是做了这么几件事： </p>
<h3 id="1-找到activity的根View"><a href="#1-找到activity的根View" class="headerlink" title="1.找到activity的根View"></a>1.找到activity的根View</h3><p>看一下入口的代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);</div><div class="line">mChildOfContent = content.getChildAt(<span class="number">0</span>);</div></pre></td></tr></table></figure></p>
<p>其中，第一行中的<code>android.R.id.content</code>所指的View，是Android所有Activity界面上开发者所能控制的区域的根View。</p>
<blockquote>
<ul>
<li>如果Activity是<code>全屏模式</code>，那么android.R.id.content就是占满全部屏幕区域的。</li>
<li>如果Activity是普通的<code>非全屏模式</code>，那么android.R.id.content就是占满除状态栏之外的所有区域。</li>
<li>其他情况，如Activity是弹窗、或者7.0以后的分屏样式等，android.R.id.content也是弹窗的范围或者分屏所在的半个屏幕——这些情况较少，就暂且不考虑了。</li>
</ul>
</blockquote>
<p>我们经常用的setContentView(View view)/setContent(int layRes)其实就是把我们指定的View或者layRes放到android.R.id.content里面，成为它的子View。</p>
<p>所以，然后，第二行content.getChildAt(0)获取到的<code>mChildOfContent</code>，其实也就是用以获取到我们用setContentView放进去的View。</p>
<h3 id="2-设置一个Listener监听View树变化"><a href="#2-设置一个Listener监听View树变化" class="headerlink" title="2.设置一个Listener监听View树变化"></a>2.设置一个Listener监听View树变化</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(&#123; <span class="comment">//简化了写法</span></div><div class="line">        possiblyResizeChildOfContent();</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>View.getViewTreeObserver()可以获取一个<code>ViewTreeObserver</code>对象——这个对象是一个观察者，专门用以监听当前View树所发生的一些变化。这里所注册的<code>addOnGlobalLayoutListener</code>，就是会在当前的View树的全局布局（GlobalLayout）发生变化、或者其中的View可视状态有变化时，进行通知回调。</p>
<p>——<strong>『软键盘弹出』，则是会触发这个事件的一个源。</strong> (软键盘弹出会使GlobalLayout发生变化)</p>
<p>也就是说，现在能监听到『软键盘弹出』的事件了。 </p>
<h3 id="3-界面变化之后，获取”可用高度”"><a href="#3-界面变化之后，获取”可用高度”" class="headerlink" title="3.界面变化之后，获取”可用高度”"></a>3.界面变化之后，获取”可用高度”</h3><p>当软键盘弹出了之后，接下来的事情是获取改变之后的界面的<code>可用高度</code>（可以被开发者用以显示内容的高度）。<br>直接看代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">computeUsableHeight</span><span class="params">()</span> </span>&#123;</div><div class="line">    Rect rect = <span class="keyword">new</span> Rect();</div><div class="line">    mChildOfContent.getWindowVisibleDisplayFrame(rect);</div><div class="line">    <span class="comment">// rect.top其实是状态栏的高度，如果是全屏主题，直接 return rect.bottom就可以了</span></div><div class="line">    <span class="keyword">return</span> (rect.bottom - rect.top);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>View.getWindowVisibleDisplayFrame(Rect rect)，这行代码能够获取到的Rect——就是界面除去了标题栏、除去了被软键盘挡住的部分，所剩下的矩形区域——如图所示，红框中的区域。<br><img src="http://barryzhang.qiniudn.com/QQ20161025.jpg" alt="Rect区域示意图"></p>
<p>↑也可以看出：</p>
<ul>
<li>rect.top值，其实就是标题栏的高度。（实际上，这也常常被用作为获取标题栏高度的方法）</li>
<li>屏幕高度-rect.bottom，是软键盘的高度。（获取软键盘高度的方法也出现了）</li>
</ul>
<p>这时，就有：</p>
<ul>
<li><code>全屏模式</code>下，<code>可用高度</code> = rect.bottom </li>
<li>非<code>全屏模式</code>，<code>可用高度</code> = rect.bottom - rect.top</li>
</ul>
<h3 id="4-最后一步，重设高度"><a href="#4-最后一步，重设高度" class="headerlink" title="4.最后一步，重设高度"></a>4.最后一步，重设高度</h3><p>我们计算出的<code>可用高度</code>，是目前在视觉效果上能看到的界面高度。但当前界面的实际高度是比<code>可用高度</code>要多出一个软键盘的距离的。<br>所以，最后一步，就是把界面高度置为<code>可用高度</code>——大功告成。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">possiblyResizeChildOfContent</span><span class="params">()</span> </span>&#123;</div><div class="line">    <span class="keyword">int</span> usableHeightNow = computeUsableHeight();</div><div class="line">    <span class="keyword">if</span> (usableHeightNow != usableHeightPrevious) &#123;</div><div class="line">        <span class="keyword">int</span> usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();</div><div class="line">        <span class="keyword">int</span> heightDifference = usableHeightSansKeyboard - usableHeightNow;</div><div class="line">        <span class="keyword">if</span> (heightDifference &gt; (usableHeightSansKeyboard/<span class="number">4</span>)) &#123;</div><div class="line">            <span class="comment">// keyboard probably just became visible</span></div><div class="line">            frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">            <span class="comment">// keyboard probably just became hidden</span></div><div class="line">            frameLayoutParams.height = usableHeightSansKeyboard;</div><div class="line">        &#125;</div><div class="line">        mChildOfContent.requestLayout();</div><div class="line">        usableHeightPrevious = usableHeightNow;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>上面的代码里添加了一个”heightDifference &gt; (usableHeightSansKeyboard/4)”的判断，这是为了去除无谓的干扰。因为能触发OnGlobalLayout事件的原因有很多，不止是软键盘的弹出变化，还包括各种子View的隐藏显示变化等，它们对界面高度的影响有限。加上了这个判断之后，只有界面的高度变化超过1/4的屏幕高度，才会进行重新设置高度，基本能保证代码只响应软键盘的弹出。</p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>总结起来，就是这样：</p>
<ol>
<li>普通Activity（不带WebView），直接使用<code>adjustpan</code>或者<code>adjustResize</code></li>
<li>如果带WebView：   <ul>
<li>a) 如果非<code>全屏模式</code>，可以使用<code>adjustResize</code></li>
<li>b) 如果是<code>全屏模式</code>，则使用<code>AndroidBug5497Workaround</code>进行处理。</li>
</ul>
</li>
</ol>
<p>OK，以上就是一段关于『软键盘挡住输入框』的爬坑之旅。</p>
<h1 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h1><p>有用的链接：<br><a href="https://code.google.com/p/android/issues/detail?id=5497" target="_blank" rel="external">https://code.google.com/p/android/issues/detail?id=5497</a><br><a href="http://stackoverflow.com/a/19494006" target="_blank" rel="external">http://stackoverflow.com/a/19494006</a><br><a href="https://developer.android.com/reference/android/view/ViewTreeObserver.html" target="_blank" rel="external">https://developer.android.com/reference/android/view/ViewTreeObserver.html</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;本文由&lt;a href=&quot;https://github.com/barryhappy&quot;&gt;BarryZhang&lt;/a&gt;原创，同时首发于diycode.cc、barryzhang.com 、github.com/barryhappy，非商业转载请注明作者和原文链接。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;开发做得久了，总免不了会遇到各种坑。&lt;br&gt;而在Android开发的路上，『软键盘挡住了输入框』这个坑，可谓是一个旷日持久的巨坑——来来来，我们慢慢看。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Android keyboard webview" scheme="http://barryhappy.github.io/tags/Android-keyboard-webview/"/>
    
  </entry>
  
  <entry>
    <title>Android开发：为什么要使用Kotlin？</title>
    <link href="http://barryhappy.github.io/2016/10/21/why-we-need-kotlin/"/>
    <id>http://barryhappy.github.io/2016/10/21/why-we-need-kotlin/</id>
    <published>2016-10-20T16:34:08.000Z</published>
    <updated>2016-10-22T13:19:23.000Z</updated>
    
    <content type="html"><![CDATA[<p>在使用Kotlin进行了一段时间的Android开发之后，我深深地体验到了它的美好，觉得是时候把它拿出来安利一下了。<br><a id="more"></a></p>
<h1 id="1-Kotlin是什么？"><a href="#1-Kotlin是什么？" class="headerlink" title="1. Kotlin是什么？"></a>1. Kotlin是什么？</h1><p>Kotlin是一门编程语言，由JetBrains公司开发的。JetBrains就是那个开发了无数个牛逼IDE的公司，Android Studio就是建立在他家的Intellij之上的。<br>Kotlin是基于JVM的，所以开发者可以什么方便地用它来进行Android开发——支持Kotlin和Java的混合编写。</p>
<h1 id="2-为什么要使用Kotlin？"><a href="#2-为什么要使用Kotlin？" class="headerlink" title="2. 为什么要使用Kotlin？"></a>2. 为什么要使用Kotlin？</h1><p>我觉得它之所以适合Android开发，主要是因为以下的特点：</p>
<h2 id="2-1-简洁、优雅"><a href="#2-1-简洁、优雅" class="headerlink" title="2.1 简洁、优雅"></a>2.1 简洁、优雅</h2><p>写了很多年的Java之后，再写Kotlin，真是觉得如沐春风。<br>除了<code>不用写分号</code>以及天然<code>支持Lambda表达式</code>之外，它的语法本身就比Java简洁许多。 </p>
<p>举几个小例子，比如，类型推导：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">val a : Int = <span class="number">1</span> <span class="comment">// 正常的声明、赋值</span></div><div class="line">val b = <span class="number">2</span> <span class="comment">// 类型推导，可以省略Int声明 </span></div><div class="line"></div><div class="line"><span class="comment">/* 定义函数：自动推导函数的返回值类型 */</span></div><div class="line">fun sum(a: Int, b: Int) = a + b</div></pre></td></tr></table></figure></p>
<p>字符串模板：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">val name = <span class="string">"Barry"</span></div><div class="line">val age = <span class="number">15</span></div><div class="line">print(<span class="string">"My name is $&#123;name&#125;, I am $&#123;age&#125; "</span>)  <span class="comment">// 会打印出： My name is Barry, I am 15</span></div></pre></td></tr></table></figure></p>
<p>下面这个例子，可以对比体会一下他们的不同—— 遍历一个列表，打印其中的奇数。<br>如果用java写:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">List list = Arrays.asList(<span class="string">"H"</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">10</span>,<span class="number">5</span>,<span class="number">9</span>,<span class="number">20</span>,<span class="number">199</span>);</div><div class="line"><span class="keyword">for</span>(Object number : list)&#123;</div><div class="line">    <span class="keyword">if</span>(!(number <span class="keyword">instanceof</span>  Integer)) &#123;</div><div class="line">        <span class="keyword">continue</span>;</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">if</span>((Integer)number % <span class="number">2</span> == <span class="number">0</span>)&#123;</div><div class="line">        System.out.println((Integer)number);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>而用Kotlin的话，显然会更清晰、简洁、优雅（注意这里的lambda表达式）：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">val list = Arrays.asList(<span class="string">"H"</span>, <span class="number">1</span>, <span class="number">3</span>, <span class="number">10</span>, <span class="number">5</span>, <span class="number">9</span>, <span class="number">20</span>, <span class="number">199</span>)</div><div class="line">list.filter &#123; it is Int &#125;</div><div class="line">        .filter &#123; it <span class="keyword">as</span> Int % <span class="number">2</span> != <span class="number">0</span> &#125;</div><div class="line">        .forEach &#123; println(it) &#125;</div></pre></td></tr></table></figure></p>
<p>在Kotlin中，诸如此类的语法糖还有很多：函数默认值、内联函数、ranges、kv遍历Map、lazy属性、数据对象声明、运算符重载…… 如果展开来说的话，每个都可以说很多。</p>
<p>当习惯这些之后，很容易就会有这种感觉：写Kotlin相比较写Java，就像高铁之于绿皮火车、iPhone之于山寨机、4K屏之于800*600……没有它之前觉得老的东西也能用，但是一旦习惯，再改回去的话就觉得浑身难受。 </p>
<p>另外，简洁的语法不仅使代码读起来清晰流畅，也让它自然<strong>简单易学</strong>——基本上花上半天时间，把官方文档看上一遍，就可以开始coding了。</p>
<h2 id="2-2-安全"><a href="#2-2-安全" class="headerlink" title="2.2 安全"></a>2.2 安全</h2><p>在Java里，我们常常要进行许多非空判断：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fun1</span> <span class="params">(String str)</span></span>&#123;</div><div class="line">   <span class="keyword">if</span>(str != <span class="keyword">null</span>) &#123;</div><div class="line">       System.out.println(<span class="string">"Length = "</span> + str.length());</div><div class="line">   &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>而在Kotlin中，即使不进行这些额外的判断，代码也是健壮的：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 如果str=null，会打印：Length = null</span></div><div class="line">fun fun1 (str: <span class="built_in">String</span>?) &#123;</div><div class="line">    println(<span class="string">"Length = "</span> + str?.length) </div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>还有，Java中经常出现的ClassCastException，在Kotlin中，也可以通过安全的类型转换进行避免：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">val l2 = listOf(<span class="string">"A"</span>,<span class="number">1</span>,<span class="number">3</span>,<span class="number">6</span>,<span class="number">8</span>,<span class="string">'c'</span>)</div><div class="line">l2.forEach &#123; println(it <span class="keyword">as</span>? Int) &#125;</div></pre></td></tr></table></figure></p>
<h2 id="2-3-函数、属性扩展"><a href="#2-3-函数、属性扩展" class="headerlink" title="2.3 函数、属性扩展"></a>2.3 函数、属性扩展</h2><p>Kotlin提供了一种方法——可以在既不需要继承父类，也不需要使用类似装饰器设计模式的情况下，对类进行扩展。简直是黑科技！<br>比如，给String类添加一个扩展方法，用以返回它的第一个字符。只需要这样：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 函数扩展</span></div><div class="line">fun <span class="built_in">String</span>.firstChar() : <span class="built_in">String</span>? &#123;</div><div class="line">    <span class="keyword">return</span> <span class="keyword">if</span>(<span class="keyword">this</span>.length &gt; <span class="number">0</span>) <span class="keyword">this</span>.substring(<span class="number">0</span>,<span class="number">1</span>) <span class="keyword">else</span> <span class="string">"NULL"</span></div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这个扩展函数可以在任何地方声明，然后其他任何地方String对象就都可以使用这个方法了:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">val s = <span class="string">"Hello"</span>.firstChar())  <span class="comment">// s的值为"H"</span></div><div class="line">println(<span class="string">"你好"</span>.firstChar())   <span class="comment">// 打印： 你</span></div><div class="line">println(<span class="string">""</span>.firstChar())   <span class="comment">// 打印： NULL</span></div></pre></td></tr></table></figure></p>
<h2 id="2-4-兼容Java"><a href="#2-4-兼容Java" class="headerlink" title="2.4 兼容Java"></a>2.4 兼容Java</h2><p>一个语言能否被广泛使用，除了它本身要好用之外，还要有一个良好的生态。 尤其在是如今，各种应用层的开发实际上很依赖于各种开源库、第三方组件等，如果一个应用层的语言没有这些支持的话，是很难被广泛使用的。<br>而对于Kotlin来说，这个问题是不存在的，因为它跟Java是无缝连接的，可以相互操作。<br>这意味着：</p>
<ul>
<li>Kotlin可以使用所有用Java写的组件：（RxJava、Retrofit、EventBus、Fresco……这些都不是问题！）</li>
<li>在现有的用Java写的项目中可以局部使用Kotlin，混合开发</li>
<li>Java-&gt;Kotlin，可以逐步迁移而不是非黑即白</li>
<li>使用Kotlin开发时，万一遇到问题，可以瞬间切换回Java~  </li>
</ul>
<h2 id="2-5-工具完善"><a href="#2-5-工具完善" class="headerlink" title="2.5 工具完善"></a>2.5 工具完善</h2><p>来，我们再理一下这个关系树：<br>JetBrains<br>|–&gt; Intellij IDEA –&gt; Android Studio<br>|–&gt; Kotlin<br>Kotlin是JetBrains公司出的；JetBrains向来以做各种牛逼的IDE著称；Android的官方开发工具AndroidStudio就是基于JetBrains公司出品的Intellij改造的。<br>↑ 以上是背景。<br>从实际体验角度来说，Kotlin的开发也是十分方便快捷的。<br>只需花几分钟给AndroidStudio安装一个Kotlin的扩展插件，就可以迅速开始体验使用Kotlin的快感了。<br>一开始的时候，你甚至都不需要会使用Kotlin！这是因为Kotlin插件提供了一个Java-&gt;Kotlin的转换功能，可以一键把现有的Java代码转换为Kotlin代码，你可以通过阅读它转换后的Kotlin代码，进行学习。<br>（实际上，我在学习Kotlin的时候，就大量使用了这种方法：当我不知道某个功能用Kotlin怎么写的时候，就会先用Java把它写出来，然后使用转换功能，生成Kotlin代码，从而就知道了Kotlin的写法）</p>
<h1 id="3-如何开始"><a href="#3-如何开始" class="headerlink" title="3. 如何开始"></a>3. 如何开始</h1><p>因为是一篇纯粹的说明文，所以我把如何开始单独抽离出来，写了这篇文章：《只需五分钟，开始使用Kotlin开发Android》<br>☞ 链接看这里：<br><a href="http://www.barryzhang.com/archives/476" target="_blank" rel="external">http://www.barryzhang.com/archives/476</a><br><a href="http://www.jianshu.com/p/5fa2c1eda64c" target="_blank" rel="external">http://www.jianshu.com/p/5fa2c1eda64c</a></p>
<h1 id="其他："><a href="#其他：" class="headerlink" title="其他："></a>其他：</h1><p>有用的链接们：</p>
<ul>
<li><a href="https://kotlinlang.org/" target="_blank" rel="external">Kotlin官网</a>  <a href="http://kotlinlang.cn/" target="_blank" rel="external">Kotlin官网：中文</a></li>
<li><a href="https://huanglizhuo.gitbooks.io/kotlin-in-chinese" target="_blank" rel="external">Kotlin in Chinese</a><br>中文翻译的Kotlin官方文档 by @huanglizhuo。<br><a href="http://kotlindoc.com/" target="_blank" rel="external">另一个地址</a>，访问速度会略快一些。</li>
<li><a href="https://github.com/wangjiegulu/kotlin-for-android-developers-zh/blob/master/SUMMARY.md" target="_blank" rel="external">中文版：Kotlin for Android Developers</a></li>
<li><a href="https://github.com/barryhappy/Gank.Kotlin" target="_blank" rel="external">厚着脸皮放一个自己的Kotlin项目</a></li>
<li><a href="https://www.zhihu.com/question/25289041" target="_blank" rel="external">如何评价 Kotlin 语言？——知乎</a> </li>
<li><a href="http://mp.weixin.qq.com/s?__biz=MzA3NTYzODYzMg==&amp;mid=404087761&amp;idx=1&amp;sn=d80625ee52f860a7a2ed4c238d2151b6" target="_blank" rel="external">Android开发必备知识：为什么说Kotlin值得一试</a> </li>
</ul>
<hr>
<p>关于作者 :<br><a href="http://www.barryzhang.com" target="_blank" rel="external">http://www.barryzhang.com</a><br><a href="https://barryhappy.github.io">https://barryhappy.github.io</a><br><a href="http://www.jianshu.com/users/e4607fd59d0d/latest_articles" target="_blank" rel="external">http://www.jianshu.com/users/e4607fd59d0d/latest_articles</a><br><a href="http://blog.csdn.net/barryhappy" target="_blank" rel="external">http://blog.csdn.net/barryhappy</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在使用Kotlin进行了一段时间的Android开发之后，我深深地体验到了它的美好，觉得是时候把它拿出来安利一下了。&lt;br&gt;
    
    </summary>
    
    
      <category term="Android Kotlin" scheme="http://barryhappy.github.io/tags/Android-Kotlin/"/>
    
  </entry>
  
  <entry>
    <title>只需五分钟，开始使用Kotlin开发Android</title>
    <link href="http://barryhappy.github.io/2016/10/20/start-kotlin-in-5-mins/"/>
    <id>http://barryhappy.github.io/2016/10/20/start-kotlin-in-5-mins/</id>
    <published>2016-10-19T16:17:12.000Z</published>
    <updated>2016-10-22T13:19:32.000Z</updated>
    
    <content type="html"><![CDATA[<p>1：本文是一篇描述如何在Android上开始一个Kotlin的HelloWorld程序的说明文。<br>2：其实你如果你网络够给力的话，也许三分钟就可以了。当然网络不够给力，也可能十分钟还没整好~</p>
<p>好了，正文开始：<br><a id="more"></a></p>
<hr>
<p>对于开发者来说，我们正处于一个美好的时代。得益于互联网的发展、工具的进步，我们现在学习一门新技术的成本和难度都比过去低了很多。<br>假设你之前没有使用过Kotlin，那么从头开始写一个HelloWorld的app也只需要这么几步：</p>
<h2 id="首先，你要有一个Android-Studio。"><a href="#首先，你要有一个Android-Studio。" class="headerlink" title="首先，你要有一个Android Studio。"></a>首先，你要有一个Android Studio。</h2><p>我正在用的是2.2.1版本，其它版本应该也大同小异。 </p>
<h2 id="其次，安装一个Kotlin的插件。"><a href="#其次，安装一个Kotlin的插件。" class="headerlink" title="其次，安装一个Kotlin的插件。"></a>其次，安装一个Kotlin的插件。</h2><p>依次打开：Android Studio &gt; Preferences &gt; Plugins，然后选择『Browse repositories』，在搜索框中搜索Kotlin，结果列表中的『Kotlin』插件，就是我们要找的目标了。<br>点击安装，安装完成之后，重启Android Studio。<br><img src="http://ww1.sinaimg.cn/large/801b780ajw1f8y08iu63gj219s11e48p.jpg" alt="Kotlin插件"></p>
<h2 id="新建一个Android项目"><a href="#新建一个Android项目" class="headerlink" title="新建一个Android项目"></a>新建一个Android项目</h2><p>重新打开Android Studio，新建一个Android项目吧，添加一个默认的MainActivity<br>——像以前一样即可。</p>
<h2 id="Java-to-Kotlin"><a href="#Java-to-Kotlin" class="headerlink" title="Java to Kotlin"></a>Java to Kotlin</h2><p>安装完插件的AndroidStudio现在已经拥有开发Kotlin的新能力了，那么如何体现这个能力呢？<br>我们先来尝试它的转换功能：Java -&gt; Kotlin，可以把现有的java文件翻译成Kotlin文件。 </p>
<p>打开MainActivity文件，在Code菜单下面可以看到一个新的功能：Convert Java File to Kotlin File。<br><img src="http://ww1.sinaimg.cn/large/801b780ajw1f8y09xgixcj21080gkqaf.jpg" alt="Java to Kotlin"></p>
<p>点击转换，可以看到结果：<br>java文件：MainActivity.java<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.barryzhang.kotlinhello;</div><div class="line"></div><div class="line"><span class="keyword">import</span> android.support.v7.app.AppCompatActivity;</div><div class="line"><span class="keyword">import</span> android.os.Bundle;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>&#123;</div><div class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line">        setContentView(R.layout.activity_main);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>转换后的Kotlin文件：MainActivity.kt<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">package com.barryzhang.kotlinhello</div><div class="line"></div><div class="line">import android.support.v7.app.AppCompatActivity</div><div class="line">import android.os.Bundle</div><div class="line"></div><div class="line">class MainActivity : AppCompatActivity() &#123;</div><div class="line"></div><div class="line">    override fun onCreate(savedInstanceState: Bundle?) &#123;</div><div class="line">        super.onCreate(savedInstanceState)</div><div class="line">        setContentView(R.layout.activity_main)</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>Kotlin的转换功能是十分实用的，对于我们重用过去的java代码、或者网上搜索到的java代码片段很有帮助。（当然，Kotlin是直接兼容java的，如果不想转换，也可以直接调用Java的方法）</p>
<h2 id="配置gradle文件"><a href="#配置gradle文件" class="headerlink" title="配置gradle文件"></a>配置gradle文件</h2><p>MainActivity已经被转换成了Kotlin实现，但是项目目前还不可以用，还需要配置一下，让项目支持grade的编译、运行。<br>当然，这一步也不需要我们做太多工作——在java转换成Kotlin之后，打开MainActivity.kt文件，编译器会提示”Kotlin not configured”，点击一下Configure按钮，IDE就会自动帮我们配置好了！（所以说Kotlin的工具完善可不是吹的，毕竟Kotlin的老爹JetBrains就是专门做工具的啊）<br><img src="http://ww1.sinaimg.cn/large/801b780ajw1f8y0akfdzij20l10eadj5.jpg" alt="Kotlin not configured"></p>
<p>这个自动配置，实际上是做了这些改动。<br>项目的build.gradle添加：<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">buildscript &#123;</div><div class="line">    ext.kotlin_version = <span class="string">'1.0.4'</span></div><div class="line">    dependencies &#123; </div><div class="line">        classpath <span class="string">"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"</span></div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p><img src="http://ww2.sinaimg.cn/large/801b780ajw1f8y0bg5kcuj20hj06nabf.jpg" alt="gradle1"><br>module里的build.gradle添加：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">apply plugin: <span class="string">'kotlin-android'</span></div><div class="line">android &#123; </div><div class="line">    sourceSets &#123;</div><div class="line">        main.java.srcDirs += <span class="string">'src/main/kotlin'</span></div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line">dependencies &#123; </div><div class="line">    compile <span class="string">"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"</span></div><div class="line">&#125;</div><div class="line">repositories &#123;</div><div class="line">    mavenCentral()</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><img src="http://ww1.sinaimg.cn/large/801b780ajw1f8y0bhs2f7j20jx0ea0v9.jpg" alt="gradle2"></p>
<p>↑↑↑ 熟悉了之后自己手写也是阔以的。</p>
<h2 id="Run"><a href="#Run" class="headerlink" title="Run"></a>Run</h2><p>配置之后，等sync完成，就可以运行了~ （如果你sync失败或者耗时过长，赶紧检讨一下自己有没有科学上网？）<br><img src="http://ww4.sinaimg.cn/large/801b780ajw1f8y10t7bubj20bi0ktt98.jpg" alt="hello"><br>biu~起飞，欢迎来到新世界的大门。 </p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;1：本文是一篇描述如何在Android上开始一个Kotlin的HelloWorld程序的说明文。&lt;br&gt;2：其实你如果你网络够给力的话，也许三分钟就可以了。当然网络不够给力，也可能十分钟还没整好~&lt;/p&gt;
&lt;p&gt;好了，正文开始：&lt;br&gt;
    
    </summary>
    
    
      <category term="Android Kotlin" scheme="http://barryhappy.github.io/tags/Android-Kotlin/"/>
    
  </entry>
  
  <entry>
    <title>【Android】真机调试新姿势：无线连接</title>
    <link href="http://barryhappy.github.io/2016/10/09/android-wireless-debug/"/>
    <id>http://barryhappy.github.io/2016/10/09/android-wireless-debug/</id>
    <published>2016-10-09T03:56:45.000Z</published>
    <updated>2016-10-09T07:12:22.000Z</updated>
    
    <content type="html"><![CDATA[<p>在进行Android开发时，一般我们都是用usb线把手机和电脑连接起来进行调试工作。但如果你觉得这样不够酷的话，可以尝试一下无线连接，颇简单，GO！<br><a id="more"></a></p>
<h2 id="准备工作"><a href="#准备工作" class="headerlink" title="准备工作"></a>准备工作</h2><ul>
<li>手机和电脑需要在同一个局域网  </li>
<li>准备一条数据线——（what？不是说无线连接吗？！别着急，这只是第一次设置时用到的，以后再连接就不需要数据线了）</li>
<li>你需要知道怎么执行adb命令（不知道的话请自行Google）</li>
</ul>
<h2 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h2><h3 id="1-打开手机的5555端口"><a href="#1-打开手机的5555端口" class="headerlink" title="1. 打开手机的5555端口"></a>1. 打开手机的5555端口</h3><p>让手机在指定的端口可以接收到TCP/IP连接。（当然，也可以不用5555，用5556或者9999等都可以，只要你喜欢）</p>
<ol>
<li>确保手机开启了usb调试</li>
<li>用usb线把手机和电脑连接起来</li>
<li><p>执行命令：</p>
<p> <code>adb tcpip 5555</code></p>
</li>
</ol>
<p>执行成功后就可以把usb线拔掉了。</p>
<h3 id="2-找到手机的IP地址"><a href="#2-找到手机的IP地址" class="headerlink" title="2. 找到手机的IP地址"></a>2. 找到手机的IP地址</h3><p>一般在 <strong>设置-关于手机-状态信息-IP地址</strong>可以找到。<br>比如，我这里看到手机的IP地址是<em>192.168.1.108</em></p>
<h3 id="3-通过IP地址和端口连接手机"><a href="#3-通过IP地址和端口连接手机" class="headerlink" title="3. 通过IP地址和端口连接手机"></a>3. 通过IP地址和端口连接手机</h3><p>执行命令：<br>    <code>adb connect 192.168.1.108:5555</code></p>
<p>3.1 如果提示</p>
<blockquote>
<p>connected to 192.168.1.108:5555</p>
</blockquote>
<p>则表示连接成功</p>
<p>3.2 如果提示</p>
<blockquote>
<p>unable to connect to 192.168.1.108:5555: Operation timed out</p>
</blockquote>
<p>——可能你的IP地址输错了;或者adb服务不可用;或者手机跟电脑没有正确地处在一个局域网中；或者链接被代理Block了等等等…</p>
<p>3.3 如果提示 </p>
<blockquote>
<p>unable to connect to 192.168.1.108:5555: Connection refused</p>
</blockquote>
<p>—— 可能你手机的5555端口没有正确打开；或者有防火墙拦截等…</p>
<h3 id="4-如果没有连接成功"><a href="#4-如果没有连接成功" class="headerlink" title="4. 如果没有连接成功"></a>4. 如果没有连接成功</h3><p>如果出现了3.2或3.3的错误提示，或者其他任何错误，都可以尝试重启一下adb服务：</p>
<blockquote>
<p>adb kill-server</p>
</blockquote>
<p>然后再重新进行尝试。</p>
<h3 id="5-连接成功"><a href="#5-连接成功" class="headerlink" title="5. 连接成功"></a>5. 连接成功</h3><p>如果连接成功的话，执行以下命令查看当前连接的设备列表：<br>    <code>adb devices</code><br>可以看到连接的设备，像这样</p>
<blockquote>
<p>$ adb devices<br>List of devices attached<br>192.168.1.108:5555      device</p>
</blockquote>
<p>——恭喜你，从此可以摆脱对数据线的依赖了。</p>
<h2 id="总结-amp-其他"><a href="#总结-amp-其他" class="headerlink" title="总结&amp;其他"></a>总结&amp;其他</h2><ol>
<li>更换一个网络环境（比如把手机和电脑从公司带回了家里），一般只需要按照新的手机IP重新进行一下connect即可。 </li>
<li>但是如果手机重启了，就需要重新<strong>连接数据线</strong>再次开启5555端口了。</li>
<li>当然开启5555端口也并非必须通过adb，也有一些方法可以直接在手机上操作打开。但这些方法一般都需要root权限，如果你想要尝试在手机上进行开启，请注意安全。</li>
<li>有利就有弊——无线调试较有线调试而言，优势自不必说，缺点是无线数据的传输会比有线要慢一些。</li>
<li>还有一点，自从用了无线调试之后，我那常年满电的测试机，现在需要时不时的进行充电了~~~<br>o(╯□╰)o</li>
</ol>
<hr>
<p>有用的链接：<br><a href="https://developer.android.com/studio/command-line/adb.html" target="_blank" rel="external">https://developer.android.com/studio/command-line/adb.html</a><br><a href="http://stackoverflow.com/questions/23936781/unable-to-connect-android-device-over-adb-tcp-ip" target="_blank" rel="external">http://stackoverflow.com/questions/23936781/unable-to-connect-android-device-over-adb-tcp-ip</a></p>
<hr>
<p>Also in:<br><a href="http://www.barryzhang.com" target="_blank" rel="external">http://www.barryzhang.com</a><br><a href="https://barryhappy.github.io">https://barryhappy.github.io</a><br><a href="http://www.jianshu.com/users/e4607fd59d0d/latest_articles" target="_blank" rel="external">http://www.jianshu.com/users/e4607fd59d0d/latest_articles</a><br><a href="http://blog.csdn.net/barryhappy" target="_blank" rel="external">http://blog.csdn.net/barryhappy</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在进行Android开发时，一般我们都是用usb线把手机和电脑连接起来进行调试工作。但如果你觉得这样不够酷的话，可以尝试一下无线连接，颇简单，GO！&lt;br&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>【Android】WebView：onReceiveError的应用与变迁</title>
    <link href="http://barryhappy.github.io/2016/10/02/AndroidWebViewOnReceiveError/"/>
    <id>http://barryhappy.github.io/2016/10/02/AndroidWebViewOnReceiveError/</id>
    <published>2016-10-02T15:53:55.000Z</published>
    <updated>2016-10-09T07:12:51.000Z</updated>
    
    <content type="html"><![CDATA[<p><code>onReceiveError</code>是WebViewClient提供的方法，用于网页产生错误时进行回调处理。</p>
<a id="more"></a>
<h2 id="1-旧版的onReceiveError"><a href="#1-旧版的onReceiveError" class="headerlink" title="1. 旧版的onReceiveError"></a>1. 旧版的onReceiveError</h2><p>在API23之前，该方法的签名是：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceivedError</span><span class="params">(WebView view, <span class="keyword">int</span> errorCode,String description, String failingUrl)</span></span>;</div></pre></td></tr></table></figure>
<p>文档是：          </p>
<blockquote>
<p>Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). The errorCode parameter corresponds to one of the ERROR_* constants.</p>
</blockquote>
<p>简单来说，onReceivedError只有在遇到不可用的(<strong>unrecoverable</strong>)错误时，才会被调用）。<br>比如，当WebView加载链接<em>www.barryzhang.com</em>时，”不可用”的情况有可以包括有：</p>
<ul>
<li>没有网络连接  </li>
<li>连接超时  </li>
<li>找不到页面www.barryzhang.com  </li>
</ul>
<p>而下面的情况则不会被报告：</p>
<ul>
<li>网页内引用其他资源加载错误，比如图片、css不可用</li>
<li>js执行错误</li>
</ul>
<h2 id="2-应用：显示个自定义ERROR界面"><a href="#2-应用：显示个自定义ERROR界面" class="headerlink" title="2. 应用：显示个自定义ERROR界面"></a>2. 应用：显示个自定义ERROR界面</h2><p>基于以上特性，所以它可以用来处理网页加载不出来的情况，比如显示一段友好的提示语、一个重试按钮等。<br>比如像这样：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceivedError</span><span class="params">(WebView view, <span class="keyword">int</span> errorCode, String description, String failingUrl)</span> </span>&#123;</div><div class="line">	<span class="keyword">super</span>.onReceivedError(view, errorCode, description, failingUrl);</div><div class="line">	layoutError.setVisibility(View.VISIBLE); </div><div class="line">	textViewErrorMessage.setText(<span class="string">"(错误码:"</span> + errorCode + <span class="string">"  "</span> + description + <span class="string">")"</span>  );</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>——这么做的还有一个原因是，虽然默认的网页错误样式每个ROM都可能不一样，但是却是一样的丑……，来个对比图感受一下，从左到右依次是：MIUI(Android5.0.2)、Nexus5X(Android7)、以及自定义之后的效果：<br><img src="http://i.imgur.com/67ovWSv.png" alt="onReceiveErro"></p>
<h2 id="3-新版的onReceiveError"><a href="#3-新版的onReceiveError" class="headerlink" title="3. 新版的onReceiveError"></a>3. 新版的onReceiveError</h2><p>So far so good, but~<br>API23(Android6)，Google对onReceiveError进行了一次改版重载，<strong>并且把老版本的废弃了</strong>，ㄒoㄒ~~<br>签名改成了这样：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceivedError</span><span class="params">(WebView view, WebResourceRequest request, WebResourceError error)</span></span>;</div></pre></td></tr></table></figure></p>
<p>文档改成了：          </p>
<blockquote>
<p>Report web resource loading error to the host application. These errors usually indicate inability to connect to the server. Note that unlike the deprecated version of the callback,the new version will be called for any resource (iframe, image, etc), not just for the main page. Thus, it is recommended to perform minimum required work in this callback.</p>
</blockquote>
<p>新版的onReceiveError能接收到的错误更多，不再局限于之前的”不可用”的情况——好像是比之前更强大了。<br>但是，这时候如果我们依然用使用旧版本的方式来使用新版，像这样：  </p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceivedError</span><span class="params">(WebView view, WebResourceRequest request, WebResourceError error)</span> </span>&#123;</div><div class="line">    <span class="comment">// ！！在新版的onReceivedError中，沿用之前的处理逻辑（这是错误的示例！！）</span></div><div class="line">	<span class="keyword">super</span>.onReceivedError(view, errorCode, description, failingUrl);</div><div class="line">	layoutError.setVisibility(View.VISIBLE); </div><div class="line">	textViewErrorMessage.setText(<span class="string">"(错误码:"</span> + error.getErrorCode() + <span class="string">"  "</span> + error.getDescription().toString() + <span class="string">")"</span>  );</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这会导致的问题是：在Android6以上的机器上，网页中的任意一个资源获取不到（比如字体），网页就很可能显示自定义的错误界面。尤其是如果Html用了本地化技术，’ERR_FILE_NOT_FOUND’开始变得特别常见。</p>
<h2 id="4-如何像在老版本一样工作？"><a href="#4-如何像在老版本一样工作？" class="headerlink" title="4. 如何像在老版本一样工作？"></a>4. 如何像在老版本一样工作？</h2><h3 id="4-1-继续用老版本呢？"><a href="#4-1-继续用老版本呢？" class="headerlink" title="4.1 继续用老版本呢？"></a>4.1 继续用老版本呢？</h3><p>Bingo！可以，起码从目前来看，测试结果表明至少在Andoid6以及Android7上是可以工作的。<br>然而，终究，使用已废弃的API终究是不太优雅——说不定哪个版本就突然不能用了，仿佛像个定时炸弹一样。</p>
<h3 id="4-2-isForMainFrame"><a href="#4-2-isForMainFrame" class="headerlink" title="4.2 isForMainFrame"></a>4.2 isForMainFrame</h3><p>我们注意到新版的onReceivedError跟老版相比，多了一个<code>WebResourceRequest</code>参数，而<code>WebResourceRequest</code>有一个方法叫做<code>isForMainFrame</code>，描述为：</p>
<blockquote>
<p>Gets whether the request was made for the main frame<br>获取当前的网络请求是否是为<em>main frame</em>创建的.</p>
</blockquote>
<p>加上这个条件判断是来试试？</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceivedError</span><span class="params">(WebView view, WebResourceRequest request, WebResourceError error)</span> </span>&#123;</div><div class="line">	<span class="keyword">super</span>.onReceivedError(view, errorCode, description, failingUrl);</div><div class="line">	<span class="keyword">if</span>(request.isForMainFrame())&#123;<span class="comment">// 在这里加上个判断</span></div><div class="line">	    <span class="comment">// 显示错误界面</span></div><div class="line">	&#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>实验证明这个方法是有效的。   </p>
<h3 id="4-3-当然-也还有其他方法"><a href="#4-3-当然-也还有其他方法" class="headerlink" title="4.3 当然,也还有其他方法"></a>4.3 当然,也还有其他方法</h3><p>可以这样，直接上代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceivedError</span><span class="params">(WebView view, WebResourceRequest request, WebResourceError error)</span> </span>&#123;</div><div class="line">	<span class="keyword">super</span>.onReceivedError(view, errorCode, description, failingUrl);</div><div class="line">	 <span class="keyword">if</span> (request.getUrl().toString() .equals(getUrl())) &#123;<span class="comment">// 在这里加上个判断</span></div><div class="line">	    <span class="comment">// 显示错误界面</span></div><div class="line">	&#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>原理是：用请求的url来判断，如果出错的url跟webView当前加载的url一致，就显示错误页面。<br>↑↑经测试，也能通过~ </p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总而言之，最终的代码这样写，可以同时兼容新旧版本：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 旧版本，会在新版本中也可能被调用，所以加上一个判断，防止重复显示</span></div><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceivedError</span><span class="params">(WebView view, <span class="keyword">int</span> errorCode, String description, String failingUrl)</span> </span>&#123;</div><div class="line">	<span class="keyword">super</span>.onReceivedError(view, errorCode, description, failingUrl);</div><div class="line">	<span class="keyword">if</span>(Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.M)&#123;</div><div class="line">		<span class="keyword">return</span>;</div><div class="line">	&#125;	</div><div class="line">	<span class="comment">// 在这里显示自定义错误页</span></div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="comment">// 新版本，只会在Android6及以上调用</span></div><div class="line"><span class="meta">@TargetApi</span>(Build.VERSION_CODES.M)</div><div class="line"><span class="meta">@Override</span></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onReceivedError</span><span class="params">(WebView view, WebResourceRequest request, WebResourceError error)</span> </span>&#123; </div><div class="line">	<span class="keyword">super</span>.onReceivedError(view, request, error);</div><div class="line">	<span class="keyword">if</span> (request.isForMainFrame())&#123; <span class="comment">// 或者： if(request.getUrl().toString() .equals(getUrl()))</span></div><div class="line">		<span class="comment">// 在这里显示自定义错误页</span></div><div class="line">	&#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;code&gt;onReceiveError&lt;/code&gt;是WebViewClient提供的方法，用于网页产生错误时进行回调处理。&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>【Android】你可能还不知道的elevation、以及Z值等</title>
    <link href="http://barryhappy.github.io/2016/09/27/elevationAndZValue/"/>
    <id>http://barryhappy.github.io/2016/09/27/elevationAndZValue/</id>
    <published>2016-09-27T15:15:57.000Z</published>
    <updated>2016-10-09T07:13:10.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="elevation、以及Z值——是什么？"><a href="#elevation、以及Z值——是什么？" class="headerlink" title="elevation、以及Z值——是什么？"></a>elevation、以及Z值——是什么？</h2><p>众所周知，with和height是所有View的基础属性。用一个二维坐标系来描述手机屏幕，那么它们就分别表示了View在坐标系上x、y方向上值 ——这是我们一直都习惯的自然表示。<br>然而，从Android5.0(API21)开始，Google引入了一个z值，把二维坐标系强行改成了三维。  </p>
<a id="more"></a>
<p>在Android API21，新添加了一个属性：<code>android:elevation</code>，用以在xml定义View的深度(高度)，也即z方向的值。<br>除了<code>elevation</code>之外，类似于已有的translationX、translationY，也相对应地新增了一个<code>translationZ</code>，用以在属性动画中动态改变Z值（使用View.setTranslationZ()）。 </p>
<blockquote>
<p>Z = elevation + translationZ</p>
</blockquote>
<h2 id="有什么用处呢？"><a href="#有什么用处呢？" class="headerlink" title="有什么用处呢？"></a>有什么用处呢？</h2><p>在引入了这个属性之后，主要影响有两个：</p>
<ol>
<li>影响View的阴影      </li>
<li>影响View相互阻挡顺序</li>
</ol>
<h3 id="1-影响View的阴影"><a href="#1-影响View的阴影" class="headerlink" title="1. 影响View的阴影"></a>1. 影响View的阴影</h3><p>Z值会对View的阴影外观造成影响，但是不是对View大小造成影响。<br>拥有更大Z值的View会有一个更大但是更柔和的阴影——这跟我们生活的实际体验是一致的，官方给的效果图：</p>
<p><img src="http://upload-images.jianshu.io/upload_images/422451-ff967532c79cc51e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="阴影效果"></p>
<h3 id="2-影响View相互阻挡顺序"><a href="#2-影响View相互阻挡顺序" class="headerlink" title="2. 影响View相互阻挡顺序"></a>2. 影响View相互阻挡顺序</h3><p>拥有更大Z值的View会挡住Z值比较小的View——即更大Z值的View会在最上层。<br>譬如，在正常的FrameLayout中，子View的绘制顺序是从上到下，也就是说，最后一个子View会显示到最上面，如果位置跟前面的View有重合，则会盖住前面的View。 </p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">FrameLayout</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></span></div><div class="line">              <span class="attr">android:orientation</span>=<span class="string">"vertical"</span></div><div class="line">              <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></div><div class="line">              <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span>&gt;</div><div class="line">    <span class="tag">&lt;<span class="name">TextView</span></span></div><div class="line">        <span class="attr">android:id</span>=<span class="string">"@+id/viewA"</span></div><div class="line">        <span class="attr">android:background</span>=<span class="string">"#f00"</span></div><div class="line">        <span class="attr">android:text</span>=<span class="string">"A"</span></div><div class="line">        <span class="attr">android:gravity</span>=<span class="string">"center"</span></div><div class="line">        <span class="attr">android:layout_width</span>=<span class="string">"100dp"</span></div><div class="line">        <span class="attr">android:layout_height</span>=<span class="string">"100dp"</span> /&gt;</div><div class="line"></div><div class="line">    <span class="tag">&lt;<span class="name">TextView</span></span></div><div class="line">        <span class="attr">android:id</span>=<span class="string">"@+id/viewB"</span></div><div class="line">        <span class="attr">android:background</span>=<span class="string">"#0f0"</span></div><div class="line">        <span class="attr">android:text</span>=<span class="string">"B"</span></div><div class="line">        <span class="attr">android:gravity</span>=<span class="string">"center"</span></div><div class="line">        <span class="attr">android:layout_marginLeft</span>=<span class="string">"60dp"</span></div><div class="line">        <span class="attr">android:layout_marginTop</span>=<span class="string">"60dp"</span></div><div class="line">        <span class="attr">android:layout_width</span>=<span class="string">"100dp"</span></div><div class="line">        <span class="attr">android:layout_height</span>=<span class="string">"100dp"</span> /&gt;</div><div class="line"></div><div class="line">    <span class="comment">&lt;!--ViewC有了一个elevation属性 --&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">TextView</span></span></div><div class="line">        <span class="attr">android:id</span>=<span class="string">"@+id/viewC"</span></div><div class="line">        <span class="attr">android:layout_marginLeft</span>=<span class="string">"180dp"</span></div><div class="line">        <span class="attr">android:background</span>=<span class="string">"#f00"</span></div><div class="line">        <span class="attr">android:text</span>=<span class="string">"C"</span></div><div class="line">        <span class="attr">android:gravity</span>=<span class="string">"center"</span></div><div class="line">        <span class="attr">android:elevation</span>=<span class="string">"1dp"</span></div><div class="line">        <span class="attr">android:layout_width</span>=<span class="string">"100dp"</span></div><div class="line">        <span class="attr">android:layout_height</span>=<span class="string">"100dp"</span> /&gt;</div><div class="line">    <span class="tag">&lt;<span class="name">TextView</span></span></div><div class="line">        <span class="attr">android:id</span>=<span class="string">"@+id/viewD"</span></div><div class="line">        <span class="attr">android:background</span>=<span class="string">"#0f0"</span></div><div class="line">        <span class="attr">android:text</span>=<span class="string">"D"</span></div><div class="line">        <span class="attr">android:gravity</span>=<span class="string">"center"</span></div><div class="line">        <span class="attr">android:layout_marginLeft</span>=<span class="string">"240dp"</span></div><div class="line">        <span class="attr">android:layout_marginTop</span>=<span class="string">"60dp"</span></div><div class="line">        <span class="attr">android:layout_width</span>=<span class="string">"100dp"</span></div><div class="line">        <span class="attr">android:layout_height</span>=<span class="string">"100dp"</span> /&gt;</div><div class="line"><span class="tag">&lt;/<span class="name">FrameLayout</span>&gt;</span></div></pre></td></tr></table></figure>
<p>比如，上面的xml代码，效果如下图所示：</p>
<p><img src="http://upload-images.jianshu.io/upload_images/422451-92a1b4c446cf3949.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="对比图"></p>
<ul>
<li>先看ViewA、ViewB，因为ViewB是第二个子View，ViewA是第一个，所以B会覆盖在A的上面。 </li>
<li>然后ViewC、D，跟AB相比较，区别就在于ViewC多了一个elevation属性，有了一个比ViewD更大的Z值，所以，即使它在ViewD的前面，但是依然能够盖住D~ （ViewC：因为朕站的更高，哇哈哈~ ）</li>
</ul>
<h2 id="还有CardView"><a href="#还有CardView" class="headerlink" title="还有CardView"></a>还有CardView</h2><p>为什么会提到CardView呢？因为CardView可以算作是一个官方使用的Z值的示例了。 </p>
<blockquote>
<p>CardView 扩展 FrameLayout 类别并让您能够显示卡片内的信息，这些信息在整个平台中拥有一致的呈现方式。CardView 小组件可拥有阴影和圆角。</p>
</blockquote>
<p>CardView是在v7支持包里面的，在API<21时，实现方式是padding+绘制阴影；而在在api>=21时，就是用elevation属性来实现的啦！</21时，实现方式是padding+绘制阴影；而在在api></p>
<p>知道了这些，也可以解释一些以前觉得诡异的View覆盖的问题了，比如：我明明在CardView的上层放了一个Button，为什么Button看不到了？？？<br>（傻孩子，因为CardView默认的elevation的呀）</p>
<p><img src="http://upload-images.jianshu.io/upload_images/422451-8b1ee8a52937f10c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt=""></p>
<hr>
<p>可能有用的链接：<br><a href="https://developer.android.com/training/material/shadows-clipping.html" target="_blank" rel="external">https://developer.android.com/training/material/shadows-clipping.html</a><br><a href="https://developer.android.com/training/material/lists-cards.html" target="_blank" rel="external">https://developer.android.com/training/material/lists-cards.html</a><br><a href="https://developer.android.com/reference/android/support/v7/widget/CardView.html" target="_blank" rel="external">https://developer.android.com/reference/android/support/v7/widget/CardView.html</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;elevation、以及Z值——是什么？&quot;&gt;&lt;a href=&quot;#elevation、以及Z值——是什么？&quot; class=&quot;headerlink&quot; title=&quot;elevation、以及Z值——是什么？&quot;&gt;&lt;/a&gt;elevation、以及Z值——是什么？&lt;/h2&gt;&lt;p&gt;众所周知，with和height是所有View的基础属性。用一个二维坐标系来描述手机屏幕，那么它们就分别表示了View在坐标系上x、y方向上值 ——这是我们一直都习惯的自然表示。&lt;br&gt;然而，从Android5.0(API21)开始，Google引入了一个z值，把二维坐标系强行改成了三维。  &lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
  <entry>
    <title>hello-from-hexo</title>
    <link href="http://barryhappy.github.io/2016/08/28/hello-from-hexo/"/>
    <id>http://barryhappy.github.io/2016/08/28/hello-from-hexo/</id>
    <published>2016-08-28T13:51:29.000Z</published>
    <updated>2016-10-09T07:13:33.000Z</updated>
    
    <content type="html"><![CDATA[<p>Hello , This is from hexo .</p>
<a id="more"></a>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Hello , This is from hexo .&lt;/p&gt;
    
    </summary>
    
    
  </entry>
  
</feed>
