<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Kevin Jern</title>
  <subtitle>踏破芒鞋，烟雨任平生。</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://KevinJe.github.io/"/>
  <updated>2017-09-22T12:57:56.348Z</updated>
  <id>https://KevinJe.github.io/</id>
  
  <author>
    <name>Kevin Jern</name>
    <email>kevinvin1997@gmail.com</email>
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Android中的动画二之使用进阶</title>
    <link href="https://KevinJe.github.io/2017/09/05/Android%E4%B8%AD%E7%9A%84%E5%8A%A8%E7%94%BB%E4%BA%8C%E4%B9%8B%E4%BD%BF%E7%94%A8%E8%BF%9B%E9%98%B6/"/>
    <id>https://KevinJe.github.io/2017/09/05/Android中的动画二之使用进阶/</id>
    <published>2017-09-05T12:23:56.000Z</published>
    <updated>2017-09-22T12:57:56.348Z</updated>
    
    <content type="html"><![CDATA[<p>上一篇文章，大概介绍了一下View Animation以及Property Animation的使用。这一篇，在简单使用的基础之上，进行一点高级的技巧。</p>
<h4 id="TypeEvaluator"><a href="#TypeEvaluator" class="headerlink" title="TypeEvaluator"></a>TypeEvaluator</h4><p>当我们使用ValueAnimator或者ObjectAnimator时，会有一个ofObject()方法和setEvaluator()。在这个方法中是有一个TypeEvaluator参数的。。那么这个TypeEvaluator的作用到底是什么呢？我们在使用ValueAnimator.ofFloat()时是会向其中传入开始值以及结束值的。然后系统就会帮助我们平滑计算出整个过程的数值。</p>
<a id="more"></a>
<p>我们可以看一下系统中的实现，系统中其实实现了几个TypeEvaluator。当我们使用ofFloat()时调用的默认是FloatEvaluator。使用ofInt()默认的是IntEvaluator。使用ofArgb()默认的是ArgbEvaluator。</p>
<figure class="highlight zephir"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * This evaluator can be used to perform type interpolation between float values.</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FloatEvaluator</span> <span class="keyword">implements</span> <span class="title">TypeEvaluator</span>&lt;<span class="title">Number</span>&gt; </span>&#123;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * This function returns the result of linearly interpolating the start and end values, with</div><div class="line">     * fraction representing the proportion between the start and end values. The</div><div class="line">     * calculation is a simple parametric calculation: result = x0 + t * (v1 - v0),</div><div class="line">     * where x0 is startValue, x1 is endValue,and t is fraction.</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> fraction   The fraction from the starting to the ending values</div><div class="line">     * <span class="doctag">@param</span> startValue The start value; should be of type float or Float</div><div class="line">     * <span class="doctag">@param</span> endValue   The end value; should be of type float or Float</div><div class="line">     * <span class="doctag">@return</span> A linear interpolation between the start and end values, given the fraction parameter.</div><div class="line">     */</div><div class="line">    <span class="keyword">public</span> <span class="keyword">Float</span> evaluate(<span class="keyword">float</span> fraction, Number startValue, Number endValue) &#123;</div><div class="line">        <span class="comment">//开始值</span></div><div class="line">        <span class="keyword">float</span> startFloat = startValue.floatValue();</div><div class="line">        <span class="comment">//开始值 + 当前进度 *（结束值 - 开始值）</span></div><div class="line">        <span class="keyword">return</span> startFloat + fraction * (endValue.floatValue() - startFloat);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>可以看到FloatEvaluator实现了TypeEvaluator，并且TypeEvaluator其中只有一个方法<code>evaluate(float fraction, Number startValue, Number endValue)</code>。这个方法中有三个值，后两个分别是我们在ofFloat(startValue,endValue)传进来的。而第一个值fraction呢？它其实是一个当前动画执行进度的这么一个参数。所以，上面evaluator方法就很明白了。就是fraction在动画执行过程中是变化的执行进度，假如现在开始值是0，结束值是100，动画执行了50%。那么此时返回的值就应该是<strong>0 + 0.5 * （100 - 0）= 50</strong>。所以，TypeEvaluator就是<strong>执行如何从初始值 过渡到 结束值 的逻辑</strong>。那么，这个变化的fraction从哪里来呢？这就要用到Interpolator了。我们后面介绍。</p>
<p>知道了原理，我们就可以打造自己的TypeEvaluator了。</p>
<figure class="highlight cs"><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></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">Point</span> &#123;</div><div class="line">    <span class="comment">// x 坐标</span></div><div class="line">    <span class="keyword">private</span> <span class="keyword">float</span> x;</div><div class="line">    <span class="comment">// y 坐标</span></div><div class="line">    <span class="keyword">private</span> <span class="keyword">float</span> y;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Point</span>(<span class="params"><span class="keyword">float</span> x, <span class="keyword">float</span> y</span>) </span>&#123;</div><div class="line">        <span class="keyword">this</span>.x = x;</div><div class="line">        <span class="keyword">this</span>.y = y;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">float</span> <span class="title">getX</span>(<span class="params"></span>) </span>&#123;</div><div class="line">        <span class="keyword">return</span> x;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">float</span> <span class="title">getY</span>(<span class="params"></span>) </span>&#123;</div><div class="line">        <span class="keyword">return</span> y;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这里先新建一个Point类，记录View的x坐标以及y坐标。</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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">PointEvaluator</span> <span class="keyword">implements</span> <span class="title">TypeEvaluator</span> </span>&#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="function"><span class="keyword">public</span> Object <span class="title">evaluate</span><span class="params">(<span class="keyword">float</span> fraction, Object startValue, Object endValue)</span> </span>&#123;</div><div class="line">        <span class="comment">//得到开始值</span></div><div class="line">        Point startPoint = (Point) startValue;</div><div class="line">        <span class="comment">//得到结束值</span></div><div class="line">        Point endPoint = (Point) endValue;</div><div class="line">        <span class="comment">//动画运行中的 x 坐标</span></div><div class="line">        <span class="keyword">float</span> x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());</div><div class="line">        <span class="comment">//动画执行过程中的 y 坐标</span></div><div class="line">        <span class="keyword">float</span> y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());</div><div class="line">        <span class="comment">//封装成Point对象</span></div><div class="line">        Point point = <span class="keyword">new</span> Point(x, y);</div><div class="line">        <span class="keyword">return</span> point;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>自己定义了一个PointEvaluator，在其中计算了在动画执行过程中View的坐标值，并封装为Point对象。</p>
<figure class="highlight pony"><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="comment">//开始点坐标</span></div><div class="line"><span class="type">Point</span> startPoint = <span class="function"><span class="keyword">new</span> <span class="title">Point</span>(getWidth() / 2, <span class="title">RADIUS</span>);</span></div><div class="line"><span class="comment">//结束点坐标</span></div><div class="line"><span class="title">Point</span> <span class="title">endPoint</span> = <span class="title">new</span> <span class="title">Point</span>(getWidth() / 2, <span class="title">getHeight</span>() - <span class="title">RADIUS</span>);</div><div class="line"><span class="comment">//使用自定义的PointEvaluator</span></div><div class="line"><span class="title">ValueAnimator</span> <span class="title">animator</span> = <span class="title">ValueAnimator</span>.<span class="title">ofObject</span>(new <span class="type">PointEvaluator</span>(), <span class="title">startPoint</span>, <span class="title">endPoint</span>);</div><div class="line"><span class="comment">//注册监听器</span></div><div class="line"><span class="title">animator</span>.<span class="title">addUpdateListener</span>(new <span class="type">ValueAnimator</span>.<span class="type">AnimatorUpdateListener</span>() &#123;</div><div class="line">    @<span class="title">Override</span></div><div class="line">    <span class="title">public</span> <span class="title">void</span> <span class="title">onAnimationUpdate</span>(<span class="type">ValueAnimator</span> animation) &#123;</div><div class="line">        <span class="comment">//不断的取到点的值，其中包含 x 以及 y 坐标</span></div><div class="line">        <span class="title">currentPoint</span> = (<span class="type">Point</span>) <span class="title">animation</span>.<span class="title">getAnimatedValue</span>();</div><div class="line">        <span class="comment">//刷新视图，不断回调onDraw()方法</span></div><div class="line">        <span class="title">invalidate</span>();</div><div class="line">    &#125;</div><div class="line">&#125;);</div></pre></td></tr></table></figure>
<p>这里使用ofObject()方法，传入自定义的TypeEvaluator，开始点，结束点。之后在监听器中不断的取值，这个值就是我们在自定义TypeEvaluator中计算出来的x,y值。然后，得到Point对象中，就含有x,y值。</p>
<p>接下来就是取出Point中的x,y值，然后不断的在onDraw()方法中绘制，就出现了一个圆自上而下运动。<br><figure class="highlight mel"><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="keyword">float</span> x = currentPoint.getX();</div><div class="line"><span class="keyword">float</span> y = currentPoint.getY();</div><div class="line"><span class="keyword">canvas</span>.drawCircle(x, y, RADIUS, mPaint);</div></pre></td></tr></table></figure></p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-1216e9127aee8072.gif?imageMogr2/auto-orient/strip" alt="Animation.gif"></p>
<p>现在，让我们来做一些修改，然小球在下落过程中不断变换颜色。</p>
<figure class="highlight pony"><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="comment">//应用系统的 ArgbEvaluator  开始值：0xffffff00 ，黄色  结束值：0xff00ff00 ， 绿色</span></div><div class="line"><span class="type">ValueAnimator</span> colorAnimator = <span class="type">ValueAnimator</span>.ofObject(<span class="function"><span class="keyword">new</span> <span class="title">ArgbEvaluator</span>(), 0<span class="title">xffffff00</span>, 0<span class="title">xff00ff00</span>);</span></div><div class="line"><span class="comment">//设置监听器</span></div><div class="line"><span class="title">colorAnimator</span>.<span class="title">addUpdateListener</span>(new <span class="type">ValueAnimator</span>.<span class="type">AnimatorUpdateListener</span>() &#123;</div><div class="line">    @<span class="title">Override</span></div><div class="line">    <span class="title">public</span> <span class="title">void</span> <span class="title">onAnimationUpdate</span>(<span class="type">ValueAnimator</span> animation) &#123;</div><div class="line">        <span class="comment">//不断得到过度的颜色值</span></div><div class="line">        <span class="title">paintColor</span> = (int) <span class="title">animation</span>.<span class="title">getAnimatedValue</span>();</div><div class="line">        <span class="comment">//刷新视图，不断调用onDraw()方法</span></div><div class="line">        <span class="title">invalidate</span>();</div><div class="line">    &#125;</div><div class="line">&#125;);</div><div class="line"><span class="comment">//动画时长</span></div><div class="line"><span class="title">colorAnimator</span>.<span class="title">setDuration</span>(<span class="number">5000</span>);</div><div class="line"><span class="comment">//开始动画</span></div><div class="line"><span class="title">colorAnimator</span>.<span class="title">start</span>();</div></pre></td></tr></table></figure>
<p>上面的实现是应用了系统提供的ArgbEvaluator()，这个方法是系统帮助我们处理颜色过渡的，只要我们提供颜色的开始值以及结束值。之后在动画监听中得到颜色过渡中的值，并且设置给画笔就可以了：</p>
<figure class="highlight abnf"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mPaint.setColor(paintColor)<span class="comment">;</span></div></pre></td></tr></table></figure>
<p>当然，也可以这样：</p>
<figure class="highlight pony"><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></pre></td><td class="code"><pre><div class="line"><span class="type">ValueAnimator</span> colorAnimator = <span class="type">ValueAnimator</span>.ofArgb(<span class="number">0xffffff00</span>, <span class="number">0xff00ff00</span>);</div><div class="line">colorAnimator.addUpdateListener(<span class="function"><span class="keyword">new</span> <span class="title">ValueAnimator</span>.<span class="title">AnimatorUpdateListener</span>() &#123;</span></div><div class="line">    @<span class="title">Override</span></div><div class="line">    <span class="title">public</span> <span class="title">void</span> <span class="title">onAnimationUpdate</span>(<span class="type">ValueAnimator</span> animation) &#123;</div><div class="line">        <span class="title">paintColor</span> = (int) <span class="title">animation</span>.<span class="title">getAnimatedValue</span>();</div><div class="line">        <span class="title">invalidate</span>();</div><div class="line">    &#125;</div><div class="line">&#125;);</div><div class="line"><span class="title">colorAnimator</span>.<span class="title">setDuration</span>(<span class="number">5000</span>);</div><div class="line"><span class="title">colorAnimator</span>.<span class="title">start</span>();</div></pre></td></tr></table></figure>
<p>看一下，两者的实现：</p>
<p>ofObject:</p>
<figure class="highlight cpp"><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">static</span> ValueAnimator <span class="title">ofObject</span><span class="params">(TypeEvaluator evaluator, Object... values)</span> </span>&#123;</div><div class="line">      ValueAnimator anim = <span class="keyword">new</span> ValueAnimator();</div><div class="line">      anim.setObjectValues(values);</div><div class="line">      anim.setEvaluator(evaluator);</div><div class="line">      <span class="keyword">return</span> anim;</div><div class="line">  &#125;</div></pre></td></tr></table></figure>
<p>ofArgb:</p>
<figure class="highlight cpp"><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">static</span> ValueAnimator <span class="title">ofArgb</span><span class="params">(<span class="keyword">int</span>... values)</span> </span>&#123;</div><div class="line">       ValueAnimator anim = <span class="keyword">new</span> ValueAnimator();</div><div class="line">       anim.setIntValues(values);</div><div class="line">       anim.setEvaluator(ArgbEvaluator.getInstance());</div><div class="line">       <span class="keyword">return</span> anim;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>看一下，其实实现相同的。</p>
<p>以上都是利用ValueAnimator实现的，那么ObjectAnimator可以吗？</p>
<p>上篇说过ObjectAnimator是通过调用属性的setter和getter方法改变属性的值，实现动画效果的。所以，我们需要自己写一个setter以及getter方法。</p>
<figure class="highlight processing"><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">private</span> <span class="built_in">int</span> <span class="built_in">color</span>;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="built_in">int</span> getColor() &#123;</div><div class="line">    <span class="keyword">return</span> <span class="built_in">color</span>;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="keyword">void</span> setColor(<span class="built_in">int</span> <span class="built_in">color</span>) &#123;</div><div class="line">    <span class="keyword">this</span>.<span class="built_in">color</span> = <span class="built_in">color</span>;</div><div class="line">    <span class="comment">//设置画笔颜色</span></div><div class="line">    mPaint.setColor(<span class="built_in">color</span>);</div><div class="line">    <span class="comment">//刷新视图，不断回调onDraw()方法</span></div><div class="line">    invalidate();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这里，我们使用int值表现画笔的颜色，并在setter方法中将其设置给画笔。最后，通过 invalidate()，不断调用onDraw()方法，实现颜色过渡的动画效果。</p>
<p>接下来，就是如何调用color了，如何对给定的颜色进行过渡了。所以，这里还要请出TypeEvaluator了。</p>
<figure class="highlight fortran"><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></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">class</span> ColorEvaluator implements TypeEvaluator &#123;</div><div class="line">    @Override</div><div class="line">    <span class="keyword">public</span> Object evaluate(<span class="built_in">float</span> <span class="built_in">fraction</span>, Object startValue, Object endValue) &#123;</div><div class="line">   </div><div class="line">        <span class="built_in">int</span> startInt = (<span class="keyword">Integer</span>) startValue;</div><div class="line">        <span class="built_in">int</span> startA = (startInt &gt;&gt; <span class="number">24</span>) &amp; xff;</div><div class="line">        <span class="built_in">int</span> startR = (startInt &gt;&gt; <span class="number">16</span>) &amp; xff;</div><div class="line">        <span class="built_in">int</span> startG = (startInt &gt;&gt; <span class="number">8</span>) &amp; xff;</div><div class="line">        <span class="built_in">int</span> startB = startInt &amp; xff;</div><div class="line"></div><div class="line">        <span class="built_in">int</span> endInt = (<span class="keyword">Integer</span>) endValue;</div><div class="line">        <span class="built_in">int</span> endA = (endInt &gt;&gt; <span class="number">24</span>) &amp; xff;</div><div class="line">        <span class="built_in">int</span> endR = (endInt &gt;&gt; <span class="number">16</span>) &amp; xff;</div><div class="line">        <span class="built_in">int</span> endG = (endInt &gt;&gt; <span class="number">8</span>) &amp; xff;</div><div class="line">        <span class="built_in">int</span> endB = endInt &amp; xff;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> (<span class="built_in">int</span>)((startA + (<span class="built_in">int</span>)(<span class="built_in">fraction</span> * (endA - startA))) &lt;&lt; <span class="number">24</span>) |</div><div class="line">                (<span class="built_in">int</span>)((startR + (<span class="built_in">int</span>)(<span class="built_in">fraction</span> * (endR - startR))) &lt;&lt; <span class="number">16</span>) |</div><div class="line">                (<span class="built_in">int</span>)((startG + (<span class="built_in">int</span>)(<span class="built_in">fraction</span> * (endG - startG))) &lt;&lt; <span class="number">8</span>) |</div><div class="line">                (<span class="built_in">int</span>)((startB + (<span class="built_in">int</span>)(<span class="built_in">fraction</span> * (endB - startB))));</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这里我们就借鉴一下系统的ArgbEvaluator的实现方法。</p>
<p>ArgbEvaluator：</p>
<figure class="highlight zephir"><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><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * This evaluator can be used to perform type interpolation between integer</div><div class="line"> * values that represent ARGB colors.</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ArgbEvaluator</span> <span class="keyword">implements</span> <span class="title">TypeEvaluator</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> ArgbEvaluator sInstance = <span class="keyword">new</span> ArgbEvaluator();</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * Returns an instance of &lt;code&gt;ArgbEvaluator&lt;/code&gt; that may be used in</div><div class="line">     * &#123;<span class="doctag">@link</span> ValueAnimator#setEvaluator(TypeEvaluator)&#125;. The same instance may</div><div class="line">     * be used in multiple &lt;code&gt;Animator&lt;/code&gt;s because it holds no state.</div><div class="line">     * <span class="doctag">@return</span> An instance of &lt;code&gt;ArgbEvalutor&lt;/code&gt;.</div><div class="line">     *</div><div class="line">     * <span class="doctag">@hide</span></div><div class="line">     */</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> ArgbEvaluator getInstance() &#123;</div><div class="line">        <span class="keyword">return</span> sInstance;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * This function returns the calculated in-between value for a color</div><div class="line">     * given integers that represent the start and end values in the four</div><div class="line">     * bytes of the 32-bit int. Each channel is separately linearly interpolated</div><div class="line">     * and the resulting calculated values are recombined into the return value.</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> fraction The fraction from the starting to the ending values</div><div class="line">     * <span class="doctag">@param</span> startValue A 32-bit int value representing colors in the</div><div class="line">     * separate bytes of the parameter</div><div class="line">     * <span class="doctag">@param</span> endValue A 32-bit int value representing colors in the</div><div class="line">     * separate bytes of the parameter</div><div class="line">     * <span class="doctag">@return</span> A value that is calculated to be the linearly interpolated</div><div class="line">     * result, derived by separating the start and end values into separate</div><div class="line">     * color channels and interpolating each one separately, recombining the</div><div class="line">     * resulting values in the same way.</div><div class="line">     */</div><div class="line">    <span class="keyword">public</span> Object evaluate(<span class="keyword">float</span> fraction, Object startValue, Object endValue) &#123;</div><div class="line">        <span class="keyword">int</span> startInt = (Integer) startValue;</div><div class="line">        <span class="keyword">int</span> startA = (startInt &gt;&gt; <span class="number">24</span>) &amp; <span class="number">0xff</span>;</div><div class="line">        <span class="keyword">int</span> startR = (startInt &gt;&gt; <span class="number">16</span>) &amp; <span class="number">0xff</span>;</div><div class="line">        <span class="keyword">int</span> startG = (startInt &gt;&gt; <span class="number">8</span>) &amp; <span class="number">0xff</span>;</div><div class="line">        <span class="keyword">int</span> startB = startInt &amp; <span class="number">0xff</span>;</div><div class="line"></div><div class="line">        <span class="keyword">int</span> endInt = (Integer) endValue;</div><div class="line">        <span class="keyword">int</span> endA = (endInt &gt;&gt; <span class="number">24</span>) &amp; <span class="number">0xff</span>;</div><div class="line">        <span class="keyword">int</span> endR = (endInt &gt;&gt; <span class="number">16</span>) &amp; <span class="number">0xff</span>;</div><div class="line">        <span class="keyword">int</span> endG = (endInt &gt;&gt; <span class="number">8</span>) &amp; <span class="number">0xff</span>;</div><div class="line">        <span class="keyword">int</span> endB = endInt &amp; <span class="number">0xff</span>;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>)((startA + (<span class="keyword">int</span>)(fraction * (endA - startA))) &lt;&lt; <span class="number">24</span>) |</div><div class="line">                (<span class="keyword">int</span>)((startR + (<span class="keyword">int</span>)(fraction * (endR - startR))) &lt;&lt; <span class="number">16</span>) |</div><div class="line">                (<span class="keyword">int</span>)((startG + (<span class="keyword">int</span>)(fraction * (endG - startG))) &lt;&lt; <span class="number">8</span>) |</div><div class="line">                (<span class="keyword">int</span>)((startB + (<span class="keyword">int</span>)(fraction * (endB - startB))));</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>我们来看一下它的颜色过渡的算法：</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-985aab92aa902691.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="ARGB.png"></p>
<p>看一下上图，经过四次与运算得到了颜色值，两位一个颜色值。开始颜色值和结束颜色值获取方式一致。最后返回 <strong>开始颜色值 + 当前进度值 * （结束颜色值 - 开始颜色值） </strong>。这样之后，又左移刚才右移的位数，进行还原。然后，四组有做了或运算。这样一番，就得到了颜色的过渡效果。</p>
<p>分析完了之后，到了使用之时：</p>
<figure class="highlight pony"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">//开始点坐标</span></div><div class="line"><span class="type">Point</span> startPoint = <span class="function"><span class="keyword">new</span> <span class="title">Point</span>(getWidth() / 2, <span class="title">RADIUS</span>);</span></div><div class="line"><span class="comment">//结束点坐标</span></div><div class="line"><span class="title">Point</span> <span class="title">endPoint</span> = <span class="title">new</span> <span class="title">Point</span>(getWidth() / 2, <span class="title">getHeight</span>() - <span class="title">RADIUS</span>);</div><div class="line"><span class="comment">//使用自定义的PointEvaluator</span></div><div class="line"><span class="title">ValueAnimator</span> <span class="title">animator</span> = <span class="title">ValueAnimator</span>.<span class="title">ofObject</span>(new <span class="type">PointEvaluator</span>(), <span class="title">startPoint</span>, <span class="title">endPoint</span>);</div><div class="line"><span class="comment">//注册监听器</span></div><div class="line"><span class="title">animator</span>.<span class="title">addUpdateListener</span>(new <span class="type">ValueAnimator</span>.<span class="type">AnimatorUpdateListener</span>() &#123;</div><div class="line">    @<span class="title">Override</span></div><div class="line">    <span class="title">public</span> <span class="title">void</span> <span class="title">onAnimationUpdate</span>(<span class="type">ValueAnimator</span> animation) &#123;</div><div class="line">        <span class="comment">//不断的取到点的值，其中包含 x 以及 y 坐标</span></div><div class="line">        <span class="title">currentPoint</span> = (<span class="type">Point</span>) <span class="title">animation</span>.<span class="title">getAnimatedValue</span>();</div><div class="line">        <span class="comment">//刷新视图，不断回调onDraw()方法</span></div><div class="line">        <span class="title">invalidate</span>();</div><div class="line">    &#125;</div><div class="line">&#125;);</div><div class="line"></div><div class="line"><span class="comment">//在ofObject中使用 自己定义的 TypeEvaluator 开始值：0xffffff00 ，黄色  结束值：0xff00ff00 ，绿色</span></div><div class="line"><span class="title">ObjectAnimator</span> <span class="title">colorAnim</span> = <span class="title">ObjectAnimator</span>.<span class="title">ofObject</span>(this, "color", new <span class="type">ColorEvaluator</span>(), 0<span class="title">xffffff00</span>, 0<span class="title">xff00ff00</span>);</div><div class="line"><span class="comment">//使用AnimatorSet，组合两个动画</span></div><div class="line"><span class="title">AnimatorSet</span> <span class="title">animatorSet</span> = <span class="title">new</span> <span class="title">AnimatorSet</span>();</div><div class="line"><span class="comment">//两个动画同时执行</span></div><div class="line"><span class="title">animatorSet</span>.<span class="title">play</span>(animator).<span class="title">with</span>(colorAnim);</div><div class="line"><span class="title">animatorSet</span>.<span class="title">setDuration</span>(<span class="number">5000</span>);</div><div class="line"><span class="comment">//开始动画</span></div><div class="line"><span class="title">animatorSet</span>.<span class="title">start</span>();</div></pre></td></tr></table></figure>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-cd64dc5a628d7ddc.gif?imageMogr2/auto-orient/strip" alt=""></p>
<h4 id="Interpolator"><a href="#Interpolator" class="headerlink" title="Interpolator"></a>Interpolator</h4><p>Interpolator，插值器。与前面的TypeEvaluator，估值器。一起实现了属性动画的各种效果。与TypeEvaluator根据开始值以及结束值进行平滑的过渡不同的是，Interpolator是用来决定值得变化趋势的，即可以先加速后减速，也可以先减速后加速。当我们步设置任何Interpolator时，系统会默认使用AccelerateDecelerateInterpolator的。这个AccelerateDecelerateInterpolator就是一个先加速后减速的效果的。</p>
<figure class="highlight applescript"><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></pre></td><td class="code"><pre><div class="line">/**</div><div class="line"> * A <span class="built_in">time</span> interpolator defines <span class="keyword">the</span> rate <span class="keyword">of</span> change <span class="keyword">of</span> an animation. This allows animations</div><div class="line"> * <span class="keyword">to</span> have non-linear motion, such <span class="keyword">as</span> acceleration <span class="keyword">and</span> deceleration.</div><div class="line"> */</div><div class="line">public interface TimeInterpolator &#123;</div><div class="line"></div><div class="line">    /**</div><div class="line">     * Maps a value representing <span class="keyword">the</span> elapsed fraction <span class="keyword">of</span> an animation <span class="keyword">to</span> a value <span class="keyword">that</span> represents</div><div class="line">     * <span class="keyword">the</span> interpolated fraction. This interpolated value <span class="keyword">is</span> <span class="keyword">then</span> multiplied <span class="keyword">by</span> <span class="keyword">the</span> change <span class="keyword">in</span></div><div class="line">     * value <span class="keyword">of</span> an animation <span class="keyword">to</span> derive <span class="keyword">the</span> animated value <span class="keyword">at</span> <span class="keyword">the</span> current elapsed animation <span class="built_in">time</span>.</div><div class="line">     *</div><div class="line">     * @param input A value <span class="keyword">between</span> <span class="number">0</span> <span class="keyword">and</span> <span class="number">1.0</span> indicating our current point</div><div class="line">     *        <span class="keyword">in</span> <span class="keyword">the</span> animation <span class="keyword">where</span> <span class="number">0</span> represents <span class="keyword">the</span> start <span class="keyword">and</span> <span class="number">1.0</span> represents</div><div class="line">     *        <span class="keyword">the</span> <span class="keyword">end</span></div><div class="line">     * @<span class="literal">return</span> The interpolation value. This value can be more than <span class="number">1.0</span> <span class="keyword">for</span></div><div class="line">     *         interpolators which overshoot their targets, <span class="keyword">or</span> <span class="keyword">less than</span> <span class="number">0</span> <span class="keyword">for</span></div><div class="line">     *         interpolators <span class="keyword">that</span> undershoot their targets.</div><div class="line">     */</div><div class="line">    float getInterpolation(float input);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>TimeInterpolator 实现了很多的类，AccelerateDecelerateInterpolator也是其中之一。看一下，实现方法中就一个getInterpolation(float input)方法。所以，想要自定义Interpolator 只要处理这一个方法就行了。</p>
<p>下面看一个最简单的线性的Interpolator：</p>
<figure class="highlight aspectj"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line"> * An interpolator where the rate of change is constant</div><div class="line"> */</div><div class="line"><span class="meta">@HasNativeInterpolator</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LinearInterpolator</span> <span class="keyword">extends</span> <span class="title">BaseInterpolator</span> <span class="keyword">implements</span> <span class="title">NativeInterpolatorFactory</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LinearInterpolator</span><span class="params">()</span> </span>&#123;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LinearInterpolator</span><span class="params">(Context context, AttributeSet attrs)</span> </span>&#123;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">float</span> <span class="title">getInterpolation</span><span class="params">(<span class="keyword">float</span> input)</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> input;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">long</span> <span class="title">createNativeInterpolator</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="function"><span class="keyword">return</span> NativeInterpolatorFactoryHelper.<span class="title">createLinearInterpolator</span><span class="params">()</span></span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>之所以说其简单，是因为getInterpolation中只是返回了input未做任何处理。所以，其值得变化率是常量，就是一个匀速运动。</p>
<p>再看一下AccelerateDecelerateInterpolator的实现：</p>
<figure class="highlight aspectj"><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="comment">/**</span></div><div class="line"> * An interpolator where the rate of change starts and ends slowly but</div><div class="line"> * accelerates through the middle.</div><div class="line"> */</div><div class="line"><span class="meta">@HasNativeInterpolator</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AccelerateDecelerateInterpolator</span> <span class="keyword">extends</span> <span class="title">BaseInterpolator</span></span></div><div class="line">        <span class="keyword">implements</span> <span class="title">NativeInterpolatorFactory</span> &#123;</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AccelerateDecelerateInterpolator</span><span class="params">()</span> </span>&#123;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@SuppressWarnings</span>(&#123;<span class="string">"UnusedDeclaration"</span>&#125;)</div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AccelerateDecelerateInterpolator</span><span class="params">(Context context, AttributeSet attrs)</span> </span>&#123;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">float</span> <span class="title">getInterpolation</span><span class="params">(<span class="keyword">float</span> input)</span> </span>&#123;</div><div class="line">        <span class="keyword">return</span> (<span class="keyword">float</span>)(Math.cos((input + <span class="number">1</span>) * Math.PI) / <span class="number">2.0</span>f) + <span class="number">0.5</span>f;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/** <span class="doctag">@hide</span> */</span></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">long</span> <span class="title">createNativeInterpolator</span><span class="params">()</span> </span>&#123;</div><div class="line">        <span class="function"><span class="keyword">return</span> NativeInterpolatorFactoryHelper.<span class="title">createAccelerateDecelerateInterpolator</span><span class="params">()</span></span>;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>可以看到，这个AccelerateDecelerateInterpolator实现的就要复杂了，用到了数学中的余弦函数来模拟先加速再减速的过程。因为input的值是从0到1的。0代表动画开始点，1代表动画结束：</p>
<blockquote>
<p>A value between 0 and 1.0 indicating our current point  in the animation where 0 represents the start and 1.0 represents the end</p>
</blockquote>
<p>所以，整个余弦之内的取值是由 π 到 2 π 的。算出值就是由 -1 到 1。所以整个返回值是由 0 到 1 。但是我们知道余弦函数由 π 到 2 π 斜率是由小到大再到小的，也就是中间的斜率最大，值得变化率也就是最大的。</p>
<p>上面的gif动画，小球直直的掉落，不符合现实生活。但是，系统帮助我们提供了一个BounceInterpolator可以模拟出一个反弹的效果。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-b27a900b32e9bf64.gif?imageMogr2/auto-orient/strip" alt=""></p>
<p>现在，我们也来自定义一个Interpolater：</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></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">CurveInterpolater</span> <span class="keyword">implements</span> <span class="title">TimeInterpolator</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">public</span> <span class="keyword">float</span> <span class="title">getInterpolation</span><span class="params">(<span class="keyword">float</span> input)</span> </span>&#123;</div><div class="line">        <span class="keyword">float</span> result;</div><div class="line">        <span class="keyword">if</span> (input &lt;= <span class="number">0.1</span>) &#123;</div><div class="line">            result = input*input;</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">            result = (<span class="keyword">float</span>) (Math.sqrt(input));</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">return</span> result;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>上面就自定义了一个先增大，再减小的过程。原谅我数学不好╮(╯﹏╰）╭。。。。。。</p>
<p>使用,只要setInterpolator就行了：</p>
<figure class="highlight haxe"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">animatorSet.setInterpolator(<span class="keyword">new</span> <span class="type">CurveInterpolater</span>());</div></pre></td></tr></table></figure>
<p>最后，看一下效果：</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-59e4a7ce18d03740.gif?imageMogr2/auto-orient/strip" alt=""></p>
<p>可以看到中间速度很快，最后又很慢。</p>
<h4 id="ViewPropertyAnimator"><a href="#ViewPropertyAnimator" class="headerlink" title="ViewPropertyAnimator"></a>ViewPropertyAnimator</h4><p>属性动画的功能确实强大，但是如果我们只想改变View进行动画操作。如：进行透明度的更改:</p>
<figure class="highlight lsl"><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">ObjectAnimator animator=ObjectAnimator.ofFloat(view,<span class="string">"alpha"</span>,<span class="number">0</span>,<span class="number">1</span>);</div><div class="line">animator.start();</div></pre></td></tr></table></figure>
<p>是不是有点麻烦了，所以于在Android 3.1系统当中补充了ViewPropertyAnimator这个机制。可以理解为ViewPropertyAnimator为一种简化的Property Animation。专门为View进行动画操作。</p>
<p>现在，只要这样：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="selector-tag">view</span><span class="selector-class">.animate</span>()<span class="selector-class">.alpha</span>(0);</div></pre></td></tr></table></figure>
<p>而且是每一个方法返回的是自身，所以支持连缀写法，所以只要一句话就完成了N个属性的更改：</p>
<figure class="highlight aspectj"><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></pre></td><td class="code"><pre><div class="line">view.animate().alpha(<span class="number">0</span>).scaleX(<span class="number">2</span>).scaleY(<span class="number">2</span>).setDuration(<span class="number">3000</span>)</div><div class="line">             .setInterpolator(<span class="keyword">new</span> DecelerateInterpolator())</div><div class="line">             .setStartDelay(<span class="number">200</span>)</div><div class="line">             .withStartAction(<span class="keyword">new</span> Runnable() &#123;</div><div class="line">                 <span class="meta">@Override</span></div><div class="line">                 <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</div><div class="line">                     Log.d(TAG, <span class="string">"start"</span>);</div><div class="line">                 &#125;</div><div class="line">             &#125;)</div><div class="line">             .withEndAction(<span class="keyword">new</span> Runnable() &#123;</div><div class="line">                 <span class="meta">@Override</span></div><div class="line">                 <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</div><div class="line">                     Log.d(TAG, <span class="string">"end"</span>);</div><div class="line">                 &#125;</div><div class="line">             &#125;);</div></pre></td></tr></table></figure>
<p>而且，并不需要我们手动调用start()方法，因为在内部已经隐形调用了。当然，你想要写也可以写上start()方法的。</p>
<p>好了，这一篇就到这里。</p>
<p>参考：</p>
<ul>
<li><p><a href="http://blog.csdn.net/guolin_blog/article/details/43536355" target="_blank" rel="external">Android属性动画完全解析(上)，初识属性动画的基本用法</a></p>
</li>
<li><p><a href="http://blog.csdn.net/guolin_blog/article/details/43816093" target="_blank" rel="external">Android属性动画完全解析(中)，ValueAnimator和ObjectAnimator的高级用法</a></p>
</li>
<li><p><a href="http://blog.csdn.net/guolin_blog/article/details/44171115" target="_blank" rel="external">Android属性动画完全解析(下)，Interpolator和ViewPropertyAnimator的用法</a></p>
</li>
<li><p><a href="http://blog.csdn.net/harvic880925/article/details/50995268" target="_blank" rel="external"> Android自定义控件三部曲文章索引</a></p>
</li>
<li><p><a href="http://blog.csdn.net/lmj623565791/article/details/38067475" target="_blank" rel="external">Android 属性动画（Property Animation） 完全解析 （上）</a></p>
</li>
<li><p><a href="http://blog.csdn.net/lmj623565791/article/details/38092093" target="_blank" rel="external">Android 属性动画（Property Animation） 完全解析 （下）</a></p>
</li>
</ul>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;上一篇文章，大概介绍了一下View Animation以及Property Animation的使用。这一篇，在简单使用的基础之上，进行一点高级的技巧。&lt;/p&gt;
&lt;h4 id=&quot;TypeEvaluator&quot;&gt;&lt;a href=&quot;#TypeEvaluator&quot; class=&quot;headerlink&quot; title=&quot;TypeEvaluator&quot;&gt;&lt;/a&gt;TypeEvaluator&lt;/h4&gt;&lt;p&gt;当我们使用ValueAnimator或者ObjectAnimator时，会有一个ofObject()方法和setEvaluator()。在这个方法中是有一个TypeEvaluator参数的。。那么这个TypeEvaluator的作用到底是什么呢？我们在使用ValueAnimator.ofFloat()时是会向其中传入开始值以及结束值的。然后系统就会帮助我们平滑计算出整个过程的数值。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
      <category term="Animation" scheme="https://KevinJe.github.io/categories/Android/Animation/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="Animation" scheme="https://KevinJe.github.io/tags/Animation/"/>
    
      <category term="Property Animation" scheme="https://KevinJe.github.io/tags/Property-Animation/"/>
    
      <category term="Interpolater" scheme="https://KevinJe.github.io/tags/Interpolater/"/>
    
      <category term="TypeEvaluator" scheme="https://KevinJe.github.io/tags/TypeEvaluator/"/>
    
  </entry>
  
  <entry>
    <title>Android中的动画一之基本使用</title>
    <link href="https://KevinJe.github.io/2017/09/03/Android%E4%B8%AD%E7%9A%84%E5%8A%A8%E7%94%BB%E4%B8%80%E4%B9%8B%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/"/>
    <id>https://KevinJe.github.io/2017/09/03/Android中的动画一之基本使用/</id>
    <published>2017-09-03T11:45:10.000Z</published>
    <updated>2017-09-22T12:57:46.758Z</updated>
    
    <content type="html"><![CDATA[<p>在一个App中，良好的交互体验无疑是很重要的一部分，而动画正是在交互之中起到了重要的作用。在Android中的动画，第一种是View Animation，也就是视图动画。第二种是Property Animation，即属性动画，还有SVG动画。而Property Animation是Google在Android3.0之后才推出的。在这之前View Animation是用的最多的。而在推出功能更加强大的Property Animation之后，View Animation用的就不再那么多了。View Animation最大的不足就是不具有交互性，虽然有视觉上的动画效果。但是假如我们让一个Button做平移操作，之后这个Button的点击事件的位置还是在原来的位置上，这就造成了不能用View Animation实现有交互性的操作。而Property Animation则是弥补了这一缺陷。</p>
<a id="more"></a>
<h3 id="View-Animation"><a href="#View-Animation" class="headerlink" title="View Animation"></a>View Animation</h3><p>View Animation的用法比较简单，大概有以下几种动画类型。</p>
<h4 id="透明度动画"><a href="#透明度动画" class="headerlink" title="透明度动画"></a>透明度动画</h4><p>就是改变View的透明度的一种动画。</p>
<figure class="highlight pony"><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="type">AlphaAnimation</span> aa = <span class="function"><span class="keyword">new</span> <span class="title">AlphaAnimation</span>(<span class="number">0</span>, <span class="number">1</span>);</span></div><div class="line"><span class="comment">//设置动画时长</span></div><div class="line"><span class="title">aa</span>.<span class="title">setDuration</span>(<span class="number">1000</span>);</div><div class="line"><span class="title">view</span>.<span class="title">startAnimation</span>(aa);</div></pre></td></tr></table></figure>
<p>透明度有0到1，即由透明到不透明。</p>
<p>就这样就简单的实现了一个透明度动画，当然你还可以使用<code>setFillAfter</code>（动画结束后是否保留当前的动画状态）、<code>setInterpolator()</code>（设置插值器）、<code>setRepeatMode()</code>（设置重复模式，有restart和reverse两种值可选）、<code>setRepeatCount()</code>（设置重复次数）等来增加动画效果。其他的可以自己去查。</p>
<h4 id="旋转动画"><a href="#旋转动画" class="headerlink" title="旋转动画"></a>旋转动画</h4><figure class="highlight mipsasm"><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">RotateAnimation <span class="built_in">ra</span> = new RotateAnimation(<span class="number">0</span>, <span class="number">360</span>, <span class="number">100</span>, <span class="number">100</span>)<span class="comment">;</span></div><div class="line"><span class="built_in">ra</span>.setDuration(<span class="number">1000</span>)<span class="comment">;</span></div><div class="line">view.startAnimation(<span class="built_in">ra</span>)<span class="comment">;</span></div></pre></td></tr></table></figure>
<p>从0度开始旋转360度，并且以（100,100）为旋转中心。</p>
<p>当然，这里也可以设置旋转参考系为自身或者父布局：</p>
<figure class="highlight mipsasm"><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">RotateAnimation <span class="built_in">ra</span> = new RotateAnimation(<span class="number">0</span>, <span class="number">360</span>,</div><div class="line">        RotateAnimation.RELATIVE_TO_SELF, <span class="number">0</span>.<span class="number">5</span>F,</div><div class="line">        RotateAnimation.RELATIVE_TO_SELF, <span class="number">0</span>.<span class="number">5</span>F)<span class="comment">;</span></div><div class="line"><span class="built_in">ra</span>.setDuration(<span class="number">1000</span>)<span class="comment">;</span></div><div class="line">view.startAnimation(<span class="built_in">ra</span>)<span class="comment">;</span></div></pre></td></tr></table></figure>
<p>这里以自身为参考，并且指定为0.5，即围绕自己的中心点旋转，由0到360度。</p>
<h4 id="位移动画"><a href="#位移动画" class="headerlink" title="位移动画"></a>位移动画</h4><figure class="highlight lsl"><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">TranslateAnimation ta = new TranslateAnimation(<span class="number">0</span>, <span class="number">200</span>, <span class="number">0</span>, <span class="number">300</span>);</div><div class="line">ta.setDuration(<span class="number">1000</span>);</div><div class="line">view.startAnimation(ta);</div></pre></td></tr></table></figure>
<p>由（0,200）移动到（0,300）。</p>
<p>####　缩放动画</p>
<figure class="highlight lsl"><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">ScaleAnimation sa = new ScaleAnimation(<span class="number">0</span>, <span class="number">2</span>, <span class="number">0</span>, <span class="number">2</span>);</div><div class="line">sa.setDuration(<span class="number">1000</span>);</div><div class="line">view.startAnimation(sa);</div></pre></td></tr></table></figure>
<p>开始横向的缩放因子为0，结束的缩放因子为2。开始的竖向缩放因子为0，结束的缩放因子为2。即从没有，到横向竖向分别扩大两倍。</p>
<figure class="highlight lsl"><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">ScaleAnimation sa = new ScaleAnimation(<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>,</div><div class="line">        Animation.RELATIVE_TO_SELF, <span class="number">0.5</span>F,</div><div class="line">        Animation.RELATIVE_TO_SELF, <span class="number">0.5</span>F);</div><div class="line">sa.setDuration(<span class="number">1000</span>);</div><div class="line">view.startAnimation(sa);</div></pre></td></tr></table></figure>
<p>和上面一样，以自身中心点为参考点缩放。即从没有，到横向竖向分别扩大一倍。</p>
<p>以上就是View Animation所支持的四种动画类型，当然这四种类型是可以一起使用的。这就要借助于<code>AnimationSet</code>了。</p>
<figure class="highlight pony"><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="type">AnimationSet</span> <span class="keyword">as</span> = <span class="function"><span class="keyword">new</span> <span class="title">AnimationSet</span>(true);</span></div><div class="line">       <span class="title">as</span>.<span class="title">setDuration</span>(<span class="number">1000</span>);</div><div class="line"></div><div class="line">       <span class="title">AlphaAnimation</span> <span class="title">aa</span> = <span class="title">new</span> <span class="title">AlphaAnimation</span>(<span class="number">0</span>, <span class="number">1</span>);</div><div class="line">       <span class="title">aa</span>.<span class="title">setDuration</span>(<span class="number">1000</span>);</div><div class="line">       <span class="title">as</span>.<span class="title">addAnimation</span>(aa);</div><div class="line"></div><div class="line">       <span class="title">TranslateAnimation</span> <span class="title">ta</span> = <span class="title">new</span> <span class="title">TranslateAnimation</span>(<span class="number">0</span>, <span class="number">100</span>, <span class="number">0</span>, <span class="number">200</span>);</div><div class="line">       <span class="title">ta</span>.<span class="title">setDuration</span>(<span class="number">1000</span>);</div><div class="line">       <span class="title">as</span>.<span class="title">addAnimation</span>(ta);</div><div class="line"></div><div class="line">       <span class="title">view</span>.<span class="title">startAnimation</span>(as);</div></pre></td></tr></table></figure>
<p>利用<code>addAnimation()</code>将动画添加进来。最后<code>startAnimation()</code>就开始执行整个动画集合了。</p>
<p>View Animation也是可以进行动画的监听的：</p>
<figure class="highlight less"><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="selector-tag">aa</span><span class="selector-class">.setAnimationListener</span>(new Animation.AnimationListener() &#123;</div><div class="line">            <span class="comment">//动画开始时</span></div><div class="line">            <span class="variable">@Override</span></div><div class="line">            public void onAnimationStart(Animation animation) &#123;</div><div class="line">                </div><div class="line">            &#125;</div><div class="line"></div><div class="line">            <span class="comment">//动画结束时</span></div><div class="line">            <span class="variable">@Override</span></div><div class="line">            public void onAnimationEnd(Animation animation) &#123;</div><div class="line"></div><div class="line">            &#125;</div><div class="line"></div><div class="line">            <span class="comment">//动画重复时</span></div><div class="line">            <span class="variable">@Override</span></div><div class="line">            public void onAnimationRepeat(Animation animation) &#123;</div><div class="line"></div><div class="line">            &#125;</div><div class="line">        &#125;);</div></pre></td></tr></table></figure>
<p>这样就可以在动画运行时进行进行相应的逻辑处理了。</p>
<h4 id="在xml中使用"><a href="#在xml中使用" class="headerlink" title="在xml中使用"></a>在xml中使用</h4><p>View Animation也是支持在xml文件中实现的，这样写的一个好处是复用性好。当遇到一样的动画时直接引用就好了。</p>
<p>首先，需要在res文件夹下新建anim文件夹，在这里面新建一个xml就可以使用View Animation了。</p>
<p>透明度动画：</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></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">alpha</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:duration</span>=<span class="string">"1000"</span></div><div class="line">       <span class="attr">android:fromAlpha</span>=<span class="string">"0"</span></div><div class="line">       <span class="attr">android:toAlpha</span>=<span class="string">"1"</span></div><div class="line">    &gt;</div><div class="line"><span class="tag">&lt;/<span class="name">alpha</span>&gt;</span></div></pre></td></tr></table></figure>
<p>fromAlpha：开始透明度<br>toAlpha：结束透明度</p>
<p>旋转动画：</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></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">rotate</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:duration</span>=<span class="string">"1000"</span></div><div class="line">        <span class="attr">android:fromDegrees</span>=<span class="string">"0"</span></div><div class="line">        <span class="attr">android:toDegrees</span>=<span class="string">"360"</span></div><div class="line">        <span class="attr">android:pivotX</span>=<span class="string">"50%"</span></div><div class="line">        <span class="attr">android:pivotY</span>=<span class="string">"50%"</span></div><div class="line">    &gt;</div><div class="line"><span class="tag">&lt;/<span class="name">rotate</span>&gt;</span></div></pre></td></tr></table></figure>
<p>fromDegrees：开始旋转的角度<br>toDegrees：结束旋转的角度<br>pivotX：旋转点X坐标<br>pivotY：旋转点Y坐标</p>
<p>这里需要注意一下<code>PivotX</code>和<code>PivotY</code>的值，当你写成 50时是相对于父布局参考而言的，而当你写成50%时表示的是相对于自身参考而言的。<br>官方文档有提到，具体看这里: </p>
<p><a href="https://developer.android.google.cn/guide/topics/graphics/view-animation.html" target="_blank" rel="external">View Animation</a></p>
<blockquote>
<p>(“50” for 50% relative to the parent, or “50%” for 50% relative to itself)</p>
</blockquote>
<p>平移动画：</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></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">translate</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:duration</span>=<span class="string">"1000"</span></div><div class="line">           <span class="attr">android:fromXDelta</span>=<span class="string">"0"</span></div><div class="line">           <span class="attr">android:fromYDelta</span>=<span class="string">"0"</span></div><div class="line">           <span class="attr">android:toXDelta</span>=<span class="string">"100"</span></div><div class="line">           <span class="attr">android:toYDelta</span>=<span class="string">"100"</span></div><div class="line">    &gt;</div><div class="line"><span class="tag">&lt;/<span class="name">translate</span>&gt;</span></div></pre></td></tr></table></figure>
<p> fromXDelta：开始X坐标<br> fromYDelta：开始Y坐标<br>toXDelta：结束X坐标<br>toYDelta：结束Y坐标</p>
<p>缩放动画：</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></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">scale</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:duration</span>=<span class="string">"1000"</span></div><div class="line">       <span class="attr">android:fromXScale</span>=<span class="string">"0"</span></div><div class="line">       <span class="attr">android:toXScale</span>=<span class="string">"2"</span></div><div class="line">       <span class="attr">android:fromYScale</span>=<span class="string">"0"</span></div><div class="line">       <span class="attr">android:toYScale</span>=<span class="string">"2"</span></div><div class="line">       <span class="attr">android:pivotX</span>=<span class="string">"50%"</span></div><div class="line">       <span class="attr">android:pivotY</span>=<span class="string">"50%"</span></div><div class="line">    &gt;</div><div class="line"><span class="tag">&lt;/<span class="name">scale</span>&gt;</span></div></pre></td></tr></table></figure>
<p>fromXScale:开始X坐标缩放因子<br>toXScale：结束X坐标缩放因子<br>fromYScale：开始Y坐标缩放因子<br>toYScale：结束Y坐标缩放因子<br>pivotX：缩放中心X坐标<br>pivotY：缩放中心Y坐标</p>
<p>动画集合：</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></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">set</span> <span class="attr">android:shareInterpolator</span>=<span class="string">"false"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">scale</span></span></div><div class="line">        <span class="attr">android:interpolator</span>=<span class="string">"@android:anim/accelerate_decelerate_interpolator"</span></div><div class="line">        <span class="attr">android:fromXScale</span>=<span class="string">"1.0"</span></div><div class="line">        <span class="attr">android:toXScale</span>=<span class="string">"1.4"</span></div><div class="line">        <span class="attr">android:fromYScale</span>=<span class="string">"1.0"</span></div><div class="line">        <span class="attr">android:toYScale</span>=<span class="string">"0.6"</span></div><div class="line">        <span class="attr">android:pivotX</span>=<span class="string">"50%"</span></div><div class="line">        <span class="attr">android:pivotY</span>=<span class="string">"50%"</span></div><div class="line">        <span class="attr">android:fillAfter</span>=<span class="string">"false"</span></div><div class="line">        <span class="attr">android:duration</span>=<span class="string">"700"</span> /&gt;</div><div class="line">    <span class="tag">&lt;<span class="name">set</span> <span class="attr">android:interpolator</span>=<span class="string">"@android:anim/decelerate_interpolator"</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">scale</span></span></div><div class="line">           <span class="attr">android:fromXScale</span>=<span class="string">"1.4"</span></div><div class="line">           <span class="attr">android:toXScale</span>=<span class="string">"0.0"</span></div><div class="line">           <span class="attr">android:fromYScale</span>=<span class="string">"0.6"</span></div><div class="line">           <span class="attr">android:toYScale</span>=<span class="string">"0.0"</span></div><div class="line">           <span class="attr">android:pivotX</span>=<span class="string">"50%"</span></div><div class="line">           <span class="attr">android:pivotY</span>=<span class="string">"50%"</span></div><div class="line">           <span class="attr">android:startOffset</span>=<span class="string">"700"</span></div><div class="line">           <span class="attr">android:duration</span>=<span class="string">"400"</span></div><div class="line">           <span class="attr">android:fillBefore</span>=<span class="string">"false"</span> /&gt;</div><div class="line">        <span class="tag">&lt;<span class="name">rotate</span></span></div><div class="line">           <span class="attr">android:fromDegrees</span>=<span class="string">"0"</span></div><div class="line">           <span class="attr">android:toDegrees</span>=<span class="string">"-45"</span></div><div class="line">           <span class="attr">android:toYScale</span>=<span class="string">"0.0"</span></div><div class="line">           <span class="attr">android:pivotX</span>=<span class="string">"50%"</span></div><div class="line">           <span class="attr">android:pivotY</span>=<span class="string">"50%"</span></div><div class="line">           <span class="attr">android:startOffset</span>=<span class="string">"700"</span></div><div class="line">           <span class="attr">android:duration</span>=<span class="string">"400"</span> /&gt;</div><div class="line">    <span class="tag">&lt;/<span class="name">set</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">set</span>&gt;</span></div></pre></td></tr></table></figure>
<p>在xml中使用和在代码中使用相差不大，基本属性都一致。</p>
<h3 id="Property-Animation"><a href="#Property-Animation" class="headerlink" title="Property Animation"></a>Property Animation</h3><p>接下来轮到Property Animation了，这一动画时真的可以改变View的属性值，用它可以实现几乎所有的动画效果。</p>
<h4 id="ValueAnimator"><a href="#ValueAnimator" class="headerlink" title="ValueAnimator"></a>ValueAnimator</h4><p>ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的，而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。我们只需要将初始值和结束值提供给ValueAnimator，并且告诉它动画所需运行的时长，那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。</p>
<p>ValueAnimator使用非常简单：</p>
<figure class="highlight lsl"><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">ValueAnimator va = ValueAnimator.ofFloat(<span class="number">0</span>, <span class="number">1</span>);</div><div class="line">va.setDuration(<span class="number">1000</span>);</div><div class="line">va.start();</div></pre></td></tr></table></figure>
<p>首先利用ValueAnimator的ofFloat()静态方法得到ValueAnimator对象，之后传入0和1，表示让ValueAnimator计算由0平滑过渡到1，然后设置了时长为1s。最后，调用start()方法，开启动画。但是，这时你是看不到任何动画效果的。因为ValueAnimator只是不断的计算值，所以可以用到监听器来获得每一次的值：</p>
<figure class="highlight cs"><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">va.addUpdateListener(<span class="keyword">new</span> ValueAnimator.AnimatorUpdateListener() &#123;</div><div class="line">           @<span class="function">Override</span></div><div class="line">           <span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onAnimationUpdate</span>(<span class="params">ValueAnimator animation</span>) &#123;</div><div class="line">               <span class="keyword">float</span> <span class="keyword">value</span> = (<span class="keyword">float</span>) animation.getAnimatedValue();</div><div class="line">               Log.d(TAG, <span class="string">"the value is "</span>+<span class="keyword">value</span>);</div><div class="line">           &#125;</div><div class="line">       &#125;);</div></pre></td></tr></table></figure>
<p>通过addUpdateListener()方法，注册监听器，当动画执行时就会不断的回调该方法。在该方法内部，就可以通过getAnimatedValue()得到每一次计算出的值，利用该数值我们就可以设置给View，从而看到动画效果。下面是得到的每一次的值。</p>
<blockquote>
<p>the value is 0.0<br>the value is 0.00252449<br>the value is 0.01070955<br>the value is 0.02447176<br>the value is 0.04365423<br>the value is 0.06803828<br>the value is 0.09549156<br>the value is 0.12912908<br>the value is 0.16699407<br>the value is 0.20865482<br>the value is 0.25090742<br>the value is 0.29854682<br>the value is 0.3484823<br>the value is 0.400145<br>the value is 0.4529459<br>the value is 0.5031415<br>the value is 0.5564282<br>the value is 0.6090715<br>the value is 0.6604718<br>the value is 0.70718765<br>the value is 0.7545208<br>the value is 0.79895246<br>the value is 0.83997655<br>the value is 0.87712574<br>the value is 0.9081696<br>the value is 0.93663126<br>the value is 0.9601159<br>the value is 0.978356<br>the value is 0.9905453<br>the value is 0.9980668<br>the value is 1.0</p>
</blockquote>
<p>ofFloat(float…values)是可以传入多个值的，比如：</p>
<figure class="highlight lsl"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">ValueAnimator va = ValueAnimator.ofFloat(<span class="number">0</span>, <span class="number">3</span>,<span class="number">5</span>,<span class="number">1</span>);</div></pre></td></tr></table></figure>
<p>这样就表示一个值由0到3再到5再到1，这样是可以完成复杂的逻辑效果的。</p>
<p>当然ValueAnimator中还提供了ofInt(),ofArgb()方法。使用上与ofFloat()无差异。只是后两个方法显得更高级一些，ofArgb()是可以对我们给出的颜色进行平滑过渡的，其实内部实现是TypeEvaluator，后面会说到它的作用。</p>
<p>这里需要注意的是，addUpdateListener()方法中getAnimatedValue()返回的是一个Object对象，我们需要转换为我们想要的类型。当你使用ofFloat()时，这里就转换为float类型。而当你使用的是ofInt()方法时，这里也就应该转换为int类型。如果你使用的是ofFloat()，却转换为int，是会报出类型转换异常的。</p>
<p>ValueAnimator中还由其他方法，如：setRepeatCount()，设置重复次数、setRepeatMode()，设置重复模式，有restart（重新开始动画）和reverse（逆向执行动画）可选、cancel()取消动画。其余的方法可以自己查看官方文档。</p>
<h4 id="ObjectAnimator"><a href="#ObjectAnimator" class="headerlink" title="ObjectAnimator"></a>ObjectAnimator</h4><p>其实ObjectAnimator是继承自ValueAnimator的：<br><figure class="highlight scala"><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">public <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ObjectAnimator</span> <span class="keyword">extends</span> <span class="title">ValueAnimator</span> </span>&#123;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>所以，在ValueAnimator中的方法ObjectAnimator也是可以使用的。只不过ObjectAnimator在ValueAnimator的基础上又实现了动画的效果，这是ValueAnimator中没有的。它是可以直接对任意对象的任意属性进行动画操作的，比如说View的alpha、translate等属性。</p>
<figure class="highlight lsl"><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">ObjectAnimator oa = ObjectAnimator.ofFloat(view, <span class="string">"alpha"</span>, <span class="number">0</span>, <span class="number">1</span>);</div><div class="line">oa.setDuration(<span class="number">1000</span>);</div><div class="line">oa.start();</div></pre></td></tr></table></figure>
<p>是不是和上面的View Animation的AlphaAnimation及其相似呢？第一个参数表示那个View执行动画，第二个参数要改变的属性，第三个参数是一个可变值，可以有多个值。0表示完全透明，1表示完全不透明。</p>
<figure class="highlight lsl"><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">ObjectAnimator oa = ObjectAnimator.ofFloat(view, <span class="string">"translationY"</span>, <span class="number">0</span>, <span class="number">300</span>,<span class="number">500</span>,<span class="number">800</span>,<span class="number">500</span>);</div><div class="line">oa.setDuration(<span class="number">3000</span>);</div><div class="line">oa.start();</div></pre></td></tr></table></figure>
<p>这样就可以实现在竖直方向上的平移效果，最后View会停在500位置，你可以加一个点击事件。会发现，ObjectAnimator是真的会改变动画的属性，不再是View Animation那样，移动后点击事件还在原位置。</p>
<p>所有可以支持的属性如下，基本上和View Animation中的属性很像：</p>
<blockquote>
<ul>
<li>translationX and translationY: These properties control where the View is located as a delta from its left and top coordinates which are set by its layout container.</li>
<li>rotation, rotationX, and rotationY: These properties control the rotation in 2D (rotation property) and 3D around the pivot point.</li>
<li>scaleX and scaleY: These properties control the 2D scaling of a View around its pivot point.</li>
<li>pivotX and pivotY: These properties control the location of the pivot point, around which the rotation and scaling transforms occur. By default, the pivot point is located at the center of the object.</li>
<li>x and y: These are simple utility properties to describe the final location of the View in its container, as a sum of the left and top values and translationX and translationY values.</li>
<li>alpha: Represents the alpha transparency on the View. This value is 1 (opaque) by default, with a value of 0 representing full transparency (not visible).</li>
</ul>
</blockquote>
<p>那么这些属性来自于哪里呢？其实ObjectAnimator内部的工作机制并不是直接对我们传入的属性名进行操作的，而是会去寻找这个属性名对应的get和set方法，因此alpha属性所对应的get和set方法应该就是：</p>
<figure class="highlight cs"><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="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setAlpha</span>(<span class="params"><span class="keyword">float</span> <span class="keyword">value</span></span>)</span>;  </div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">float</span> <span class="title">getAlpha</span>(<span class="params"></span>)</span>;</div></pre></td></tr></table></figure>
<p>在View中是可以找到这两个方法的，也就是任何继承自View的对象都可以使用上面的属性。</p>
<blockquote>
<p>The object property that you are animating must have a setter function (in camel case) in the form of set<propertyname>(). Because the <a href="https://developer.android.google.cn/reference/android/animation/ObjectAnimator.html" target="_blank" rel="external">ObjectAnimator</a> automatically updates the property during animation, it must be able to access the property with this setter method. For example, if the property name is foo, you need to have a setFoo()<br> method. If this setter method does not exist, you have three options:</propertyname></p>
<ul>
<li>Add the setter method to the class if you have the rights to do so.</li>
<li>Use a wrapper class that you have rights to change and have that wrapper receive the value with a valid setter method and forward it to the original object.</li>
<li>Use <a href="https://developer.android.google.cn/reference/android/animation/ValueAnimator.html" target="_blank" rel="external">ValueAnimator</a> instead.</li>
</ul>
</blockquote>
<p>官方文档中也有说明，大意就是你想操纵的View必须要有setXXXX()方法（XXXX为属性名称）。ObjectAnimator是在动画执行过程中直接更改属性的，从而完成动画效果。如果，setter方法不存在，给出了三点解决办法。</p>
<ol>
<li>添加setter方法。<br>2.使用包装类的方法给一个属性增加getter、setter方法。</li>
</ol>
<figure class="highlight arduino"><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="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> WrapperView &#123;</div><div class="line">       <span class="keyword">private</span> View mView;</div><div class="line"></div><div class="line">       <span class="keyword">public</span> WrapperView(View view) &#123;</div><div class="line">           mView = view;</div><div class="line">       &#125;</div><div class="line"></div><div class="line">       <span class="keyword">public</span> <span class="keyword">int</span> getWidth() &#123;</div><div class="line">           <span class="built_in">return</span> mView.getLayoutParams().<span class="built_in">width</span>;</div><div class="line">       &#125;</div><div class="line"></div><div class="line">       <span class="keyword">public</span> <span class="keyword">void</span> setWidth(<span class="keyword">int</span> <span class="built_in">width</span>) &#123;</div><div class="line">           mView.getLayoutParams().<span class="built_in">width</span> = <span class="built_in">width</span>;</div><div class="line">           mView.requestLayout();</div><div class="line">       &#125;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>使用：<br><figure class="highlight lsl"><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">WrapperView wrapper = new WrapperView(view);</div><div class="line">ObjectAnimator.ofInt(wrapper, <span class="string">"width"</span>, <span class="number">0</span>, <span class="number">300</span>, <span class="number">600</span>).setDuration(<span class="number">3000</span>).start();</div></pre></td></tr></table></figure></p>
<p>3.使用ValueAnimator代替。</p>
<p>注意：getter属性是非必须的，那么什么时候一定会用到getter属性呢。因为ofFloat()和ofInt()方法中都有一个可变参数，我们可以传入多个参数。也就是说只传入一个参数也是可以的，这时候这一个参数只表示结束值。那从哪个值开始呢？这里就用到了getter方法，getter方法中返回的值会默认设为开始的数值，假如我们只传入一个参数的情况下。</p>
<p>来看系统的View源码中Alpha值实现：</p>
<figure class="highlight cpp"><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="keyword">protected</span> <span class="keyword">float</span> mAlpha;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">float</span> <span class="title">getAlpha</span><span class="params">()</span> </span>&#123;</div><div class="line">       <span class="keyword">return</span> mAlpha;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>也就是getter中alpha默认值是0。所以，当我们这样时：</p>
<figure class="highlight mipsasm"><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">ObjectAnimator oa = ObjectAnimator.ofFloat(view, <span class="string">"alpha"</span>,<span class="number">0</span>.<span class="number">5</span>f)<span class="comment">;</span></div><div class="line">oa.setDuration(<span class="number">3000</span>)<span class="comment">;</span></div><div class="line">oa.start()<span class="comment">;</span></div></pre></td></tr></table></figure>
<p>是会看到view先完全透明（0），最后停在半透明状态的（0.5）。</p>
<p><strong>也就是当且仅当动画的只有一个过渡值时，系统才会调用对应属性的getter函数来得到动画的初始值。但是，setter方法是属性必须要有的。</strong></p>
<h4 id="组合动画"><a href="#组合动画" class="headerlink" title="组合动画"></a>组合动画</h4><p>和View Animation一样，Propty Animation也是支持组合动画的。想使用组合动画，首先要使用AnimatorSet。</p>
<figure class="highlight gams"><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></pre></td><td class="code"><pre><div class="line">ObjectAnimator moveIn = ObjectAnimator.ofFloat(view, <span class="string">"translationX"</span>, <span class="number">-500</span>f, <span class="number">0</span>f);</div><div class="line">ObjectAnimator rotate = ObjectAnimator.ofFloat(view, <span class="string">"rotation"</span>, <span class="number">0</span>f, <span class="number">360</span>f);</div><div class="line">ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(view, <span class="string">"alpha"</span>, <span class="number">1</span>f, <span class="number">0</span>f, <span class="number">1</span>f);</div><div class="line">AnimatorSet <span class="keyword">set</span> = new <span class="comment">AnimatorSet()</span>;</div><div class="line"><span class="keyword">set</span>.playTogether(moveIn,rotate,fadeInOut);</div><div class="line"><span class="keyword">set</span>.playSequentially(moveIn,rotate,fadeInOut);</div><div class="line"><span class="keyword">set</span>.setDuration(5000);</div><div class="line"><span class="keyword">set</span>.start();</div></pre></td></tr></table></figure>
<p>playTogether()表示动画一起执行，playSequentially()表示动画一个接一个执行。</p>
<p>AnimatorSet中还提供了一个play()方法，会返回一个AnimatorSet.Builder。AnimatorSet.Builder中有四个方法，用于组合动画到一起。</p>
<blockquote>
<ul>
<li>after(long delay)：延时指定毫秒执行动画</li>
<li>after(Animator anim)   将现有动画插入到传入的动画之后执行</li>
<li>before(Animator anim)   将现有动画插入到传入的动画之前执行</li>
<li>with(Animator anim)   将现有动画和传入的动画同时执行</li>
</ul>
</blockquote>
<h4 id="Animator监听器"><a href="#Animator监听器" class="headerlink" title="Animator监听器"></a>Animator监听器</h4><p>前面ValueAnimator的一个重要的监听器是addUpdateListener()，可以在其中getAnimatedValue()得到当前animation的值。</p>
<p>addUpdateListener()只是Animator监听器中的一个，Animator还有两个监听器用于控制动画执行的开始到结束的每一个过程。</p>
<figure class="highlight aspectj"><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></pre></td><td class="code"><pre><div class="line">ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(view, <span class="string">"alpha"</span>, <span class="number">0</span>, <span class="number">1</span>);</div><div class="line">objectAnimator.addListener(<span class="keyword">new</span> Animator.AnimatorListener() &#123;</div><div class="line">           <span class="meta">@Override</span></div><div class="line">           <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onAnimationStart</span><span class="params">(Animator animation)</span> </span>&#123;</div><div class="line">               Log.d(TAG, <span class="string">"Animation is start"</span>);</div><div class="line">           &#125;</div><div class="line"></div><div class="line">           <span class="meta">@Override</span></div><div class="line">           <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onAnimationEnd</span><span class="params">(Animator animation)</span> </span>&#123;</div><div class="line">               Log.d(TAG, <span class="string">"Animation is end"</span>);</div><div class="line">           &#125;</div><div class="line"></div><div class="line">           <span class="meta">@Override</span></div><div class="line">           <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onAnimationCancel</span><span class="params">(Animator animation)</span> </span>&#123;</div><div class="line">               Log.d(TAG, <span class="string">"Animation is cancel"</span>);</div><div class="line">           &#125;</div><div class="line"></div><div class="line">           <span class="meta">@Override</span></div><div class="line">           <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onAnimationRepeat</span><span class="params">(Animator animation)</span> </span>&#123;</div><div class="line">               Log.d(TAG, <span class="string">"Animation is repeat"</span>);</div><div class="line">           &#125;</div><div class="line">       &#125;);</div></pre></td></tr></table></figure>
<p>看名字就能猜出这几个方法会回调的时间了。但是有时候，我们只想监听一个或者几个事件，所以这里还提供了AnimatorListenerAdapter()用于监听任意一个事件。</p>
<figure class="highlight aspectj"><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">objectAnimator.addListener(<span class="keyword">new</span> AnimatorListenerAdapter() &#123;</div><div class="line">           <span class="meta">@Override</span></div><div class="line">           <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onAnimationEnd</span><span class="params">(Animator animation)</span> </span>&#123;</div><div class="line">               <span class="keyword">super</span>.onAnimationEnd(animation);</div><div class="line">               Log.d(TAG, <span class="string">"Animation is end"</span>);</div><div class="line">           &#125;</div></pre></td></tr></table></figure>
<p>这里就只是监听了动画结束这一事件。</p>
<h4 id="在xml中使用-1"><a href="#在xml中使用-1" class="headerlink" title="在xml中使用"></a>在xml中使用</h4><p>Property Animation也是可以在xml中使用的，以此来提高复用率。</p>
<p>首先需要在res下新建animator文件夹。在其中新建xml文件就可以使用Property Animation了。</p>
<p>在其中可以使用三种标签：<br>animator : 对应代码中的ValueAnimator<br>objectAnimator : 对应代码中的ObjectAnimator<br>set : 对应代码中的AnimatorSet</p>
<p>在这三个标签内可以进行动画的组合，或者单独使用，好多属性与上面View Animation相似。想要了解更多，请看这里：</p>
<p><a href="https://developer.android.google.cn/guide/topics/resources/animation-resource.html#Property" target="_blank" rel="external">Animation Resources</a></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></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">set</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:ordering</span>=<span class="string">"sequentially"</span>&gt;</div><div class="line"></div><div class="line">    <span class="tag">&lt;<span class="name">objectAnimator</span></span></div><div class="line">        <span class="attr">android:duration</span>=<span class="string">"2000"</span></div><div class="line">        <span class="attr">android:propertyName</span>=<span class="string">"translationX"</span></div><div class="line">        <span class="attr">android:valueFrom</span>=<span class="string">"-500"</span></div><div class="line">        <span class="attr">android:valueTo</span>=<span class="string">"0"</span></div><div class="line">        <span class="attr">android:valueType</span>=<span class="string">"floatType"</span>&gt;</div><div class="line">    <span class="tag">&lt;/<span class="name">objectAnimator</span>&gt;</span></div><div class="line"></div><div class="line">    <span class="tag">&lt;<span class="name">set</span> <span class="attr">android:ordering</span>=<span class="string">"together"</span>&gt;</span></div><div class="line">        <span class="tag">&lt;<span class="name">objectAnimator</span></span></div><div class="line">            <span class="attr">android:duration</span>=<span class="string">"3000"</span></div><div class="line">            <span class="attr">android:propertyName</span>=<span class="string">"rotation"</span></div><div class="line">            <span class="attr">android:valueFrom</span>=<span class="string">"0"</span></div><div class="line">            <span class="attr">android:valueTo</span>=<span class="string">"360"</span></div><div class="line">            <span class="attr">android:valueType</span>=<span class="string">"floatType"</span>&gt;</div><div class="line">        <span class="tag">&lt;/<span class="name">objectAnimator</span>&gt;</span></div><div class="line"></div><div class="line">        <span class="tag">&lt;<span class="name">set</span> <span class="attr">android:ordering</span>=<span class="string">"sequentially"</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">objectAnimator</span></span></div><div class="line">                <span class="attr">android:duration</span>=<span class="string">"1500"</span></div><div class="line">                <span class="attr">android:propertyName</span>=<span class="string">"alpha"</span></div><div class="line">                <span class="attr">android:valueFrom</span>=<span class="string">"1"</span></div><div class="line">                <span class="attr">android:valueTo</span>=<span class="string">"0"</span></div><div class="line">                <span class="attr">android:valueType</span>=<span class="string">"floatType"</span>&gt;</div><div class="line">            <span class="tag">&lt;/<span class="name">objectAnimator</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">objectAnimator</span></span></div><div class="line">                <span class="attr">android:duration</span>=<span class="string">"1500"</span></div><div class="line">                <span class="attr">android:propertyName</span>=<span class="string">"alpha"</span></div><div class="line">                <span class="attr">android:valueFrom</span>=<span class="string">"0"</span></div><div class="line">                <span class="attr">android:valueTo</span>=<span class="string">"1"</span></div><div class="line">                <span class="attr">android:valueType</span>=<span class="string">"floatType"</span>&gt;</div><div class="line">            <span class="tag">&lt;/<span class="name">objectAnimator</span>&gt;</span></div><div class="line">        <span class="tag">&lt;/<span class="name">set</span>&gt;</span></div><div class="line">    <span class="tag">&lt;/<span class="name">set</span>&gt;</span></div><div class="line"></div><div class="line"><span class="tag">&lt;/<span class="name">set</span>&gt;</span></div></pre></td></tr></table></figure>
<p>在代码中加载：<br><figure class="highlight gams"><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">AnimatorSet <span class="keyword">set</span> = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,</div><div class="line">    R.anim.property_animator);</div><div class="line"><span class="keyword">set</span>.setTarget(myObject);</div><div class="line"><span class="keyword">set</span>.start();</div></pre></td></tr></table></figure></p>
<p>如果在xml中使用了ValueAnimator，则还是可以在代码中监听动画执行过程中返回的值，对View做相应的处理。</p>
<figure class="highlight aspectj"><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">ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(<span class="keyword">this</span>,</div><div class="line">        R.animator.animator);</div><div class="line">xmlAnimator.addUpdateListener(<span class="keyword">new</span> ValueAnimator.AnimatorUpdateListener() &#123;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onAnimationUpdate</span><span class="params">(ValueAnimator updatedAnimation)</span> </span>&#123;</div><div class="line">        <span class="keyword">float</span> animatedValue = (<span class="keyword">float</span>)updatedAnimation.getAnimatedValue();</div><div class="line">        textView.setTranslationX(animatedValue);</div><div class="line">    &#125;</div><div class="line">&#125;);</div><div class="line"></div><div class="line">xmlAnimator.start();</div></pre></td></tr></table></figure>
<h4 id="Ending"><a href="#Ending" class="headerlink" title="Ending"></a>Ending</h4><p>以上就是Android中，动画的基本使用，其中既有View Animation也有Property Animation。虽然花费了时间来整理，但还是值得的，以后再查找也好有个方便。好了，本篇就到这里。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在一个App中，良好的交互体验无疑是很重要的一部分，而动画正是在交互之中起到了重要的作用。在Android中的动画，第一种是View Animation，也就是视图动画。第二种是Property Animation，即属性动画，还有SVG动画。而Property Animation是Google在Android3.0之后才推出的。在这之前View Animation是用的最多的。而在推出功能更加强大的Property Animation之后，View Animation用的就不再那么多了。View Animation最大的不足就是不具有交互性，虽然有视觉上的动画效果。但是假如我们让一个Button做平移操作，之后这个Button的点击事件的位置还是在原来的位置上，这就造成了不能用View Animation实现有交互性的操作。而Property Animation则是弥补了这一缺陷。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
      <category term="Animation" scheme="https://KevinJe.github.io/categories/Android/Animation/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="Animation" scheme="https://KevinJe.github.io/tags/Animation/"/>
    
      <category term="View Animation" scheme="https://KevinJe.github.io/tags/View-Animation/"/>
    
      <category term="Property Animation" scheme="https://KevinJe.github.io/tags/Property-Animation/"/>
    
  </entry>
  
  <entry>
    <title>Android测量文本的宽与高</title>
    <link href="https://KevinJe.github.io/2017/08/23/Android%E6%B5%8B%E9%87%8F%E6%96%87%E6%9C%AC%E7%9A%84%E5%AE%BD%E4%B8%8E%E9%AB%98/"/>
    <id>https://KevinJe.github.io/2017/08/23/Android测量文本的宽与高/</id>
    <published>2017-08-23T09:09:32.000Z</published>
    <updated>2017-09-22T12:55:42.470Z</updated>
    
    <content type="html"><![CDATA[<p>在自定义View的时候，绘制文字往往需要我们测量出文字的宽高值。以此宽高值，我们才能绘制相应的文字。于是，记录一下经常使用的几种方法。</p>
<a id="more"></a>
<h4 id="measureText"><a href="#measureText" class="headerlink" title="measureText"></a>measureText</h4><blockquote>
<p>Return the width of the text</p>
</blockquote>
<p>利用Paint的<code>measureText()</code>方法可以轻松的测量出文字的宽的。当然，<code>measureText()</code>有好四个重载方法，可以根据需要使用。</p>
<figure class="highlight processing"><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">Paint mPaint = <span class="keyword">new</span> Paint();</div><div class="line"><span class="keyword">String</span> <span class="built_in">text</span> = <span class="string">"Hello World"</span>;</div><div class="line"><span class="built_in">float</span> <span class="built_in">textWidth</span> = mPaint.measureText(<span class="built_in">text</span>, <span class="number">0</span>, <span class="built_in">text</span>.length());</div></pre></td></tr></table></figure>
<h4 id="getTextBounds"><a href="#getTextBounds" class="headerlink" title="getTextBounds()"></a>getTextBounds()</h4><blockquote>
<p>Return in bounds (allocated by the caller) the smallest rectangle that encloses all of the characters, with an implied origin at (0,0)<br>返回边界（由调用者分配）包含所有字符的最小矩形，隐含原点（0,0）</p>
</blockquote>
<p>这一方法也是Paint中的，获得文本所在矩形区域，从而得到文本的宽高。</p>
<figure class="highlight arduino"><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></pre></td><td class="code"><pre><div class="line">Paint mPaint = <span class="keyword">new</span> Paint();</div><div class="line">Rect <span class="built_in">rect</span> = <span class="keyword">new</span> Rect();</div><div class="line"><span class="comment">//文本</span></div><div class="line"><span class="keyword">String</span> <span class="built_in">text</span>= <span class="string">"Kevin"</span>;</div><div class="line"><span class="comment">//测量文字</span></div><div class="line">mPaint.getTextBounds(<span class="built_in">text</span>, <span class="number">0</span>, <span class="built_in">text</span>.length(), <span class="built_in">rect</span>);</div><div class="line"><span class="keyword">int</span> <span class="built_in">width</span> = <span class="built_in">rect</span>.<span class="built_in">width</span>();</div><div class="line"><span class="keyword">int</span> <span class="built_in">height</span> = <span class="built_in">rect</span>.<span class="built_in">height</span>();</div><div class="line"><span class="keyword">int</span> left = <span class="built_in">rect</span>.left;</div><div class="line"><span class="keyword">int</span> right = <span class="built_in">rect</span>.right;</div><div class="line"><span class="comment">//width也可以这样计算出来</span></div><div class="line"><span class="comment">//width = right - left;</span></div><div class="line"><span class="keyword">int</span> bottom = <span class="built_in">rect</span>.bottom;</div><div class="line"><span class="keyword">int</span> top = <span class="built_in">rect</span>.top;</div><div class="line"><span class="comment">//height也可以这样计算出来</span></div><div class="line"><span class="comment">//height = top - bottom;</span></div><div class="line">canvas.drawText(<span class="built_in">text</span>, <span class="built_in">width</span>, <span class="built_in">height</span>, mPaint);</div></pre></td></tr></table></figure>
<h4 id="getFontMetrics-amp-getFontMetricsInt"><a href="#getFontMetrics-amp-getFontMetricsInt" class="headerlink" title="getFontMetrics &amp; getFontMetricsInt"></a>getFontMetrics &amp; getFontMetricsInt</h4><p>两者类似，只是返回的经度不同。前者float，后者int。<br>FontMetrics其中定义了五个成员变量：</p>
<blockquote>
<p>ascent<br>The recommended distance above the baseline for singled spaced text.</p>
<p>bottom<br>The maximum distance below the baseline for the lowest glyph in the font at a given text size.</p>
<p>descent<br>The recommended distance below the baseline for singled spaced text.</p>
<p>leading<br>The recommended additional space to add between lines of text.</p>
<p>top<br>The maximum distance above the baseline for the tallest glyph in the font at a given text size. </p>
</blockquote>
<p>ascent和descent是一对，以baseLine为基础，baseLine以上到字符最高处为ascent，到最低出则为descent。</p>
<p>top和bottom是一对，以baseLine为基础，baseLine以上到字符最高处为top，到最低出则为bottom。只不过，top和bottom还要包含一些预留的内边距。所以，通常，top值要比ascent大、bottom值要比descent大。</p>
<p>leading是两行文字之间的行间距。即上一行的descent到下一行的ascent的距离。</p>
<p>更详细的，请参考：</p>
<p><a href="https://www.cnblogs.com/tianzhijiexian/p/4297664.html" target="_blank" rel="external">用TextPaint来绘制文字</a></p>
<p><a href="http://mikewang.blog.51cto.com/3826268/871765" target="_blank" rel="external">Android字符串进阶之三：字体属性及测量（FontMetrics)</a></p>
<p>所以，一下就是获得文本的高度：</p>
<figure class="highlight protobuf"><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">Paint paint = new Paint();</div><div class="line">Paint.FontMetrics fontMetrics= mPaint.getFontMetrics();</div><div class="line"><span class="comment">//不包括文本预留的边距</span></div><div class="line"><span class="built_in">float</span> height1 = fontMetrics.descent - fontMetrics.ascent;</div><div class="line"><span class="comment">//包括文本预留的边距</span></div><div class="line"><span class="built_in">float</span> height2 = fontMetrics.bottom - fontMetrics.top;</div></pre></td></tr></table></figure>
<h4 id="getTextWidths"><a href="#getTextWidths" class="headerlink" title="getTextWidths()"></a>getTextWidths()</h4><blockquote>
<p>　Return the advance widths for the characters in the string</p>
</blockquote>
<p>这个方法返回的是单个字符的宽度，所以这种方法应该是最精确的了。</p>
<figure class="highlight processing"><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></pre></td><td class="code"><pre><div class="line"><span class="built_in">float</span> <span class="built_in">width</span> = <span class="number">0</span>;</div><div class="line"><span class="built_in">int</span> len = <span class="built_in">str</span>.length();  </div><div class="line">Paint paint = <span class="keyword">new</span> Paint();</div><div class="line"><span class="built_in">float</span>[] widths = <span class="keyword">new</span> <span class="built_in">float</span>[len];  </div><div class="line">paint.getTextWidths(<span class="built_in">str</span>, widths);  </div><div class="line"><span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; len; i++) &#123;  </div><div class="line">     <span class="built_in">width</span> += widths[i];  </div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h4 id="Layout-getDesiredWidth"><a href="#Layout-getDesiredWidth" class="headerlink" title="Layout.getDesiredWidth()"></a>Layout.getDesiredWidth()</h4><blockquote>
<p>Return how wide a layout must be in order to display the specified<br> text with one line per paragraph</p>
</blockquote>
<pre><code>TextPaint textPaint = new TextPaint();
paint.setTextSize(size);//设置字体大小
paint.setTypeface(Typeface.xx);//设置字体
float width = Layout.getDesiredWidth(str,textPaint);
</code></pre>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在自定义View的时候，绘制文字往往需要我们测量出文字的宽高值。以此宽高值，我们才能绘制相应的文字。于是，记录一下经常使用的几种方法。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/categories/Android/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="width &amp; height" scheme="https://KevinJe.github.io/tags/width-height/"/>
    
      <category term="text" scheme="https://KevinJe.github.io/tags/text/"/>
    
  </entry>
  
  <entry>
    <title>自定义View之简易时钟</title>
    <link href="https://KevinJe.github.io/2017/08/22/%E8%87%AA%E5%AE%9A%E4%B9%89View%E4%B9%8B%E7%AE%80%E6%98%93%E6%97%B6%E9%92%9F/"/>
    <id>https://KevinJe.github.io/2017/08/22/自定义View之简易时钟/</id>
    <published>2017-08-22T09:38:19.000Z</published>
    <updated>2017-09-22T13:00:36.055Z</updated>
    
    <content type="html"><![CDATA[<p>今天要实现的是自定义View实现一款简易时钟，主要就是练习如何进行自定义View，所以基本上没有什么美感可言。下面，我们就开始了。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-a530916976b8ccc9.gif?imageMogr2/auto-orient/strip" alt="简易时钟"></p>
<p>要实现这款简易时钟，我们还是要先分析出它所包含的元素。就以上面的效果图来说，基本上包括了：最外圈大表盘以及内部中间两个小圆圈，刻度线，刻度文字，表针，数字时间以及上方作者名称。OK，一共就这么多的元素，下面我们就可以开始了。</p>
<a id="more"></a>
<p>第一步，绘制表盘以及最中间的两个小圆圈：</p>
<figure class="highlight lsl"><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"><span class="comment">//半径</span></div><div class="line">radius = mWidth / <span class="number">2</span> - <span class="number">20</span>;</div><div class="line"><span class="comment">//坐标移动到中心位置</span></div><div class="line">canvas.translate(mWidth / <span class="number">2</span>, mHeight / <span class="number">2</span>);</div><div class="line"><span class="comment">//绘制表盘</span></div><div class="line">canvas.drawCircle(<span class="number">0</span>, <span class="number">0</span>, radius, mOuterPaint);</div><div class="line"><span class="comment">//绘制中间的两个小圆圈</span></div><div class="line">canvas.drawCircle(<span class="number">0</span>, <span class="number">0</span>, radius / <span class="number">32</span>, mOuterPaint);</div><div class="line">canvas.drawCircle(<span class="number">0</span>, <span class="number">0</span>, radius / <span class="number">64</span>, mOuterPaint);</div></pre></td></tr></table></figure>
<p>很简单，就是直接绘制三个圆，半径大小不一致罢了。</p>
<p>第二步，绘制刻度线：</p>
<figure class="highlight lsl"><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="comment">//刻度线的高度</span></div><div class="line">timeHeight = radius - <span class="number">15</span>;</div><div class="line"><span class="comment">// 绘制刻度线</span></div><div class="line">for (int i = <span class="number">0</span>; i &lt; <span class="number">60</span>; i++) &#123;</div><div class="line">    <span class="comment">//整点刻度线</span></div><div class="line">    if (i % <span class="number">5</span> == <span class="number">0</span>) &#123;</div><div class="line">        canvas.drawLine(radius, <span class="number">0</span>, timeHeight - <span class="number">15</span>, <span class="number">0</span>, mTimePaint);</div><div class="line">    &#125; else &#123;</div><div class="line">        <span class="comment">//普通刻度线</span></div><div class="line">        canvas.drawLine(radius, <span class="number">0</span>, timeHeight, <span class="number">0</span>, mTimePaint);</div><div class="line">    &#125;</div><div class="line">    <span class="comment">//每一次循环，画布旋转 6 度</span></div><div class="line">    <span class="comment">//一个整点包含一小时，角度：360/12 = 30</span></div><div class="line">    <span class="comment">//一个整点包含5分钟，每分钟的角度：30/5 = 6</span></div><div class="line">    canvas.rotate(<span class="number">360</span> / <span class="number">60</span>, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这里面用到了<code>canvas.rotate(360 / 60, 0, 0)</code>，rotate表示的是画布旋转，后面的参数是旋转中心点的x以及y坐标。这样，每次画布旋转6度，进行刻度的绘制。</p>
<p>第三步，绘制刻度线的文字：</p>
<figure class="highlight processing"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">//12次循环，绘制12次</span></div><div class="line"><span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">12</span>; i++) &#123;</div><div class="line">    Rect <span class="built_in">rect</span> = <span class="keyword">new</span> Rect();</div><div class="line">    <span class="comment">//绘制的刻度文字</span></div><div class="line">    <span class="keyword">String</span> <span class="built_in">text</span> = i + <span class="string">""</span>;</div><div class="line">    <span class="comment">//测量出文字的宽度</span></div><div class="line">    <span class="built_in">float</span> measureText = mTextPaint.measureText(<span class="built_in">text</span>);</div><div class="line">    mTextPaint.getTextBounds(<span class="built_in">text</span>, <span class="number">0</span>, <span class="built_in">text</span>.length(), <span class="built_in">rect</span>);</div><div class="line">    <span class="comment">//刻度文字起始的 x 坐标  -10 是为了让文字和刻度之间留一些空隙</span></div><div class="line">    <span class="built_in">float</span> x = timeHeight - <span class="number">10</span> - measureText;</div><div class="line">    <span class="comment">//得到文字宽的的 1/2</span></div><div class="line">    <span class="built_in">float</span> xText = measureText / <span class="number">2</span>;</div><div class="line">    <span class="comment">//得到的是文字的中间的高度值</span></div><div class="line">    <span class="built_in">float</span> y = (<span class="built_in">rect</span>.bottom - <span class="built_in">rect</span>.top) / <span class="number">2</span>;</div><div class="line">    <span class="comment">//这里是以 1 点为基准，利用三角函数计算得到全部的坐标值</span></div><div class="line">    <span class="built_in">float</span> dx = (<span class="built_in">float</span>) ((x) * Math.<span class="built_in">sin</span>(i * <span class="number">30</span> * Math.<span class="literal">PI</span> / <span class="number">180</span>));</div><div class="line">    <span class="built_in">float</span> dy = (<span class="built_in">float</span>) ((x) * Math.<span class="built_in">cos</span>(i * <span class="number">30</span> * Math.<span class="literal">PI</span> / <span class="number">180</span>));</div><div class="line">    <span class="comment">//绘制刻度文字，在 x 方向上 - 文字宽的的 1/2</span></div><div class="line">    <span class="comment">//在 基线方向上 + 文字的中间的高度值</span></div><div class="line">    canvas.drawText(<span class="built_in">text</span>, dx - xText, -dy + y, mTextPaint);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这样，就绘制出了刻度文字。其实，绘制刻度文字是可以利用<code>rotate()</code>，只不过这样利用画布旋转绘制出的文字有的是倒立的。似乎是我以3点为基准出现的问题。最后，没办法，就利用三角函数，以1点为基准绘制完成了。为何以1点为基准呢？因为循环从1开始啊。。。</p>
<p>第四步，绘制指针并让其动起来：</p>
<p>在绘制指针之前我们是要得到当前的时间的，然后，指针顺着当前时间运动。所以，先要获取当前时间：<br><figure class="highlight lsl"><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></pre></td><td class="code"><pre><div class="line">        Calendar mCalendar = Calendar.getInstance();</div><div class="line">        mTempHour = mCalendar.get(Calendar.HOUR);</div><div class="line">        mTempMinute = mCalendar.get(Calendar.MINUTE);</div><div class="line">        mTempSecond = mCalendar.get(Calendar.SECOND);</div><div class="line"><span class="comment">//                    Log.d(TAG, "handleMessage: " + tempSecond);</span></div><div class="line"><span class="comment">//                    Log.d(TAG, "refreshTime: " + tempHour + tempMinute + tempSecond);</span></div><div class="line">        <span class="comment">//计算出秒指针旋转的角度</span></div><div class="line">        mSecondRotate = mTempSecond * <span class="number">360</span> / <span class="number">60</span>;</div><div class="line"><span class="comment">//                    Log.d(TAG, "handleMessage: " + mSecondRotate);</span></div><div class="line">        <span class="comment">//计算出分指针的旋转的角度</span></div><div class="line">        mMinuteRotate = (<span class="type">float</span>) (mTempMinute * <span class="number">360</span> / <span class="number">60</span> + mTempSecond * <span class="number">0.1</span>);</div><div class="line"><span class="comment">//                    Log.d(TAG, "handleMessage: "+tempMinute);</span></div><div class="line">        <span class="comment">//计算出时针需要旋转的角度</span></div><div class="line">        mHourRotate = (<span class="type">float</span>) (mTempHour * <span class="number">30</span> + mTempMinute * <span class="number">0.5</span>);</div></pre></td></tr></table></figure></p>
<p>这里利用了Calendar，得到当前的时间。然后，计算出各个指针需要旋转的角度。这里，注意，秒针走一秒，分针走当前秒数 <em> 0.1（6/60 = 0.1）度。而分针走0.1度，时针走当前分钟数 </em> 0.5（6*30/360 = 0.5）度。</p>
<figure class="highlight lsl"><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></pre></td><td class="code"><pre><div class="line">canvas.save();</div><div class="line">canvas.save();</div><div class="line">canvas.rotate(hourRotate, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line">canvas.drawLine(<span class="number">0</span>, <span class="number">10</span>, <span class="number">0</span>, -(radius - <span class="number">160</span>), mHourPoint);</div><div class="line">canvas.restore();</div><div class="line">canvas.save();</div><div class="line">canvas.rotate(minuteRotate, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line">canvas.drawLine(<span class="number">0</span>, <span class="number">20</span>, <span class="number">0</span>, -(radius - <span class="number">120</span>), mMinutePaint);</div><div class="line">canvas.restore();</div><div class="line">canvas.save();</div><div class="line">canvas.rotate(secondRotate, <span class="number">0</span>, <span class="number">0</span>);</div><div class="line">canvas.drawLine(<span class="number">0</span>, <span class="number">30</span>, <span class="number">0</span>, -(radius - <span class="number">80</span>), mSecondPoint);</div><div class="line">canvas.restore();</div><div class="line">canvas.restore();</div></pre></td></tr></table></figure>
<p>这里先用<code>save()</code>方法保存整个画布状态，在每次绘制指针前，再次保存画布状态。然后，在绘制指针前都旋转相应的角度。一次次旋转，看起来就像指针在动。之所以每次绘制指针前都保存画布是为了不让三个指针互相干扰。每一个指针都在一层独立的画布上。当然，这一步，一定要将指针绘制到12点的位置，3个指针都是。因为你算出的旋转角度就是从12点起，到12点结束，360度。</p>
<p>第五步，绘制数字时间：</p>
<figure class="highlight arduino"><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">Rect <span class="built_in">rect</span> = <span class="keyword">new</span> Rect();</div><div class="line"><span class="comment">//当前的时间</span></div><div class="line"><span class="keyword">String</span> time = mTempHour + <span class="string">" : "</span> + mTempMinute + <span class="string">" : "</span> + mTempSecond;</div><div class="line"><span class="comment">//测量文字</span></div><div class="line">mNumberPaint.getTextBounds(time, <span class="number">0</span>, time.length(), <span class="built_in">rect</span>);</div><div class="line"><span class="comment">//其实 x 坐标为 文字宽度的一半</span></div><div class="line"><span class="keyword">int</span> dx =<span class="built_in">rect</span>.<span class="built_in">width</span>() / <span class="number">2</span>;</div><div class="line"><span class="comment">//基准线就定为 半径的一半</span></div><div class="line"><span class="keyword">int</span> baseLine = radius / <span class="number">2</span>;</div><div class="line"><span class="comment">//绘制文字</span></div><div class="line">canvas.drawText(time, dx, baseLine, mNumberPaint);</div></pre></td></tr></table></figure>
<p>第六步，绘制作者名字：</p>
<figure class="highlight arduino"><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></pre></td><td class="code"><pre><div class="line">Rect <span class="built_in">rect</span> = <span class="keyword">new</span> Rect();</div><div class="line"><span class="comment">//文字</span></div><div class="line"><span class="keyword">String</span> author = <span class="string">"By Kevin"</span>;</div><div class="line"><span class="comment">//测量文字</span></div><div class="line">mNumberPaint.getTextBounds(author, <span class="number">0</span>, author.length(), <span class="built_in">rect</span>);</div><div class="line"><span class="comment">//起始 x 坐标为文字的一半 ，为负值是在 左方 ，因为原点在屏幕中心</span></div><div class="line"><span class="keyword">int</span> dx = -<span class="built_in">rect</span>.<span class="built_in">width</span>() / <span class="number">2</span>;</div><div class="line"><span class="comment">//基准线就定为 半径的一半 ，为负值在 上方 ，因为原点在屏幕中心</span></div><div class="line"><span class="keyword">int</span> y = -radius / <span class="number">2</span>;</div><div class="line">canvas.drawText(author, dx, y, mAuthorPaint);</div></pre></td></tr></table></figure>
<p>这两步代码基本一样，只是方向相反，也是很简单。</p>
<p>最后，给出<code>onDraw()</code>中的代码：</p>
<figure class="highlight lsl"><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></pre></td><td class="code"><pre><div class="line"> @Override</div><div class="line">    protected void onDraw(Canvas canvas) &#123;</div><div class="line">        <span class="comment">//半径</span></div><div class="line">        radius = mWidth / <span class="number">2</span> - <span class="number">20</span>;</div><div class="line">        <span class="comment">//刻度线的高度</span></div><div class="line">        timeHeight = radius - <span class="number">15</span>;</div><div class="line">        <span class="comment">//坐标移动到中心位置</span></div><div class="line">        canvas.translate(mWidth / <span class="number">2</span>, mHeight / <span class="number">2</span>);</div><div class="line">        <span class="comment">//绘制表盘</span></div><div class="line">        canvas.drawCircle(<span class="number">0</span>, <span class="number">0</span>, radius, mOuterPaint);</div><div class="line">        <span class="comment">//绘制中间的两个小圆圈</span></div><div class="line">        canvas.drawCircle(<span class="number">0</span>, <span class="number">0</span>, radius / <span class="number">32</span>, mOuterPaint);</div><div class="line">        canvas.drawCircle(<span class="number">0</span>, <span class="number">0</span>, radius / <span class="number">64</span>, mOuterPaint);</div><div class="line">        <span class="comment">//绘制作者名字</span></div><div class="line">        drawAuthor(canvas);</div><div class="line"><span class="comment">//        mHandler.sendEmptyMessage(HANDLER_MSG);</span></div><div class="line">        <span class="comment">//延时1s绘制，会不停的执行onDraw()方法，从而实现指针的动态效果</span></div><div class="line">        <span class="comment">//这里利用Handler也是可以的</span></div><div class="line">        postInvalidateDelayed(<span class="number">1000</span>);</div><div class="line">        <span class="comment">//绘制刻度</span></div><div class="line">        drawScale(canvas);</div><div class="line">        <span class="comment">//绘制刻度文字</span></div><div class="line">        drawScaleText(canvas);</div><div class="line">        <span class="comment">//得到当前时间</span></div><div class="line">        refreshTime();</div><div class="line">        <span class="comment">//绘制数字时间</span></div><div class="line">        drawNumberTime(canvas);</div><div class="line">        <span class="comment">//绘制指针</span></div><div class="line">        drawPoints(canvas, mHourRotate, mMinuteRotate, mSecondRotate);</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>到此为止，就完成了这个简易时钟的绘制。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天要实现的是自定义View实现一款简易时钟，主要就是练习如何进行自定义View，所以基本上没有什么美感可言。下面，我们就开始了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://upload-images.jianshu.io/upload_images/1602023-a530916976b8ccc9.gif?imageMogr2/auto-orient/strip&quot; alt=&quot;简易时钟&quot;&gt;&lt;/p&gt;
&lt;p&gt;要实现这款简易时钟，我们还是要先分析出它所包含的元素。就以上面的效果图来说，基本上包括了：最外圈大表盘以及内部中间两个小圆圈，刻度线，刻度文字，表针，数字时间以及上方作者名称。OK，一共就这么多的元素，下面我们就可以开始了。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/categories/Android/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/tags/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
  </entry>
  
  <entry>
    <title>自定义View之字母索引</title>
    <link href="https://KevinJe.github.io/2017/08/21/%E8%87%AA%E5%AE%9A%E4%B9%89View%E4%B9%8B%E5%AD%97%E6%AF%8D%E7%B4%A2%E5%BC%95/"/>
    <id>https://KevinJe.github.io/2017/08/21/自定义View之字母索引/</id>
    <published>2017-08-21T13:08:32.000Z</published>
    <updated>2017-09-22T13:00:50.046Z</updated>
    
    <content type="html"><![CDATA[<p>在Android中，字母索引这一设计在很多App中都有体现。手机联系人、微信、支付宝等在联系人一栏都会有这种设计。这种设计的便捷之处就是可以快捷的查找联系人。所以，我们今天也来打造这么一个自定义View的效果。注意，这里仅仅是模仿UI效果，不会实现功能。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-7286240a2d48c2d7.gif?imageMogr2/auto-orient/strip" alt="字母索引"></p>
<p>这里只是谈一下实现的思路，不再详细的说明各个流程。详细的流程请看上两篇自定义View的文章，那两篇我流程写的很详细。</p>
<a id="more"></a>
<p>由于现在只想要这个IndexBar显示的宽度为字母的宽度+padding。也就是在这里我们的布局width为wrap_content，而height为match_parent。所以，在绘制竖排竖排字母前，我们还是要测量一下View。</p>
<p><code>onMeasure()</code>中：</p>
<figure class="highlight processing"><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"></div><div class="line">@Override</div><div class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> onMeasure(<span class="built_in">int</span> widthMeasureSpec, <span class="built_in">int</span> heightMeasureSpec) &#123;</div><div class="line">        <span class="keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);</div><div class="line">        <span class="built_in">int</span> <span class="built_in">height</span> = MeasureSpec.getSize(heightMeasureSpec);</div><div class="line">        <span class="comment">//测量文字宽度</span></div><div class="line">        mTextWidth = (<span class="built_in">int</span>) mPaint.measureText(<span class="string">"A"</span>);</div><div class="line">        <span class="comment">//view的宽 = 左边padding + 右边padding　+ 文字大小</span></div><div class="line">        <span class="built_in">int</span> <span class="built_in">width</span> = getPaddingLeft() + getPaddingRight() + mTextWidth;</div><div class="line">        setMeasuredDimension(<span class="built_in">width</span>, <span class="built_in">height</span>);</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>这里很好理解，由于高度是match_parent，所以直接不用处理高度。这里只要处理宽度就可以了。通过计算得到View的宽，之后只需要使用<code>setMeasuredDimension(width, height)</code>就完成了整个View的测量。</p>
<p>接下来，就可以进行View的绘制了。</p>
<p><code>onDraw()</code>中：</p>
<figure class="highlight processing"><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></pre></td><td class="code"><pre><div class="line">@Override</div><div class="line">   <span class="keyword">protected</span> <span class="keyword">void</span> onDraw(Canvas canvas) &#123;</div><div class="line">       <span class="comment">//每个字母x坐标的起始位置 = View的宽度 - 字母宽度 - 右边的padding</span></div><div class="line">       <span class="built_in">int</span> x = getWidth() - mTextWidth - getPaddingRight();</div><div class="line">       <span class="comment">//单个字母的高度 = View的高度 / 字母总数</span></div><div class="line">       <span class="built_in">float</span> singleLetterHeight = (getHeight()) / letters.length;</div><div class="line">       <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; letters.length; i++) &#123;</div><div class="line">           <span class="comment">//得到具体的字母</span></div><div class="line">           <span class="keyword">String</span> letter = letters[i];</div><div class="line">           <span class="comment">//拿到当前画笔的 Paint.FontMetrics</span></div><div class="line">           Paint.FontMetrics metrics = mLetterPaint.getFontMetrics();</div><div class="line">           <span class="comment">//第一个字母的基线位置（y坐标）</span></div><div class="line">           <span class="built_in">int</span> y = (<span class="built_in">int</span>) ((metrics.bottom - metrics.top) / <span class="number">2</span> - metrics.bottom);</div><div class="line">           <span class="comment">//每一个字母的基线y坐标 = 单个字母高度/2 + 第i个字母的高度 + 第一个字母的基线坐标</span></div><div class="line">           <span class="comment">//这里之所以加上 singleLetterHeight / 2 是为了让字母整体向下移动，因为第一个字母会显示不全</span></div><div class="line">           <span class="built_in">int</span> baseLine = (<span class="built_in">int</span>) ((singleLetterHeight / <span class="number">2</span> + singleLetterHeight * i) + y);</div><div class="line">           <span class="comment">//通过上面计算的坐标，开始绘制每个字母</span></div><div class="line">           canvas.drawText(letter, x, baseLine, mLetterPaint);</div><div class="line">       &#125;</div><div class="line">       <span class="comment">//手指触摸时出现的覆盖在字母上的深颜色的矩形框的位置</span></div><div class="line">       Rect <span class="built_in">rect</span> = <span class="keyword">new</span> Rect(x / <span class="number">2</span>, <span class="number">0</span>, getWidth(), getHeight());</div><div class="line">       <span class="keyword">if</span> (isDown) &#123;</div><div class="line">           <span class="comment">//绘制那个矩形框</span></div><div class="line">           canvas.drawRect(<span class="built_in">rect</span>, mPaint);</div><div class="line">           <span class="comment">//为了让手指离开时，矩形框也消失的一个flag</span></div><div class="line">           isDown = <span class="keyword">false</span>;</div><div class="line">       &#125;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>当然下面这样进行坐标的计算也是可以的,是利用getTextBounds()：</p>
<figure class="highlight processing"><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></pre></td><td class="code"><pre><div class="line">@Override</div><div class="line">   <span class="keyword">protected</span> <span class="keyword">void</span> onDraw(Canvas canvas) &#123;</div><div class="line">       <span class="comment">//每个字母x坐标的起始位置 = View的宽度 - 字母宽度 - 右边的padding</span></div><div class="line">       <span class="built_in">int</span> x = getWidth() - mTextWidth - getPaddingRight();</div><div class="line">       <span class="comment">//单个字母的高度 = View的高度 / 字母总数</span></div><div class="line">       <span class="built_in">float</span> singleLetterHeight = (getHeight()) / letters.length;</div><div class="line">       <span class="keyword">for</span> (<span class="built_in">int</span> i = <span class="number">0</span>; i &lt; letters.length; i++) &#123;</div><div class="line">           <span class="comment">//得到具体的字母</span></div><div class="line">           <span class="keyword">String</span> letter = letters[i];</div><div class="line">           Rect <span class="built_in">rect</span> = <span class="keyword">new</span> Rect();</div><div class="line">           mLetterPaint.getTextBounds(letter,<span class="number">0</span>,letter.length(),<span class="built_in">rect</span>);</div><div class="line">           <span class="comment">//第一个字母的基线位置</span></div><div class="line">           <span class="built_in">int</span> y = <span class="built_in">rect</span>.bottom - <span class="built_in">rect</span>.top;</div><div class="line">           Log.d(TAG, <span class="string">"onDraw: "</span>+y);</div><div class="line">           Log.d(TAG, <span class="string">"onDraw: "</span>+<span class="built_in">rect</span>.bottom);</div><div class="line">           Log.d(TAG, <span class="string">"onDraw: "</span>+<span class="built_in">rect</span>.top);</div><div class="line">           <span class="comment">//每一个字母的基线y坐标 = 单个字母高度/2 + 第i个字母的高度 + 第一个字母的基线坐标</span></div><div class="line">           <span class="comment">//这里之所以加上 singleLetterHeight / 2 是为了让字母整体向下移动</span></div><div class="line">           <span class="built_in">int</span> baseLine = (<span class="built_in">int</span>) ((singleLetterHeight / <span class="number">2</span> + singleLetterHeight * i) + y);</div><div class="line">           <span class="comment">//通过上面计算的坐标，开始绘制每个字母</span></div><div class="line">           canvas.drawText(letter, x, baseLine, mLetterPaint);</div><div class="line">       &#125;</div><div class="line">       <span class="comment">//手指触摸时出现的覆盖在字母上的深颜色的矩形框的位置</span></div><div class="line">       Rect <span class="built_in">rect</span> = <span class="keyword">new</span> Rect(x / <span class="number">2</span>, <span class="number">0</span>, getWidth(), getHeight());</div><div class="line">       <span class="keyword">if</span> (isDown) &#123;</div><div class="line">           <span class="comment">//绘制那个矩形框</span></div><div class="line">           canvas.drawRect(<span class="built_in">rect</span>, mPaint);</div><div class="line">           <span class="comment">//为了让手指离开时，矩形框也消失的一个flag</span></div><div class="line">           isDown = <span class="keyword">false</span>;</div><div class="line">       &#125;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>绘制矩形框时，发现矩形框与字母颜色类似，做了如下处理：</p>
<figure class="highlight mipsasm"><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></pre></td><td class="code"><pre><div class="line">private void init(<span class="built_in">Context</span> <span class="built_in">context</span>, AttributeSet attrs) &#123;</div><div class="line">        TypedArray array = <span class="built_in">context</span>.obtainStyledAttributes(attrs, R.styleable.LetterSiderBar)<span class="comment">;</span></div><div class="line">        mLetterColor = array.getColor(R.styleable.LetterSiderBar_letterTextColor, mLetterColor)<span class="comment">;</span></div><div class="line">        mLetterSize = array.getDimension(R.styleable.LetterSiderBar_letterTextSize, mLetterSize)<span class="comment">;</span></div><div class="line">        mPaint = new Paint()<span class="comment">;</span></div><div class="line">        mPaint.setAntiAlias(true)<span class="comment">;</span></div><div class="line">        mPaint.setColor(Color.DKGRAY)<span class="comment">;</span></div><div class="line">        mPaint.setAlpha(<span class="number">127</span>)<span class="comment">;</span></div><div class="line">        mPaint.setStyle(Paint.Style.FILL)<span class="comment">;</span></div><div class="line"></div><div class="line">        mLetterPaint = new Paint()<span class="comment">;</span></div><div class="line">        mLetterPaint.setAntiAlias(true)<span class="comment">;</span></div><div class="line">        mLetterPaint.setTextSize(sp2px(<span class="number">15</span>))<span class="comment">;</span></div><div class="line">        mLetterPaint.setColor(Color.GRAY)<span class="comment">;</span></div><div class="line">        mLetterPaint.setStyle(Paint.Style.STROKE)<span class="comment">;</span></div><div class="line">        mLetterPaint.setTypeface(Typeface.DEFAULT_BOLD)<span class="comment">;</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p><code>setAlpha(127)</code>，允许传入0~255之间的一个值，0为全透明，255为不同明。这里传入127表示半透明。这样就解决了同是灰色显示不清的问题。</p>
<p>绘制完字母之后，就需要进行触摸手势的处理了，这里只需要重写<code>onTouchEvent()</code>方法：</p>
<figure class="highlight aspectj"><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="meta">@Override</span></div><div class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">boolean</span> <span class="title">onTouchEvent</span><span class="params">(MotionEvent event)</span> </span>&#123;</div><div class="line">        <span class="keyword">switch</span> (event.getAction()) &#123;</div><div class="line">            <span class="comment">//手指按下</span></div><div class="line">            <span class="keyword">case</span> MotionEvent.ACTION_DOWN:</div><div class="line">            //手指滑动</div><div class="line">            <span class="keyword">case</span> MotionEvent.ACTION_MOVE:</div><div class="line">                //当前位置</div><div class="line">                <span class="keyword">float</span> currentMoveY = event.getY();</div><div class="line">                <span class="comment">//单个字母高度</span></div><div class="line">                <span class="keyword">float</span> singleLetterHeight = (getHeight() - getPaddingTop() - getPaddingBottom()) / letters.length;</div><div class="line">                <span class="comment">//当前字母所在位置</span></div><div class="line">                <span class="keyword">int</span> currentPosition = (<span class="keyword">int</span>) (currentMoveY / singleLetterHeight);</div><div class="line">                <span class="comment">//当前的位置&lt;0，就代表手指滑到了View之外，View最上面</span></div><div class="line">                <span class="comment">//直接将当前位置置为第一个字母位置</span></div><div class="line">                <span class="keyword">if</span> (currentPosition &lt; <span class="number">0</span>) &#123;</div><div class="line">                    currentPosition = <span class="number">0</span>;</div><div class="line">                &#125;</div><div class="line">                <span class="comment">//当前位置 &gt; 所有字母数，手指在View之外，View最下面</span></div><div class="line">                <span class="comment">//直接将当前位置置为最后一个字母位置</span></div><div class="line">                <span class="keyword">if</span> (currentPosition &gt; letters.length - <span class="number">1</span>) &#123;</div><div class="line">                    currentPosition = letters.length - <span class="number">1</span>;</div><div class="line">                &#125;</div><div class="line">                <span class="comment">//得到当前字母</span></div><div class="line">                mCurrentLetter = letters[currentPosition];</div><div class="line">                <span class="keyword">if</span> (mListener != <span class="keyword">null</span>) &#123;</div><div class="line">                    isTouch = <span class="keyword">true</span>;</div><div class="line">                    mListener.onTouch(mCurrentLetter, isTouch);</div><div class="line">                &#125;</div><div class="line">                isDown = <span class="keyword">true</span>;</div><div class="line">                <span class="keyword">break</span>;</div><div class="line">            <span class="comment">//手指抬起</span></div><div class="line">            <span class="keyword">case</span> MotionEvent.ACTION_UP:</div><div class="line">                postDelayed(<span class="keyword">new</span> Runnable() &#123;</div><div class="line">                    <span class="meta">@Override</span></div><div class="line">                    <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</div><div class="line">                        <span class="keyword">if</span> (mListener != <span class="keyword">null</span>) &#123;</div><div class="line">                            isTouch = <span class="keyword">false</span>;</div><div class="line">                            mListener.onTouch(mCurrentLetter, isTouch);</div><div class="line">                        &#125;</div><div class="line">                    &#125;</div><div class="line">                &#125;, <span class="number">500</span>);</div><div class="line">                <span class="keyword">break</span>;</div><div class="line">            <span class="keyword">default</span>:</div><div class="line">        &#125;</div><div class="line">        invalidate();</div><div class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<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><div class="line">6</div><div class="line">7</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">setOnSlideLetterBarTochListener</span><span class="params">(OnSlideLetterBarTochListener listener)</span> </span>&#123;</div><div class="line">        <span class="keyword">this</span>.mListener = listener;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">OnSlideLetterBarTochListener</span> </span>&#123;</div><div class="line">        <span class="function"><span class="keyword">void</span> <span class="title">onTouch</span><span class="params">(String letter, <span class="keyword">boolean</span> isToch)</span></span>;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>最后，布局文件：</p>
<figure class="highlight stylus"><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></pre></td><td class="code"><pre><div class="line">&lt;RelativeLayout xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></div><div class="line">                android:layout_width=<span class="string">"match_parent"</span></div><div class="line">                android:layout_height=<span class="string">"match_parent"</span></div><div class="line">    &gt;</div><div class="line"></div><div class="line">    &lt;com<span class="selector-class">.example</span><span class="selector-class">.letterindexview</span><span class="selector-class">.LetterSiderBar</span></div><div class="line">        android:id=<span class="string">"@+id/slide"</span></div><div class="line">        android:layout_width=<span class="string">"match_parent"</span></div><div class="line">        android:layout_height=<span class="string">"match_parent"</span></div><div class="line">        android:layout_alignParentRight=<span class="string">"true"</span></div><div class="line">        android:<span class="attribute">padding</span>=<span class="string">"15dp"</span></div><div class="line">        /&gt;</div><div class="line"></div><div class="line">    &lt;TextView</div><div class="line">        android:id=<span class="string">"@+id/text"</span></div><div class="line">        android:layout_width=<span class="string">"wrap_content"</span></div><div class="line">        android:layout_height=<span class="string">"wrap_content"</span></div><div class="line">        android:layout_centerInParent=<span class="string">"true"</span></div><div class="line">        android:<span class="attribute">background</span>=<span class="string">"@drawable/shape_text"</span></div><div class="line">        android:gravity=<span class="string">"center"</span></div><div class="line">        android:textColor=<span class="string">"#fff"</span></div><div class="line">        android:textSize=<span class="string">"30sp"</span></div><div class="line">        android:<span class="attribute">visibility</span>=<span class="string">"gone"</span></div><div class="line">        /&gt;</div><div class="line">&lt;/RelativeLayout&gt;</div></pre></td></tr></table></figure>
<p>屏幕中心圆形文字，是一个TextView，背景是一个shape。<br><figure class="highlight processing"><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">&lt;<span class="built_in">shape</span> xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></div><div class="line">       android:<span class="built_in">shape</span>=<span class="string">"oval"</span></div><div class="line">    &gt;</div><div class="line">    &lt;solid android:<span class="built_in">color</span>=<span class="string">"@color/colorPrimary"</span>/&gt;</div><div class="line">    &lt;<span class="built_in">size</span> android:<span class="built_in">width</span>=<span class="string">"60dp"</span>/&gt;</div><div class="line">    &lt;<span class="built_in">size</span> android:<span class="built_in">height</span>=<span class="string">"60dp"</span>/&gt;</div><div class="line">&lt;/<span class="built_in">shape</span>&gt;</div></pre></td></tr></table></figure></p>
<p>最后在Activity中处理：</p>
<figure class="highlight processing"><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">@Override</div><div class="line"><span class="keyword">protected</span> <span class="keyword">void</span> onCreate(Bundle savedInstanceState) &#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">    <span class="keyword">final</span> TextView <span class="built_in">text</span> = (TextView) findViewById(R.id.<span class="built_in">text</span>);</div><div class="line">    LetterSiderBar letter = (LetterSiderBar) findViewById(R.id.slide);</div><div class="line">    letter.setOnSlideLetterBarTochListener(<span class="keyword">new</span> LetterSiderBar.OnSlideLetterBarTochListener() &#123;</div><div class="line">        @Override</div><div class="line">        <span class="keyword">public</span> <span class="keyword">void</span> onTouch(<span class="keyword">final</span> <span class="keyword">String</span> letter, <span class="built_in">boolean</span> isToch) &#123;</div><div class="line">            <span class="keyword">if</span> (isToch) &#123;</div><div class="line">                <span class="built_in">text</span>.setVisibility(View.VISIBLE);</div><div class="line">                <span class="built_in">text</span>.setText(letter);</div><div class="line">            &#125; <span class="keyword">else</span> &#123;</div><div class="line">                <span class="built_in">text</span>.setVisibility(View.GONE);</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">    &#125;);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这样字母索引就完成了。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在Android中，字母索引这一设计在很多App中都有体现。手机联系人、微信、支付宝等在联系人一栏都会有这种设计。这种设计的便捷之处就是可以快捷的查找联系人。所以，我们今天也来打造这么一个自定义View的效果。注意，这里仅仅是模仿UI效果，不会实现功能。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://upload-images.jianshu.io/upload_images/1602023-7286240a2d48c2d7.gif?imageMogr2/auto-orient/strip&quot; alt=&quot;字母索引&quot;&gt;&lt;/p&gt;
&lt;p&gt;这里只是谈一下实现的思路，不再详细的说明各个流程。详细的流程请看上两篇自定义View的文章，那两篇我流程写的很详细。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/categories/Android/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/tags/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
  </entry>
  
  <entry>
    <title>Android中dp、sp与px之间的换算</title>
    <link href="https://KevinJe.github.io/2017/08/21/Android%E4%B8%ADdp%E3%80%81sp%E4%B8%8Epx%E4%B9%8B%E9%97%B4%E7%9A%84%E6%8D%A2%E7%AE%97/"/>
    <id>https://KevinJe.github.io/2017/08/21/Android中dp、sp与px之间的换算/</id>
    <published>2017-08-21T08:11:01.000Z</published>
    <updated>2017-09-22T12:58:10.053Z</updated>
    
    <content type="html"><![CDATA[<p>在Android中我们常见的尺寸单位有dp、sp、px。</p>
<ul>
<li>px<br>通常我们会以手机对角线的长度来定义吋。比如：5.5吋，5.7吋。<br>而分辨率则是手机的像素点数。如：1920*1080，就是高1920个像素，宽1080像素。像素点数就决定我们屏幕的细腻程度，现在有的机器已经上了2k的屏幕了。而这里的像素点数的单位就是px。</li>
</ul>
<a id="more"></a>
<ul>
<li><p>dp<br>但是Android设备的屏幕大小不，这就对屏幕设配增加了难度。所以，为了满足不同分辨率的设备。Android使用了dp，dp是一种相对单位（根据屏幕的像素密度，也就是每英寸像素数的不同而调整），能够尽量保证相同数值的dp长度在不同的设备上显示的实际尺寸大致一样。</p>
</li>
<li><p>sp<br>同样的，sp是针对于文字的一种相对尺寸单位，其也会根据屏幕的像素密度的不同而调整，并考虑了字体不同的影响。</p>
</li>
</ul>
<table>
<thead>
<tr>
<th style="text-align:left">密度</th>
<th style="text-align:center">ldpi</th>
<th style="text-align:center">mdpi</th>
<th style="text-align:center">hdpi</th>
<th style="text-align:center">xhdpi</th>
<th style="text-align:center">xxhdpi</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:left">密度值</td>
<td style="text-align:center">120</td>
<td style="text-align:center">160</td>
<td style="text-align:center">240</td>
<td style="text-align:center">320</td>
<td style="text-align:center">480</td>
</tr>
<tr>
<td style="text-align:left">分辨率</td>
<td style="text-align:center">240*320</td>
<td style="text-align:center">320*480</td>
<td style="text-align:center">480*800</td>
<td style="text-align:center">720*1280</td>
<td style="text-align:center">1080*1920</td>
</tr>
<tr>
<td style="text-align:left">dp、px的关系</td>
<td style="text-align:center">1dp = 0.75px</td>
<td style="text-align:center">1dp = 1px</td>
<td style="text-align:center">1dp = 1.5px</td>
<td style="text-align:center">1dp = 2.0px</td>
<td style="text-align:center">1dp = 3.0px</td>
</tr>
</tbody>
</table>
<p>在Android中使用mdpi，即密度值为160的屏幕为标准。在这样的屏幕下，1dp = 1px。所以，可以参照上表得到换算比例。</p>
<p><code>ldpi:mdpi:hdpi:xhdpi:xxhdpi = 3:4:6:8:12</code></p>
<p>所以就有了dp、sp、px之间的转换，为了方便使用，我们可以单独抽出一个类，进行封装。</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><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</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">DisplayUtil</span></span>&#123;</div><div class="line">    <span class="comment">/**</span></div><div class="line">     * px转化为dp或者dip</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> context</div><div class="line">     * <span class="doctag">@param</span> pxValue</div><div class="line">     * <span class="doctag">@return</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">int</span> <span class="title">px2dip</span><span class="params">(Context context, <span class="keyword">float</span> pxValue)</span> </span>&#123;</div><div class="line">        <span class="comment">//得到换算比例</span></div><div class="line">        <span class="keyword">float</span> scale = context.getResources().getDisplayMetrics().density;</div><div class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>) (pxValue / scale + <span class="number">0.5f</span>); <span class="comment">//加上0.5f是为了结果四舍五入</span></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * dip或者dp转换为px</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> context</div><div class="line">     * <span class="doctag">@param</span> dpValue</div><div class="line">     * <span class="doctag">@return</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">int</span> <span class="title">dip2px</span><span class="params">(Context context, <span class="keyword">float</span> dpValue)</span> </span>&#123;</div><div class="line">        <span class="comment">//得到换算比例</span></div><div class="line">        <span class="keyword">float</span> scale = context.getResources().getDisplayMetrics().density;</div><div class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>) (dpValue * scale + <span class="number">0.5f</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * px转换为sp</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> context</div><div class="line">     * <span class="doctag">@param</span> pxValue</div><div class="line">     * <span class="doctag">@return</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">int</span> <span class="title">px2sp</span><span class="params">(Context context, <span class="keyword">float</span> pxValue)</span> </span>&#123;</div><div class="line">        <span class="comment">//得到换算比例</span></div><div class="line">        <span class="keyword">float</span> scale = context.getResources().getDisplayMetrics().density;</div><div class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>) (pxValue / scale + <span class="number">0.5f</span>);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="comment">/**</span></div><div class="line">     * sp转化为px</div><div class="line">     *</div><div class="line">     * <span class="doctag">@param</span> context</div><div class="line">     * <span class="doctag">@param</span> spValue</div><div class="line">     * <span class="doctag">@return</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">int</span> <span class="title">sp2px</span><span class="params">(Context context, <span class="keyword">float</span> spValue)</span> </span>&#123;</div><div class="line">        <span class="comment">//得到换算比例</span></div><div class="line">        <span class="keyword">float</span> fontScale = context.getResources().getDisplayMetrics().density;</div><div class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>) (spValue * fontScale + <span class="number">0.5f</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>当然，系统也是提供好了一个TypedValue类帮助我们转换单位。</p>
<figure class="highlight aspectj"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">/**</span></div><div class="line">    * dip或者dp转换为px</div><div class="line">    *</div><div class="line">    * <span class="doctag">@param</span> context</div><div class="line">    * <span class="doctag">@param</span> dpValue</div><div class="line">    * <span class="doctag">@return</span></div><div class="line">    */</div><div class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="function"><span class="keyword">int</span> <span class="title">dip2px</span><span class="params">(Context context, <span class="keyword">float</span> dpValue)</span> </span>&#123;</div><div class="line">       <span class="keyword">return</span> (<span class="keyword">int</span>) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());</div><div class="line">   &#125;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line">    * sp转化为px</div><div class="line">    *</div><div class="line">    * <span class="doctag">@param</span> context</div><div class="line">    * <span class="doctag">@param</span> spValue</div><div class="line">    * <span class="doctag">@return</span></div><div class="line">    */</div><div class="line">   <span class="keyword">public</span> <span class="keyword">static</span> <span class="function"><span class="keyword">int</span> <span class="title">sp2px</span><span class="params">(Context context, <span class="keyword">float</span> spValue)</span> </span>&#123;</div><div class="line">     <span class="keyword">return</span> (<span class="keyword">int</span>) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, context.getResources().getDisplayMetrics());</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>看一下，<code>applyDimension</code>方法内部实现：</p>
<figure class="highlight cs"><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="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">float</span> <span class="title">applyDimension</span>(<span class="params"><span class="keyword">int</span> unit, <span class="keyword">float</span> <span class="keyword">value</span>,</span></span></div><div class="line">                                      DisplayMetrics metrics)</div><div class="line">   &#123;</div><div class="line">       <span class="keyword">switch</span> (unit) &#123;</div><div class="line">       <span class="keyword">case</span> COMPLEX_UNIT_PX:</div><div class="line">           <span class="keyword">return</span> <span class="keyword">value</span>;</div><div class="line">       <span class="keyword">case</span> COMPLEX_UNIT_DIP:</div><div class="line">           <span class="keyword">return</span> <span class="keyword">value</span> * metrics.density;</div><div class="line">       <span class="keyword">case</span> COMPLEX_UNIT_SP:</div><div class="line">           <span class="keyword">return</span> <span class="keyword">value</span> * metrics.scaledDensity;</div><div class="line">       <span class="keyword">case</span> COMPLEX_UNIT_PT:</div><div class="line">           <span class="keyword">return</span> <span class="keyword">value</span> * metrics.xdpi * (<span class="number">1.0</span>f/<span class="number">72</span>);</div><div class="line">       <span class="keyword">case</span> COMPLEX_UNIT_IN:</div><div class="line">           <span class="keyword">return</span> <span class="keyword">value</span> * metrics.xdpi;</div><div class="line">       <span class="keyword">case</span> COMPLEX_UNIT_MM:</div><div class="line">           <span class="keyword">return</span> <span class="keyword">value</span> * metrics.xdpi * (<span class="number">1.0</span>f/<span class="number">25.4</span>f);</div><div class="line">       &#125;</div><div class="line">       <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>看到了吧，其实和我们上面的写法一致，只不过是系统帮助我们封装好的。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在Android中我们常见的尺寸单位有dp、sp、px。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;px&lt;br&gt;通常我们会以手机对角线的长度来定义吋。比如：5.5吋，5.7吋。&lt;br&gt;而分辨率则是手机的像素点数。如：1920*1080，就是高1920个像素，宽1080像素。像素点数就决定我们屏幕的细腻程度，现在有的机器已经上了2k的屏幕了。而这里的像素点数的单位就是px。&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="dp、sp、px" scheme="https://KevinJe.github.io/tags/dp%E3%80%81sp%E3%80%81px/"/>
    
  </entry>
  
  <entry>
    <title>自定义View之运动步数</title>
    <link href="https://KevinJe.github.io/2017/08/20/%E8%87%AA%E5%AE%9A%E4%B9%89View%E4%B9%8B%E8%BF%90%E5%8A%A8%E6%AD%A5%E6%95%B0/"/>
    <id>https://KevinJe.github.io/2017/08/20/自定义View之运动步数/</id>
    <published>2017-08-20T12:28:14.000Z</published>
    <updated>2017-09-22T13:00:26.503Z</updated>
    
    <content type="html"><![CDATA[<p>上一篇中介绍了如何实现自定义View的流程。那么，在这一篇中我们就动手打造一个简单的运动步数的自定义View。可以看一下gif图展示了本次的运动步数的效果。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-d4107cb7567beee5.gif?imageMogr2/auto-orient/strip" alt="运动步数"></p>
<p>这里的效果，我想大部分人都见过。下面来看一下具体的实现过程。</p>
<a id="more"></a>
<h3 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h3><h4 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h4><p>通过gif图，我们可以对上图进行拆解。图中一共四个元素。分别是：最底层的弧线、动态显示的步数弧线、记录当前步数的文字、“步数”文字。整个拆解之后，就很容易了，我们只需要一步一步的绘制，就能得到效果了。至于动态的显示步数，则需要用到属性动画来处理。下面我们来一步一步实现。</p>
<h4 id="自定义属性"><a href="#自定义属性" class="headerlink" title="自定义属性"></a>自定义属性</h4><p>在values目录下新建attrs.xml文件。在其中定义自己用得上的属性。</p>
<figure class="highlight sqf"><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></pre></td><td class="code"><pre><div class="line">&lt;declare-styleable <span class="built_in">name</span>=<span class="string">"StepsView"</span>&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"radius"</span> <span class="built_in">format</span>=<span class="string">"dimension"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"bottomPaintColor"</span> <span class="built_in">format</span>=<span class="string">"color|reference"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"stepsPaintColor"</span> <span class="built_in">format</span>=<span class="string">"color|reference"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"startAngle"</span> <span class="built_in">format</span>=<span class="string">"integer"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"sweepAngle"</span> <span class="built_in">format</span>=<span class="string">"integer"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"paintWidth"</span> <span class="built_in">format</span>=<span class="string">"integer"</span>/&gt;</div><div class="line">    &lt;/declare-styleable&gt;</div></pre></td></tr></table></figure>
<h4 id="得到自定义属性"><a href="#得到自定义属性" class="headerlink" title="得到自定义属性"></a>得到自定义属性</h4><p>这里直接继承View，自己写这么一个效果。</p>
<figure class="highlight mipsasm"><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">public class StepsView <span class="keyword">extends </span>View &#123;</div><div class="line">public StepsView(<span class="built_in">Context</span> <span class="built_in">context</span>) &#123;</div><div class="line">        this(<span class="built_in">context</span>, null)<span class="comment">;</span></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    public StepsView(<span class="built_in">Context</span> <span class="built_in">context</span>, @Nullable AttributeSet attrs) &#123;</div><div class="line">        this(<span class="built_in">context</span>, attrs, <span class="number">0</span>)<span class="comment">;</span></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    public StepsView(<span class="built_in">Context</span> <span class="built_in">context</span>, @Nullable AttributeSet attrs, int defStyleAttr) &#123;</div><div class="line">        super(<span class="built_in">context</span>, attrs, defStyleAttr)<span class="comment">;</span></div><div class="line">        init(<span class="built_in">context</span>, attrs)<span class="comment">;</span></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    private void init(<span class="built_in">Context</span> <span class="built_in">context</span>, AttributeSet attrs) &#123;</div><div class="line">        TypedArray array = <span class="built_in">context</span>.obtainStyledAttributes(attrs, R.styleable.StepsView)<span class="comment">;</span></div><div class="line">        mRadius = array.getDimension(R.styleable.StepsView_radius, defRadius)<span class="comment">;</span></div><div class="line">        int <span class="keyword">bottomPaintColor </span>= array.getColor(R.styleable.StepsView_bottomPaintColor, Color.<span class="keyword">BLUE);</span></div><div class="line">        int stepsPaintColor = array.getColor(R.styleable.StepsView_stepsPaintColor, getResources().getColor(R.color.colorAccent))<span class="comment">;</span></div><div class="line">        mStartAngle = array.getInteger(R.styleable.StepsView_startAngle, defStartAngle)<span class="comment">;</span></div><div class="line">        mSweepAngle = array.getInteger(R.styleable.StepsView_sweepAngle, defSweepAngle)<span class="comment">;</span></div><div class="line">        int paintWidth = array.getInteger(R.styleable.StepsView_paintWidth, defPaintWidth)<span class="comment">;</span></div><div class="line">        array.recycle()<span class="comment">;</span></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>得到上面的属性后，就可以用各个属性了。</p>
<h4 id="初始化画笔"><a href="#初始化画笔" class="headerlink" title="初始化画笔"></a>初始化画笔</h4><figure class="highlight pony"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">//1.外部圆圈</span></div><div class="line">centerX = mWidth / <span class="number">2</span>;</div><div class="line">centerY = mHeight / <span class="number">2</span>;</div><div class="line">mBottomPaint = <span class="function"><span class="keyword">new</span> <span class="title">Paint</span>();</span></div><div class="line"><span class="title">mBottomPaint</span>.<span class="title">setColor</span>(bottomPaintColor);</div><div class="line"><span class="title">mBottomPaint</span>.<span class="title">setStrokeWidth</span>(paintWidth);</div><div class="line"><span class="title">mBottomPaint</span>.<span class="title">setAntiAlias</span>(true);</div><div class="line"><span class="comment">// 设置为 ROUND，即圆角描边</span></div><div class="line"><span class="title">mBottomPaint</span>.<span class="title">setStrokeCap</span>(<span class="type">Paint</span>.<span class="type">Cap</span>.<span class="type">ROUND</span>);</div><div class="line"><span class="title">mBottomPaint</span>.<span class="title">setStrokeJoin</span>(<span class="type">Paint</span>.<span class="type">Join</span>.<span class="type">ROUND</span>);</div><div class="line"><span class="title">mBottomPaint</span>.<span class="title">setStyle</span>(<span class="type">Paint</span>.<span class="type">Style</span>.<span class="type">STROKE</span>);</div><div class="line"><span class="title">ArcRectF</span> = <span class="title">new</span> <span class="title">RectF</span>(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius);</div><div class="line"></div><div class="line"><span class="comment">//2.第二层圆圈,当前进度</span></div><div class="line"><span class="title">mStepsPaint</span> = <span class="title">new</span> <span class="title">Paint</span>();</div><div class="line"><span class="title">mStepsPaint</span>.<span class="title">setColor</span>(stepsPaintColor);</div><div class="line"><span class="title">mStepsPaint</span>.<span class="title">setStrokeWidth</span>(paintWidth);</div><div class="line"><span class="title">mStepsPaint</span>.<span class="title">setAntiAlias</span>(true);</div><div class="line"><span class="comment">// 设置为 ROUND，即圆角描边</span></div><div class="line"><span class="title">mStepsPaint</span>.<span class="title">setStrokeCap</span>(<span class="type">Paint</span>.<span class="type">Cap</span>.<span class="type">ROUND</span>);</div><div class="line"><span class="title">mStepsPaint</span>.<span class="title">setStrokeJoin</span>(<span class="type">Paint</span>.<span class="type">Join</span>.<span class="type">ROUND</span>);</div><div class="line"><span class="title">mStepsPaint</span>.<span class="title">setStyle</span>(<span class="type">Paint</span>.<span class="type">Style</span>.<span class="type">STROKE</span>);</div><div class="line"></div><div class="line"><span class="comment">//3.当前步数的画笔</span></div><div class="line"><span class="title">mTextPaint</span> = <span class="title">new</span> <span class="title">Paint</span>();</div><div class="line"><span class="title">mTextPaint</span>.<span class="title">setColor</span>(getResources().<span class="title">getColor</span>(<span class="type">R</span>.color.colorAccent));</div><div class="line"><span class="title">mTextPaint</span>.<span class="title">setTextSize</span>(dipToPx(<span class="number">50</span>));</div><div class="line"><span class="title">mTextPaint</span>.<span class="title">setTypeface</span>(<span class="type">Typeface</span>.<span class="type">DEFAULT_BOLD</span>);</div><div class="line"><span class="title">mTextPaint</span>.<span class="title">setAntiAlias</span>(true);</div><div class="line"><span class="title">mTextPaint</span>.<span class="title">setStyle</span>(<span class="type">Paint</span>.<span class="type">Style</span>.<span class="type">STROKE</span>);</div><div class="line"><span class="comment">//最下方“步数”两个字的画笔</span></div><div class="line"><span class="title">mStepPaint</span> = <span class="title">new</span> <span class="title">Paint</span>();</div><div class="line"><span class="title">mStepPaint</span>.<span class="title">setColor</span>(<span class="type">Color</span>.<span class="type">GRAY</span>);</div><div class="line"><span class="title">mStepPaint</span>.<span class="title">setTextSize</span>(dipToPx(<span class="number">30</span>));</div><div class="line"><span class="title">mStepPaint</span>.<span class="title">setTypeface</span>(<span class="type">Typeface</span>.<span class="type">DEFAULT_BOLD</span>);</div><div class="line"><span class="title">mStepPaint</span>.<span class="title">setAntiAlias</span>(true);</div><div class="line"><span class="title">mStepPaint</span>.<span class="title">setStyle</span>(<span class="type">Paint</span>.<span class="type">Style</span>.<span class="type">STROKE</span>);</div></pre></td></tr></table></figure>
<p>可以这样获取View的宽高，由于我们的View只是变化一次，所以<code>onSizeChanged()</code>只会调用一次。</p>
<figure class="highlight aspectj"><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="keyword">protected</span> <span class="function"><span class="keyword">void</span> <span class="title">onSizeChanged</span><span class="params">(<span class="keyword">int</span> w, <span class="keyword">int</span> h, <span class="keyword">int</span> oldw, <span class="keyword">int</span> oldh)</span> </span>&#123;</div><div class="line">       <span class="keyword">super</span>.onSizeChanged(w, h, oldw, oldh);</div><div class="line">       mWidth = w;</div><div class="line">       mHeight = h;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<h4 id="在onDraw-中绘制"><a href="#在onDraw-中绘制" class="headerlink" title="在onDraw()中绘制"></a>在<code>onDraw()</code>中绘制</h4><p>这一步是整个View的关键，这一步你就可以自己设计了。</p>
<figure class="highlight mel"><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></pre></td><td class="code"><pre><div class="line">@Override</div><div class="line">    protected void onDraw(Canvas <span class="keyword">canvas</span>) &#123;</div><div class="line">        super.onDraw(<span class="keyword">canvas</span>);</div><div class="line">        <span class="comment">//将画布移动了  mWidth / 2, mHeight / 2 的距离</span></div><div class="line">        <span class="comment">//这一步使得原点坐标由屏幕左上角移动到了屏幕中心</span></div><div class="line">        <span class="keyword">canvas</span>.translate(mWidth / <span class="number">2</span>, mHeight / <span class="number">2</span>);</div><div class="line">        <span class="comment">//首先绘制最底层的圆弧</span></div><div class="line">        <span class="keyword">canvas</span>.drawArc(ArcRectF, mStartAngle, mSweepAngle, false, mBottomPaint);</div><div class="line"></div><div class="line">        <span class="comment">//当前进度圆弧，需要动态显示</span></div><div class="line">        <span class="keyword">canvas</span>.drawArc(ArcRectF, mStartAngle, <span class="keyword">percent</span> * mSweepAngle, false, mStepsPaint);</div><div class="line">     </div><div class="line">        <span class="comment">// 绘制实时步数文字，需要动态显示</span></div><div class="line">        <span class="keyword">canvas</span>.drawText(<span class="keyword">string</span>, dx, baseLine, mTextPaint);</div><div class="line">        <span class="comment">//绘制“步数”文字</span></div><div class="line">        <span class="keyword">canvas</span>.drawText(<span class="string">"步数"</span>, dx / <span class="number">2</span>, baseLine + getFontHeight(<span class="number">50</span>) * <span class="number">3</span>, mStepPaint);</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>以上就是四个元素的绘制，其中都是对坐标的计算，然后调用canvas来进行图形的绘制。</p>
<ul>
<li><p>最底下的弧形不用过多介绍，其中<code>ArcRectF = new RectF(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius);</code><br>是这个样子的。</p>
</li>
<li><p>由于最上面的圆弧需要动态显示，所以这里可以对外提供两个方法。来对当前的最大步数，以及当前的步数进行设置。</p>
</li>
</ul>
<figure class="highlight cpp"><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></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">setStpes</span><span class="params">(<span class="keyword">int</span> stpes)</span> </span>&#123;</div><div class="line">       <span class="keyword">this</span>.stpes = stpes;</div><div class="line">   &#125;</div><div class="line"></div><div class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setMaxStpes</span><span class="params">(<span class="keyword">int</span> maxStpes)</span> </span>&#123;</div><div class="line">       <span class="keyword">this</span>.maxStpes = maxStpes;</div><div class="line">   &#125;</div><div class="line"></div><div class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getStpes</span><span class="params">()</span> </span>&#123;</div><div class="line">       <span class="keyword">return</span> stpes;</div><div class="line">   &#125;</div><div class="line"></div><div class="line">   <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getMaxStpes</span><span class="params">()</span> </span>&#123;</div><div class="line">       <span class="keyword">return</span> maxStpes;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>之后，需要用到属性动画，我们仍然是暴露到外面。因为，只有调用者才知道该设置多少参数。</p>
<figure class="highlight aspectj"><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">public</span> <span class="function"><span class="keyword">void</span> <span class="title">setAnimation</span><span class="params">(<span class="keyword">float</span> last, <span class="keyword">float</span> current, <span class="keyword">int</span> length)</span> </span>&#123;</div><div class="line">       ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);</div><div class="line">       progressAnimator.setDuration(length);</div><div class="line">       progressAnimator.setTarget(currentAngleLength);</div><div class="line">       progressAnimator.addUpdateListener(<span class="keyword">new</span> ValueAnimator.AnimatorUpdateListener() &#123;</div><div class="line">           <span class="meta">@Override</span></div><div class="line">           <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onAnimationUpdate</span><span class="params">(ValueAnimator animation)</span> </span>&#123;</div><div class="line">               currentAngleLength = (<span class="keyword">float</span>) animation.getAnimatedValue();</div><div class="line">               invalidate();</div><div class="line">           &#125;</div><div class="line">       &#125;);</div><div class="line">       progressAnimator.start();</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>这里的<code>invalidate()</code>方法会不断的调用<code>ondraw()</code>方法，不断地重绘，达到整个动态效果。</p>
<p>所以，<code>percent = currentAngleLength / maxStpes;</code>就是当前的步数百分比。</p>
<ul>
<li>绘制当前的步数文字，这里也是动态显示的。<figure class="highlight arduino"><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"><span class="keyword">String</span> <span class="keyword">string</span> = (<span class="keyword">int</span>) currentAngleLength + <span class="string">""</span>;</div><div class="line"> <span class="comment">// 测量文字的宽高</span></div><div class="line"> Rect textBounds = <span class="keyword">new</span> Rect();</div><div class="line"> mTextPaint.getTextBounds(<span class="keyword">string</span>, <span class="number">0</span>, <span class="keyword">string</span>.length(), textBounds);</div><div class="line"> <span class="keyword">int</span> dx = -textBounds.<span class="built_in">width</span>() / <span class="number">2</span>;</div><div class="line"> <span class="comment">// 获取画笔的FontMetrics</span></div><div class="line"> Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();</div><div class="line"> <span class="comment">// 计算文字的基线</span></div><div class="line"> <span class="keyword">int</span> baseLine = (<span class="keyword">int</span>) ((fontMetrics.bottom - fontMetrics.top) / <span class="number">2</span> - fontMetrics.bottom);</div></pre></td></tr></table></figure>
</li>
</ul>
<p>currentAngleLength 是在属性动画中不断返回的进度值。转化成String类型后，就成字符串了，就可以用canvas绘制到屏幕上了。通过<code>mTextPaint.getTextBounds()</code>就可以完成对字符串的测量，然后得到width，由于坐标系原点在屏幕中心。所以这里这样处理<code>int dx = -textBounds.width() / 2;</code>。从dx开始绘制，绘制长度为string字符串的长度。然后借助 Paint.FontMetrics得到字符串的高度，这里之所以使用 Paint.FontMetrics是因为 Paint.FontMetrics中bottom是测量给定文本大小下字体中最低字形的最大距离，而top是在给定文本大小的字体中最高字形的基线上方的最大距离。而<code>getTextBounds()</code>返回的bottom以及top是不会这样的。所以使用了Paint.FontMetrics。这里baseLine的计算，其实大部分绘制文字时都需要这样计算，baseLine就是整个文本的基线，也就是整个文本的最底端。这里坐标系原点在屏幕中心，通过(fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom，就可以得到（文本高度/2）这么一个的正值。你可以自己计算一下。毕竟我的数学也不好╮(╯﹏╰）╭。。。之后<code>canvas.drawText(string, dx, baseLine, mTextPaint);</code>就可以了。为了让文本在正中心，由于坐标系在屏幕中心，我们必须让dx为负值，即文本的最左端，baseLine为正值。</p>
<ul>
<li>绘制“步数”</li>
</ul>
<p>这个相比于上一个就很简单了，上一步都已经计算出了所需绘制文字的左边位置以及底部基线。<br><figure class="highlight armasm"><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"> //绘制“步数”文字</div><div class="line">    canvas.drawText(<span class="string">"步数"</span>, dx / <span class="number">2</span>, <span class="keyword">baseLine </span>+ getFontHeight(<span class="number">50</span>) * <span class="number">3</span>, mStepPaint)<span class="comment">;</span></div><div class="line"></div><div class="line">    public int getFontHeight() &#123;</div><div class="line">//      Rect <span class="keyword">bounds_Number </span>= new Rect()<span class="comment">;</span></div><div class="line">//      <span class="keyword">String </span><span class="keyword">string </span>= <span class="string">"步数"</span><span class="comment">;</span></div><div class="line">//      mStepPaint.getTextBounds(<span class="keyword">string, </span><span class="number">0</span>, <span class="keyword">string.length(), </span><span class="keyword">bounds_Number);</span></div><div class="line">//      return (<span class="keyword">bounds_Number.bottom </span>- <span class="keyword">bounds_Number.top) </span>/ <span class="number">2</span> - <span class="keyword">bounds_Number.bottom;</span></div><div class="line">        Paint.FontMetrics fontMetrics = mStepPaint.getFontMetrics()<span class="comment">;</span></div><div class="line">        return (int) (fontMetrics.<span class="keyword">bottom </span>- fontMetrics.top)<span class="comment">;</span></div><div class="line">    &#125;</div></pre></td></tr></table></figure></p>
<p>这里应该可以看懂吧？dx/2，是由于“步数”只有两个字，大部分情况下是上面步数的1/2。而baseLine，你也只要计算出上个文字的baseLine+下面的文字高度就是“步数”的baseLine了。</p>
<h4 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h4><p>在布局中：</p>
<figure class="highlight vim"><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></pre></td><td class="code"><pre><div class="line">&lt;LinearLayout</div><div class="line">    xmln<span class="variable">s:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></div><div class="line">    xmln<span class="variable">s:kevin</span>=<span class="string">"http://schemas.android.com/apk/res-auto"</span></div><div class="line">    xmln<span class="variable">s:tools</span>=<span class="string">"http://schemas.android.com/tools"</span></div><div class="line">    android:layout_width=<span class="string">"match_parent"</span></div><div class="line">    android:layout_height=<span class="string">"match_parent"</span></div><div class="line">    tool<span class="variable">s:context</span>=<span class="string">"com.example.qqview.MainActivity"</span>&gt;</div><div class="line"></div><div class="line">   &lt;<span class="keyword">com</span>.example.qqview.StepsView</div><div class="line">       android:id=<span class="string">"@+id/step"</span></div><div class="line">       android:layout_width=<span class="string">"match_parent"</span></div><div class="line">       android:layout_height=<span class="string">"match_parent"</span></div><div class="line">       kevin:radius=<span class="string">"400px"</span></div><div class="line">       kevin:bottomPaintColor=<span class="string">"@color/colorPrimary"</span></div><div class="line">       kevin:stepsPaintColor=<span class="string">"@color/colorAccent"</span></div><div class="line">       kevin:startAngle=<span class="string">"120"</span></div><div class="line">       kevin:sweepAngle=<span class="string">"300"</span></div><div class="line">       /&gt;</div><div class="line"></div><div class="line">&lt;/LinearLayout&gt;</div></pre></td></tr></table></figure>
<p>注意需要使用自定义名空间：<code>xmlns:kevin=&quot;http://schemas.android.com/apk/res-auto&quot;</code></p>
<p>Activity中，只需要调用暴露出来的方法，即可完成：</p>
<figure class="highlight scala"><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></pre></td><td class="code"><pre><div class="line">public <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">    <span class="keyword">private</span> int steps = <span class="number">6000</span>;</div><div class="line">    <span class="keyword">private</span> int maxSteps = <span class="number">10000</span>;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">protected</span> void onCreate(<span class="type">Bundle</span> savedInstanceState) &#123;</div><div class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line">        setContentView(<span class="type">R</span>.layout.activity_main);</div><div class="line">        <span class="type">StepsView</span> stepsView= (<span class="type">StepsView</span>) findViewById(step);</div><div class="line">        stepsView.setMaxStpes(maxSteps);</div><div class="line">        stepsView.setStpes(steps);</div><div class="line">        stepsView.setAnimation(<span class="number">0</span>,steps,<span class="number">1500</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>好了，这样就完成了步数运动这个自定义View了。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;上一篇中介绍了如何实现自定义View的流程。那么，在这一篇中我们就动手打造一个简单的运动步数的自定义View。可以看一下gif图展示了本次的运动步数的效果。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://upload-images.jianshu.io/upload_images/1602023-d4107cb7567beee5.gif?imageMogr2/auto-orient/strip&quot; alt=&quot;运动步数&quot;&gt;&lt;/p&gt;
&lt;p&gt;这里的效果，我想大部分人都见过。下面来看一下具体的实现过程。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/categories/Android/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/tags/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
  </entry>
  
  <entry>
    <title>自定义View之基础部分</title>
    <link href="https://KevinJe.github.io/2017/08/20/%E8%87%AA%E5%AE%9A%E4%B9%89View%E4%B9%8B%E5%9F%BA%E7%A1%80%E9%83%A8%E5%88%86/"/>
    <id>https://KevinJe.github.io/2017/08/20/自定义View之基础部分/</id>
    <published>2017-08-20T11:44:02.000Z</published>
    <updated>2017-09-22T12:56:44.737Z</updated>
    
    <content type="html"><![CDATA[<p>最近，一直再看《Android群英传》，于是打算将最近的所经历的记录下来。</p>
<h2 id="View的测量"><a href="#View的测量" class="headerlink" title="View的测量"></a>View的测量</h2><p>想要实现自定义View的效果，我们首先需要进行的就是View的测量了。当然，这不是必须要去做的，只是大部分情况下会进行的一个操作。就像现实生活中，我们要去裁剪，也是需要先测量一下长和宽。在Android中通过继承View类，重写其<code>onMeasure(int widthMeasureSpec, int heightMeasureSpec)</code>方法。系统提供了一个<code>MeasureSpec</code>类，帮助我们进行View的测量。<code>MeasureSpec</code>是一个32位的int值，其中高两位为测量模式，低30位是测量的大小。这里，我们只需要关注一下测量的模式。</p>
<a id="more"></a>
<h3 id="测量模式"><a href="#测量模式" class="headerlink" title="测量模式"></a>测量模式</h3><ul>
<li>EXACTLY</li>
</ul>
<p>按照字义，即是精确测量模式。当我们在布局中写成<code>android:layout_width=&quot;match_parent&quot;
                                         android:layout_height=&quot;match_parent&quot;</code>这个样子或者直接<code>android:layout_width=&quot;300dp&quot;
                                         android:layout_height=&quot;300dp&quot;</code>。这时系统就会使用EXACTLY模式。</p>
<ul>
<li>AT_MOST</li>
</ul>
<p>最大值模式，当在布局中<code>android:layout_width=&quot;wrap_content” android:layout_height=&quot;wrap_content&quot;</code>。即包裹内容，控件的大小会随着控件内部内容的多少而变化。但是，只要这个尺寸不超过父控件允许的最大尺寸就可以了。</p>
<ul>
<li>UNSPECIFIED</li>
</ul>
<p>不确定模式，View想多大就多大。类似的系统中的ScrollView、ListView就是应用的这种模式。因为我们并不能确定你要向其中添加多少内容。</p>
<p>下面是<code>onMeasure</code>方法：</p>
<figure class="highlight aspectj"><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="meta">@Override</span></div><div class="line">   <span class="keyword">protected</span> <span class="function"><span class="keyword">void</span> <span class="title">onMeasure</span><span class="params">(<span class="keyword">int</span> widthMeasureSpec, <span class="keyword">int</span> heightMeasureSpec)</span> </span>&#123;</div><div class="line">       <span class="keyword">super</span>.onMeasure(widthMeasureSpec, heightMeasureSpec);</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>我们进到<code>onMeasure</code>方法里面会发现系统显示调用了：</p>
<figure class="highlight lisp"><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">setMeasuredDimension(<span class="name">getDefaultSize</span>(<span class="name">getSuggestedMinimumWidth</span>(), widthMeasureSpec),</div><div class="line">                getDefaultSize(<span class="name">getSuggestedMinimumHeight</span>(), heightMeasureSpec))<span class="comment">;</span></div></pre></td></tr></table></figure>
<p>再来看一下这个<code>getDefaultSize()</code>方法：</p>
<figure class="highlight arduino"><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="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> getDefaultSize(<span class="keyword">int</span> <span class="built_in">size</span>, <span class="keyword">int</span> measureSpec) &#123;</div><div class="line">       <span class="keyword">int</span> result = <span class="built_in">size</span>;</div><div class="line">       <span class="keyword">int</span> specMode = MeasureSpec.getMode(measureSpec);</div><div class="line">       <span class="keyword">int</span> specSize = MeasureSpec.getSize(measureSpec);</div><div class="line"></div><div class="line">       <span class="built_in">switch</span> (specMode) &#123;</div><div class="line">       <span class="built_in">case</span> MeasureSpec.UNSPECIFIED:</div><div class="line">           result = <span class="built_in">size</span>;</div><div class="line">           <span class="built_in">break</span>;</div><div class="line">       <span class="built_in">case</span> MeasureSpec.AT_MOST:</div><div class="line">       <span class="built_in">case</span> MeasureSpec.EXACTLY:</div><div class="line">           result = specSize;</div><div class="line">           <span class="built_in">break</span>;</div><div class="line">       &#125;</div><div class="line">       <span class="built_in">return</span> result;</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>看到了吧，假如我们没有重写<code>onMeasure</code>方法，系统会默认进行处理。我们平时布局宽高中用到的比较多的就是<code>match_parent</code>和<code>wrap_content</code>。也就是对应的<code>MeasureSpec.AT_MOST</code>和 <code>MeasureSpec.EXACTLY</code>。看到这里<code>MeasureSpec.AT_MOST</code>和 <code>MeasureSpec.EXACTLY</code>是一起处理的。所以，如果我们不进行重写<code>onMeasure</code>方法。那么，最终当我们布局宽高指定为<code>wrap_content</code>时就会出现默认填充整个屏幕的情况，也就是和<code>match_parent</code>一样了。所以，这里重写<code>onMeasure</code>方法就是为了让一个View在<code>wrap_content</code>属性下有一个默认的大小，而不是直接填充整个屏幕。</p>
<p>再往下面看会发现<code>setMeasuredDimension(int measuredWidth, int measuredHeight)</code>这一方法。看一下注释： </p>
<blockquote>
<p>This method must be called by onMeasure(int, int) to store the<br>      measured width and measured height</p>
</blockquote>
<p>所以，系统就是调用这一方法将测量的宽高值传进去，完成的测量工作。<br>在这里，给出一个模板代码：</p>
<figure class="highlight nimrod"><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></pre></td><td class="code"><pre><div class="line"> @<span class="type">Override</span></div><div class="line">    protected <span class="built_in">void</span> onMeasure(<span class="built_in">int</span> widthMeasureSpec, <span class="built_in">int</span> heightMeasureSpec) &#123;</div><div class="line">        setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    private <span class="built_in">int</span> measureWidth(<span class="built_in">int</span> widthMeasureSpec) &#123;</div><div class="line">        <span class="built_in">int</span> <span class="literal">result</span> = <span class="number">0</span>;</div><div class="line">        <span class="built_in">int</span> mode = <span class="type">MeasureSpec</span>.getMode(widthMeasureSpec);</div><div class="line">        <span class="built_in">int</span> size = <span class="type">MeasureSpec</span>.getSize(widthMeasureSpec);</div><div class="line">        <span class="keyword">if</span> (mode == <span class="type">MeasureSpec</span>.<span class="type">EXACTLY</span>) &#123;</div><div class="line">            <span class="literal">result</span> = size;</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">            <span class="literal">result</span> = <span class="number">200</span>;</div><div class="line">            <span class="keyword">if</span> (mode == <span class="type">MeasureSpec</span>.<span class="type">AT_MOST</span>) &#123;</div><div class="line">                <span class="literal">result</span> = <span class="type">Math</span>.min(<span class="literal">result</span>, size);</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> <span class="literal">result</span>;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    private <span class="built_in">int</span> measureHeight(<span class="built_in">int</span> heightMeasureSpec) &#123;</div><div class="line">        <span class="built_in">int</span> <span class="literal">result</span> = <span class="number">0</span>;</div><div class="line">        <span class="built_in">int</span> mode = <span class="type">MeasureSpec</span>.getMode(heightMeasureSpec);</div><div class="line">        <span class="built_in">int</span> size = <span class="type">MeasureSpec</span>.getSize(heightMeasureSpec);</div><div class="line">        <span class="keyword">if</span> (mode == <span class="type">MeasureSpec</span>.<span class="type">EXACTLY</span>) &#123;</div><div class="line">            <span class="literal">result</span> = size;</div><div class="line">        &#125; <span class="keyword">else</span> &#123;</div><div class="line">            <span class="literal">result</span> = <span class="number">200</span>;</div><div class="line">            <span class="keyword">if</span> (mode == <span class="type">MeasureSpec</span>.<span class="type">AT_MOST</span>) &#123;</div><div class="line">                <span class="literal">result</span> = <span class="type">Math</span>.min(<span class="literal">result</span>, size);</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line"></div><div class="line">        <span class="keyword">return</span> <span class="literal">result</span>;</div><div class="line">    &#125;</div><div class="line">`</div></pre></td></tr></table></figure>
<h2 id="View的绘制"><a href="#View的绘制" class="headerlink" title="View的绘制"></a>View的绘制</h2><p>现实中，我们想要进行绘画的话，是需要纸和笔的。同样在Android中，我们也需要纸和笔来进行绘制。这里我们只需要重写<code>onDraw(Canvas canvas)</code>方法。</p>
<h3 id="画布Canvas"><a href="#画布Canvas" class="headerlink" title="画布Canvas"></a>画布Canvas</h3><p>什么是Canvas呢？Canvas就是画布，也就相当于一张纸。Android中已经封装好了许多常用的绘图的API。只要在<code>onDraw(Canvas canvas)</code>中，利用<code>canvas.drawXXX()</code>就可以做出相应的形状了。比如：</p>
<figure class="highlight lsl"><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"><span class="comment">//画一个矩形，前四个参数是为了确定坐标位置，最后一个参数为画笔   </span></div><div class="line">canvas.drawRect(<span class="number">0</span>,<span class="number">0</span>,getMeasuredWidth(),getMeasuredHeight(),mPaint1);</div><div class="line"><span class="comment">//画一个圆</span></div><div class="line">canvas.drawCircle(<span class="number">0</span>, <span class="number">0</span>, (<span class="type">float</span>) (mRadius / <span class="number">1.5</span>), mCirclePaint);</div><div class="line"><span class="comment">//画一个弧</span></div><div class="line">canvas.drawArc(mArcRectF, <span class="number">270</span>, mSweepAngle, false, mArcPaint);</div><div class="line"><span class="comment">//绘制文字</span></div><div class="line">canvas.drawText(mShowText, <span class="number">0</span>, mShowText.length(), mCircleXY, mCircleXY + (mShowTextSize / <span class="number">4</span>)</div><div class="line">                , mTextPaint);</div></pre></td></tr></table></figure>
<p>当然，画布的作用远不止这些。还有，诸如，平移，旋转等功能……</p>
<h3 id="画笔Paint"><a href="#画笔Paint" class="headerlink" title="画笔Paint"></a>画笔Paint</h3><p>正如现实生活中没有笔肯定是不能绘画的。在自定义View中，我们需要用到Paint来绘制View。Paint有颜色，粗细，绘制形状等属性。<br><figure class="highlight pony"><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"><span class="comment">//新建一个画笔</span></div><div class="line">mArcPaint = <span class="function"><span class="keyword">new</span> <span class="title">Paint</span>();</span></div><div class="line"><span class="comment">//抗锯齿</span></div><div class="line"><span class="title">mArcPaint</span>.<span class="title">setAntiAlias</span>(true);</div><div class="line"><span class="comment">//画笔颜色       mArcPaint.setColor(getResources().getColor(android.R.color.holo_blue_bright));</span></div><div class="line"><span class="comment">//画笔宽度</span></div><div class="line"> <span class="title">mArcPaint</span>.<span class="title">setStrokeWidth</span>((float) (mRadius * <span class="number">0.1</span>));</div><div class="line"> <span class="comment">//画笔的风格，描边</span></div><div class="line"><span class="title">mArcPaint</span>.<span class="title">setStyle</span>(<span class="type">Paint</span>.<span class="type">Style</span>.<span class="type">STROKE</span>);</div></pre></td></tr></table></figure></p>
<h3 id="绘制"><a href="#绘制" class="headerlink" title="绘制"></a>绘制</h3><h4 id="继承系统控件，扩展属性"><a href="#继承系统控件，扩展属性" class="headerlink" title="继承系统控件，扩展属性"></a>继承系统控件，扩展属性</h4><p>这种就是系统控件缺少我们想要的某一个属性，我们就是可以继承系统的控件，重写相应的方法进行绘制。比如，可以重写<code>onDraw()</code>方法，进行UI的修改。</p>
<h4 id="继承View，实现新的控件"><a href="#继承View，实现新的控件" class="headerlink" title="继承View，实现新的控件"></a>继承View，实现新的控件</h4><p>这种就是Android中没有控件满足我们的需求时，就可以自己自定义View实现自己的效果。</p>
<h5 id="自定义属性"><a href="#自定义属性" class="headerlink" title="自定义属性"></a>自定义属性</h5><ul>
<li>在values文件夹下新建 attrs.xml 文件。在其中实现自己想要的属性代码。</li>
</ul>
<figure class="highlight sqf"><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">&lt;<span class="built_in">resources</span>&gt;</div><div class="line">    &lt;declare-styleable <span class="built_in">name</span>=<span class="string">"TopBar"</span>&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"title"</span> <span class="built_in">format</span>=<span class="string">"string"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"titleTextSize"</span> <span class="built_in">format</span>=<span class="string">"dimension"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"titleTextColor"</span> <span class="built_in">format</span>=<span class="string">"color"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"leftTextColor"</span> <span class="built_in">format</span>=<span class="string">"color"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"leftBackground"</span> <span class="built_in">format</span>=<span class="string">"color|reference"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"leftText"</span> <span class="built_in">format</span>=<span class="string">"string"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"rightTextColor"</span> <span class="built_in">format</span>=<span class="string">"color"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"rightBackground"</span> <span class="built_in">format</span>=<span class="string">"color|reference"</span>/&gt;</div><div class="line">        &lt;attr <span class="built_in">name</span>=<span class="string">"rightText"</span> <span class="built_in">format</span>=<span class="string">"string"</span>/&gt;</div><div class="line">    &lt;/declare-styleable&gt;</div><div class="line">&lt;/<span class="built_in">resources</span>&gt;</div></pre></td></tr></table></figure>
<p>name属性就是一个名字，相当于id。而format则是定义这个属性是什么类型的，string就是文字，dimension就是尺寸，reference引用（@drawable/xxx）。</p>
<h5 id="使用属性值"><a href="#使用属性值" class="headerlink" title="使用属性值"></a>使用属性值</h5><figure class="highlight stata"><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></pre></td><td class="code"><pre><div class="line">public TopBar(Context context) &#123;</div><div class="line">       this(context, null);</div><div class="line">   &#125;</div><div class="line"></div><div class="line">   public TopBar(Context context, AttributeSet attrs) &#123;</div><div class="line">       this(context, attrs, 0);</div><div class="line">   &#125;</div><div class="line"></div><div class="line">   public TopBar(Context context, AttributeSet attrs, int defStyleAttr) &#123;</div><div class="line">       super(context, attrs, defStyleAttr);</div><div class="line">       <span class="comment">// 通过这个方法，将你在atts.xml中定义的declare-styleable</span></div><div class="line">       <span class="comment">// 的所有属性的值存储到TypedArray中</span></div><div class="line">       TypedArray <span class="keyword">ta</span> = context.obtainStyledAttributes(attrs, R.styleable.TopBar);</div><div class="line">        mLeftTextColor = <span class="keyword">ta</span>.getColor(R.styleable.TopBar_leftTextColor, 0);</div><div class="line">       mLeftBackground = <span class="keyword">ta</span>.getDrawable(R.styleable.TopBar_leftBackground);</div><div class="line">       mLeftText = <span class="keyword">ta</span>.getString(R.styleable.TopBar_leftText);</div><div class="line">       <span class="keyword">Log</span>.<span class="built_in">d</span>(TAG, <span class="string">"TopBar: "</span> + mLeftText);</div><div class="line">       mRightTextColor = <span class="keyword">ta</span>.getColor(R.styleable.TopBar_rightTextColor, 0);</div><div class="line">       mRightBackground = <span class="keyword">ta</span>.getDrawable(R.styleable.TopBar_rightBackground);</div><div class="line">       mRightText = <span class="keyword">ta</span>.getString(R.styleable.TopBar_rightText);</div><div class="line">       mTitleTextSize = <span class="keyword">ta</span>.getDimension(R.styleable.TopBar_titleTextSize, 10);</div><div class="line">       mTitleTextColor = <span class="keyword">ta</span>.getColor(R.styleable.TopBar_titleTextColor, 0);</div><div class="line">       mTitle = <span class="keyword">ta</span>.getString(R.styleable.TopBar_title);</div><div class="line">       <span class="comment">// 获取完TypedArray的值后，一般要调用</span></div><div class="line">       <span class="comment">// recyle方法来避免重新创建的时候的错误</span></div><div class="line">       <span class="keyword">ta</span>.recycle();</div></pre></td></tr></table></figure>
<p>这里可以在第一个构造方法中调用第二个，第二个调用第三个。最后在第三个构造方法中执行<code>TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.TopBar);</code>。就获得了我们在attrs.xml中定义的属性的TypedArray，然后通过一系列的<code>ta.getXxx</code>方法，就可以得到我们的自定义属性。最后在获取完所有的属性值时一定要调用<code>ta.recycle();</code>方法。</p>
<h5 id="设计"><a href="#设计" class="headerlink" title="设计"></a>设计</h5><p>前面的操作已经准备好了工具，接下来就是设计阶段。也就是按照自己的想要的效果设计了。其实，再复杂的自定义View，只要一部分，一部分的绘制总能完成。而在绘制中，通常都是对坐标的计算了。只要我们坐标计算无误，总能实现自己的自定义View。当然，我也只是一个小白。。。</p>
<h5 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h5><p>最后，一切OK。我们就可以在布局中应用我们的自定义View了。<br><figure class="highlight nsis"><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">&lt;com.example.customviewtest.TopBar</div><div class="line">    xmlns:android=<span class="string">"http://schemas.android.com/apk/res/android"</span></div><div class="line">    xmlns:<span class="literal">custom</span>=<span class="string">"http://schemas.android.com/apk/res-auto"</span></div><div class="line">    android:id=<span class="string">"@+id/topbar"</span></div><div class="line">    android:layout_width=<span class="string">"match_parent"</span></div><div class="line">    android:layout_height=<span class="string">"100dp"</span></div><div class="line">    <span class="literal">custom</span>:leftBackground=<span class="string">"@drawable/blue_button"</span></div><div class="line">    <span class="literal">custom</span>:leftText=<span class="string">"Back"</span></div><div class="line">    <span class="literal">custom</span>:leftTextColor=<span class="string">"#FFFFFF"</span></div><div class="line">    <span class="literal">custom</span>:rightBackground=<span class="string">"@drawable/blue_button"</span></div><div class="line">    <span class="literal">custom</span>:rightText=<span class="string">"More"</span></div><div class="line">    <span class="literal">custom</span>:rightTextColor=<span class="string">"#FFFFFF"</span></div><div class="line">    <span class="literal">custom</span>:title=<span class="string">"自定义标题"</span></div><div class="line">    <span class="literal">custom</span>:titleTextColor=<span class="string">"#123412"</span></div><div class="line">    <span class="literal">custom</span>:titleTextSize=<span class="string">"15sp"</span></div><div class="line">    &gt;</div></pre></td></tr></table></figure></p>
<p>首先一定要写全路径<code>com.example.customviewtest.TopBar</code>。然后，最重要的是<code>xmlns:custom=&quot;http://schemas.android.com/apk/res-auto&quot;</code>。这里custom可以写我们想写的名空间，然后就可以引用我们的自定义的属性了。</p>
<h5 id="Enjoy-it"><a href="#Enjoy-it" class="headerlink" title="Enjoy it"></a>Enjoy it</h5><p>以上就是自定义View的大体流程。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;最近，一直再看《Android群英传》，于是打算将最近的所经历的记录下来。&lt;/p&gt;
&lt;h2 id=&quot;View的测量&quot;&gt;&lt;a href=&quot;#View的测量&quot; class=&quot;headerlink&quot; title=&quot;View的测量&quot;&gt;&lt;/a&gt;View的测量&lt;/h2&gt;&lt;p&gt;想要实现自定义View的效果，我们首先需要进行的就是View的测量了。当然，这不是必须要去做的，只是大部分情况下会进行的一个操作。就像现实生活中，我们要去裁剪，也是需要先测量一下长和宽。在Android中通过继承View类，重写其&lt;code&gt;onMeasure(int widthMeasureSpec, int heightMeasureSpec)&lt;/code&gt;方法。系统提供了一个&lt;code&gt;MeasureSpec&lt;/code&gt;类，帮助我们进行View的测量。&lt;code&gt;MeasureSpec&lt;/code&gt;是一个32位的int值，其中高两位为测量模式，低30位是测量的大小。这里，我们只需要关注一下测量的模式。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/categories/Android/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="自定义View" scheme="https://KevinJe.github.io/tags/%E8%87%AA%E5%AE%9A%E4%B9%89View/"/>
    
  </entry>
  
  <entry>
    <title>关于Android7.0FileProvider的使用</title>
    <link href="https://KevinJe.github.io/2017/07/27/%E5%85%B3%E4%BA%8EAndroid7-0FileProvider%E7%9A%84%E4%BD%BF%E7%94%A8/"/>
    <id>https://KevinJe.github.io/2017/07/27/关于Android7-0FileProvider的使用/</id>
    <published>2017-07-27T08:31:57.000Z</published>
    <updated>2017-09-22T12:57:31.607Z</updated>
    
    <content type="html"><![CDATA[<p>就在昨天，手机接收到Android7.0的推送，我也是第一时间进行了更新。体验了一番，应用的安装速度确实是得到了提高。然后，写了图片选择的小demo，发现再Android7.0上会出现问题。查了一下，发现是Android7.0的一个新特性，将解决办法记录一下。</p>
<a id="more"></a>
<p>这里模仿一下上传头像的过程，既可以拍照上传，也可以从图库中直接选择。然后就是一个裁切的动作。在Android7.0以前可以这样写：</p>
<h3 id="Android-7-0-以前"><a href="#Android-7-0-以前" class="headerlink" title="Android 7.0 以前"></a>Android 7.0 以前</h3><figure class="highlight aspectj"><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><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">void</span> <span class="title">takePhoto</span><span class="params">()</span> </span>&#123;</div><div class="line">       mOutputImage = <span class="keyword">new</span> File(Environment.getExternalStorageDirectory(), <span class="string">"/MyImage/"</span> + System.currentTimeMillis() + <span class="string">".jpg"</span>);</div><div class="line">       <span class="keyword">if</span> (!mOutputImage.getParentFile().exists()) &#123;</div><div class="line">           mOutputImage.getParentFile().mkdirs();</div><div class="line">       &#125;</div><div class="line">       imageUri = Uri.fromFile(mOutputImage);</div><div class="line">       Intent intent = <span class="keyword">new</span> Intent(MediaStore.ACTION_IMAGE_CAPTURE);</div><div class="line">       intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);</div><div class="line">       startActivityForResult(intent, TAKE_PHOTO);</div><div class="line">   &#125;</div><div class="line"></div><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">void</span> <span class="title">choosePhoto</span><span class="params">()</span> </span>&#123;</div><div class="line">       <span class="keyword">if</span> (ContextCompat.checkSelfPermission(MainActivity.<span class="keyword">this</span>, Manifest.permission</div><div class="line">               .WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) &#123;</div><div class="line">           ActivityCompat.requestPermissions(MainActivity.<span class="keyword">this</span>, <span class="keyword">new</span> String[]&#123;</div><div class="line">                   Manifest.permission.WRITE_EXTERNAL_STORAGE</div><div class="line">           &#125;, <span class="number">1</span>);</div><div class="line">       &#125; <span class="keyword">else</span> &#123;</div><div class="line">           openAlbum();</div><div class="line">       &#125;</div><div class="line">   &#125;</div><div class="line"></div><div class="line"><span class="keyword">private</span> <span class="function"><span class="keyword">void</span> <span class="title">openAlbum</span><span class="params">()</span> </span>&#123;</div><div class="line">       Intent intent = <span class="keyword">new</span> Intent(Intent.ACTION_PICK);</div><div class="line">       intent.setType(<span class="string">"image/*"</span>);</div><div class="line">       startActivityForResult(intent, CHOOSE_PHOTO);</div><div class="line">   &#125;</div><div class="line"></div><div class="line"><span class="meta">@Override</span></div><div class="line">   <span class="keyword">protected</span> <span class="function"><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">       <span class="keyword">switch</span> (requestCode) &#123;</div><div class="line">           <span class="keyword">case</span> TAKE_PHOTO:</div><div class="line">               <span class="keyword">if</span> (resultCode == RESULT_OK) &#123;</div><div class="line">                   cropImage(imageUri);</div><div class="line">               &#125;</div><div class="line">               <span class="keyword">break</span>;</div><div class="line">           <span class="keyword">case</span> CHOOSE_PHOTO:</div><div class="line">               <span class="keyword">if</span> (resultCode == RESULT_OK) &#123;</div><div class="line">                   imageUri = data.getData();</div><div class="line">                   cropImage(imageUri);</div><div class="line">               &#125;</div><div class="line">               <span class="keyword">break</span>;</div><div class="line">           <span class="keyword">case</span> CROP_PHOTO:</div><div class="line">               pictrue.setImageURI(imageUri);</div><div class="line">               <span class="keyword">break</span>;</div><div class="line">           <span class="keyword">default</span>:</div><div class="line">       &#125;</div><div class="line">   &#125;</div><div class="line"></div><div class="line"><span class="keyword">private</span> <span class="keyword">void</span> cropImage(Uri uri) &#123;</div><div class="line">       Intent intent = <span class="keyword">new</span> Intent(<span class="string">"com.android.camera.action.CROP"</span>);</div><div class="line">       intent.setDataAndType(uri, <span class="string">"image/*"</span>);</div><div class="line">       intent.putExtra(<span class="string">"crop"</span>, <span class="string">"true"</span>);</div><div class="line">       intent.putExtra(<span class="string">"aspectX"</span>, <span class="number">1</span>);</div><div class="line">       intent.putExtra(<span class="string">"aspectY"</span>, <span class="number">1</span>);</div><div class="line">       intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);</div><div class="line">       startActivityForResult(intent, CROP_PHOTO);</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>上面choosePhoto()中进行了运行时权限的处理，以防止在Android6.0上出现问题。然后一切很简单了，就是调用系统的相机以及相册，然后剪切，最后将剪切后的图片设置到一个ImageView中。在Android7.0以前上面的代码是没有问题的。但是，如果将上述代码运行在7.0以上的设备中就会报错了。</p>
<p>报出了 <strong>FileUriExposedException</strong>异常。后来一查，发现在Android7.0，Android 框架执行的 StrictMode API 政策禁止在您的应用外部公开 file:// URI。如果一项包含文件 URI 的 intent 离开您的应用，则应用出现故障，并出现 FileUriExposedException 异常。所以，上述异常的问题在于我们使用的URI是 file:// URI类型的从而导致错误。在7.0上解决的方法是应用FileProvider。FileProvider，就是 ContentProvider 的一个特殊子类，帮助我们将访问受限的 file:// URI 转化为可以授权共享的 content:// URI。</p>
<h3 id="Android-7-0"><a href="#Android-7-0" class="headerlink" title="Android 7.0"></a>Android 7.0</h3><h4 id="一-注册provider"><a href="#一-注册provider" class="headerlink" title="一.注册provider"></a>一.注册provider</h4><p>既然FileProvider是ContentProvider的子类，所以它也是需要注册的。</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></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">provider</span></span></div><div class="line">           <span class="attr">android:name</span>=<span class="string">"android.support.v4.content.FileProvider"</span></div><div class="line">           <span class="attr">android:authorities</span>=<span class="string">"com.example.cameraalbumtest.fileprovider"</span></div><div class="line">           <span class="attr">android:exported</span>=<span class="string">"false"</span></div><div class="line">           <span class="attr">android:grantUriPermissions</span>=<span class="string">"true"</span>&gt;</div><div class="line">           <span class="tag">&lt;<span class="name">meta-data</span></span></div><div class="line">               <span class="attr">android:name</span>=<span class="string">"android.support.FILE_PROVIDER_PATHS"</span></div><div class="line">               <span class="attr">android:resource</span>=<span class="string">"@xml/file_paths"</span>/&gt;</div><div class="line">       <span class="tag">&lt;/<span class="name">provider</span>&gt;</span></div></pre></td></tr></table></figure>
<p>这里，name的写法是固定的，authorities大都是包名后接上fileprovider。可以不写死，而是动态应用<code>${applicationId}.fileprovider</code>。exported为false与grantUriPermissions为true，这都是固定的。之后<meta-data>标签中的name也是固定写法。resource引用了xml下的文件。</meta-data></p>
<h4 id="二-添加引用文件"><a href="#二-添加引用文件" class="headerlink" title="二.添加引用文件"></a>二.添加引用文件</h4><p>上边引用了file_path文件，需要新建一个xml文件夹，在文件夹下新建一个file_path文件。</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></pre></td><td class="code"><pre><div class="line"><span class="php"><span class="meta">&lt;?</span>xml version=<span class="string">"1.0"</span> encoding=<span class="string">"utf-8"</span><span class="meta">?&gt;</span></span></div><div class="line"><span class="tag">&lt;<span class="name">paths</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span>&gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">external-path</span></span></div><div class="line">        <span class="attr">name</span>=<span class="string">"my_images"</span></div><div class="line">        <span class="attr">path</span>=<span class="string">""</span>/&gt;</div><div class="line"></div><div class="line"><span class="tag">&lt;/<span class="name">paths</span>&gt;</span></div></pre></td></tr></table></figure>
<p>这里<external-path>为URI共享的路径，name值紧随其后，path值为共享具体的目录，什么都不写则共享整个外部存储空间。</external-path></p>
<ul>
<li><p><files-path>：内部存储空间应用私有目录下的 files/ 目录，等同于 Context.getFilesDir() 所获取的目录路径；</files-path></p>
</li>
<li><p><cache-path>：内部存储空间应用私有目录下的 cache/ 目录，等同于 Context.getCacheDir() 所获取的目录路径；</cache-path></p>
</li>
<li><p><external-path>：外部存储空间根目录，等同于 Environment.getExternalStorageDirectory() 所获取的目录路径；</external-path></p>
</li>
<li><p><external-files-path>：外部存储空间应用私有目录下的 files/ 目录，等同于 Context.getExternalFilesDir(null) 所获取的目录路径；</external-files-path></p>
</li>
<li><p><external-cache-path>：外部存储空间应用私有目录下的 cache/ 目录，等同于 Context.getExternalCacheDir()；</external-cache-path></p>
</li>
</ul>
<h4 id="三-使用FileProvider"><a href="#三-使用FileProvider" class="headerlink" title="三.使用FileProvider"></a>三.使用FileProvider</h4><figure class="highlight arduino"><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></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">void</span> takePhoto() &#123;</div><div class="line">       mOutputImage = <span class="keyword">new</span> <span class="built_in">File</span>(Environment.getExternalStorageDirectory(), <span class="string">"/MyImage/"</span> + System.currentTimeMillis() + <span class="string">".jpg"</span>);</div><div class="line">       <span class="built_in">if</span> (!mOutputImage.getParentFile().<span class="built_in">exists</span>()) &#123;</div><div class="line">           mOutputImage.getParentFile().mkdirs();</div><div class="line">       &#125;</div><div class="line">       <span class="built_in">if</span> (Build.VERSION.SDK_INT &gt;= <span class="number">24</span>) &#123;</div><div class="line">           imageUri = FileProvider.getUriForFile(MainActivity.<span class="keyword">this</span>,</div><div class="line">                   <span class="string">"com.example.cameraalbumtest.fileprovider"</span>, mOutputImage);</div><div class="line">           Log.d(TAG, <span class="string">"uri "</span> + imageUri);</div><div class="line">           Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"Android 7.0"</span>, Toast.LENGTH_SHORT).show();</div><div class="line">       &#125; <span class="built_in">else</span> &#123;</div><div class="line">           imageUri = Uri.fromFile(mOutputImage);</div><div class="line">       &#125;</div><div class="line">       Intent intent = <span class="keyword">new</span> Intent(MediaStore.ACTION_IMAGE_CAPTURE);</div><div class="line">       intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);</div><div class="line">       startActivityForResult(intent, TAKE_PHOTO);</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>这里做了一下判断，在7.0上才使用FileProvider，以下还是使用原来的方法。看一下具体的使用，只是用FileProvider.getUriForFile()方法代替了Uri.fromfile()。这里的参数分别是Context，authorities，file，这里的authorities就是我们刚刚注册provider时的那个authorities。这样就将一个File://URI装换成一个Content://URI。这里看一下Log，生成的uri是下面的样子。<br><code>content://com.example.cameraalbumtest.fileprovider/my_images/MyImage/1501138971453.jpg</code></p>
<p>可以看出前一部分是一个authorities，后面紧接着name值（在path_file中设置的，name值代替真实的path值）。然后后面接着图片的具体目录。那看一下原先的file：URI：<br><code>file:///storage/emulated/0/MyImage/1501140051699.jpg</code><br>两者对比发现，FileProvider很好的隐藏掉了图片的存储位置，增加了安全性。</p>
<p>这里就介绍完了FileProvider的基本使用。但是运行上面代码在7.0设备上还是会出错，这次出错在剪切逻辑上。会发现拍照完成之后并不能进行剪切，而从图库中选择的图片就可以剪切。解决方法如下：</p>
<figure class="highlight mipsasm"><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">private void cropImage(Uri uri) &#123;</div><div class="line">        Intent intent = new Intent(<span class="string">"com.android.camera.action.CROP"</span>)<span class="comment">;</span></div><div class="line">        intent.setDataAndType(uri, <span class="string">"image/*"</span>)<span class="comment">;</span></div><div class="line">        intent.putExtra(<span class="string">"crop"</span>, <span class="string">"true"</span>)<span class="comment">;</span></div><div class="line">        intent.putExtra(<span class="string">"aspectX"</span>, <span class="number">1</span>)<span class="comment">;</span></div><div class="line">        intent.putExtra(<span class="string">"aspectY"</span>, <span class="number">1</span>)<span class="comment">;</span></div><div class="line">        intent.putExtra(MediaStore.<span class="keyword">EXTRA_OUTPUT, </span>imageUri)<span class="comment">;</span></div><div class="line">        intent.<span class="keyword">addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);</span></div><div class="line">        intent.<span class="keyword">addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);</span></div><div class="line">        startActivityForResult(intent, CROP_PHOTO)<span class="comment">;</span></div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<p>之所以加上addFlags()是为了授予其他应用访问权限。<br>授权方式有两种：</p>
<p>第一种方式，使用 Context 提供的 grantUriPermission(package, Uri, mode_flags) 方法向其他应用授权访问 URI 对象。三个参数分别表示授权访问 URI 对象的其他应用包名，授权访问的 Uri 对象，和授权类型。其中，授权类型为 Intent 类提供的读写类型常量：</p>
<p>FLAG_GRANT_READ_URI_PERMISSION</p>
<p>FLAG_GRANT_WRITE_URI_PERMISSION</p>
<p>或者二者同时授权。这种形式的授权方式，权限有效期截止至发生设备重启或者手动调用 revokeUriPermission() 方法撤销授权时。</p>
<p>第二种方式，配合 Intent 使用。通过 setData() 方法向 intent 对象添加 Content URI。然后使用 setFlags() 或者 addFlags() 方法设置读写权限，可选常量值同上。这种形式的授权方式，权限有效期截止至其它应用所处的堆栈销毁，并且一旦授权给某一个组件后，该应用的其它组件拥有相同的访问权限。</p>
<p>拥有授予权限的 Content URI 后，便可以通过 startActivity() 或者 setResult() 方法启动其他应用并传递授权过的 Content URI 数据。</p>
<p>鸿洋大神的这一篇可以看一看，对Android7.0的适配给出了解决办法。</p>
<p> <a href="http://blog.csdn.net/lmj623565791/article/details/72859156" target="_blank" rel="external">Android 7.0 行为变更 通过FileProvider在应用间共享文件吧</a></p>
<p>参考链接：<br><a href="http://yifeng.studio/2017/05/03/android-7-0-compat-fileprovider/" target="_blank" rel="external">关于 Android 7.0 适配中 FileProvider 部分的总结</a></p>
<p><a href="http://www.devio.org/2016/09/28/Android7.0%E9%80%82%E9%85%8D%E5%BF%83%E5%BE%97/" target="_blank" rel="external">Android7.0适配心得</a></p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;就在昨天，手机接收到Android7.0的推送，我也是第一时间进行了更新。体验了一番，应用的安装速度确实是得到了提高。然后，写了图片选择的小demo，发现再Android7.0上会出现问题。查了一下，发现是Android7.0的一个新特性，将解决办法记录一下。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="FileProvider" scheme="https://KevinJe.github.io/tags/FileProvider/"/>
    
  </entry>
  
  <entry>
    <title>怀梦想，致远方！</title>
    <link href="https://KevinJe.github.io/2017/06/06/%E6%80%80%E6%A2%A6%E6%83%B3%EF%BC%8C%E8%87%B4%E8%BF%9C%E6%96%B9%EF%BC%81/"/>
    <id>https://KevinJe.github.io/2017/06/06/怀梦想，致远方！/</id>
    <published>2017-06-06T13:10:43.000Z</published>
    <updated>2017-09-22T12:57:22.979Z</updated>
    
    <content type="html"><![CDATA[<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width="330" height="86" src="//music.163.com/outchain/player?type=2&id=26361010&auto=1&height=66"></iframe>


<p><img src="http://upload-images.jianshu.io/upload_images/1602023-83eb730ffd8e7e4d.jpg?imageMogr2/auto-orient/strip%7CimageView2/1/w/500" alt="怀梦想，致远方！"></p>
<p>前几天，随机播放到了这首《光阴的故事》。一想，才知道好长时间没有听过这首歌曲了，一直在我的歌单中沉睡。然后，又泛起一阵回忆。是啊，又到一年高考时，内心久久不能平静。初听这首《光阴的故事》是在《中国合伙人》这部电影。当然，这首歌曲是改遍的，原唱是罗大佑。记得那时还是在高中，看到的这部电影，主题曲就是《光阴的故事》。演唱的就是三个主角，黄晓明、邓超、佟大为。这部电影非常的不错，看的时候也是热血澎湃。想象着，自己什么时候也能达到那种高度。那种年轻人的朝气，那一批有梦想、懂得上进、努力又勤奋的年轻人，深深地打动了我。当时看，真的有心灵震撼的感觉。那时候，思想还是挺单一的，那还是一段青春的回忆。时间真快啊，一年又一年。距离那年，也早已经过去了四五年了，真是一梦匆匆四五年。</p>
<a id="more"></a>
<p>昨天，我姐姐给我打电话，问我放不放假。我便问她什么假啊。她说，高考，你们不放假吗？她这么一问，高考放假早都是过去式了。如今，再也没有机会了。要是能回去，就好了，我自己是怎么想的。只是，岁月不饶人，也没有故事中会有的时空穿梭机。时间真的是一把杀猪刀，无情地在脸上刻上叫做岁月的皱纹，然后，就这么老了。</p>
<p>明天，又是一年一度的高考。高考对于自己来说，还是挺有意义的，值得回忆的。今天，看到俞敏洪也在说虽然自己参加了三次高考最后上了北京大学，但自己并不仇恨高考，自己喜爱高考。毕竟，曾经在最美好的年纪里为了一件事心无旁骛，幻想着有一天踏入大学的校园。那也是青春中值得纪念的事情啊。如今，当初的那些小小的梦想早已经实现。我也早就已经踏入了大学的校园。可是，再也找不回那段青葱的岁月，那段执着的青春插曲。那段时光，就这样随着高考的结束而终结。我曾想着快快长大，拥抱外面的世界。如今，一眨眼，二十几岁了。依然，迷茫如少年，彷徨如当年。</p>
<p>从来到大学到现在可能真的没有什么进步吧。唯一在进步的可能也只有年纪在一直增长。眼看又一年的高考在即，又一年过去了。自己总想追随时间的脚步，可是无法跟随。一年又一年，我们在这一年又一年中渐渐地长大了。想的事情也多了，自己的责任也大了，承担的也多了。才发现，长大还真不一定时间好事情。曾经，自己那样的渴望长大，现在却有些怕了。但是，生活仍在继续。时间告诉我跟住它的脚步，向前再向前。</p>
<p>那一群即将参加高考的学子，你们真的还在最好的年纪。我真的很羡慕。加油吧，少年。不论最后的结果如何，我们都曾经在最美好的年纪为了一件事情而努力。自己只要问心无愧，知足常乐，这便是最好的结果。奔跑吧，骄傲的少年，年轻的心里面是对梦想的执着。</p>
<p>还记得，零几年那时候还是功能机大行其道。我也整天泡在那种论坛之中。那时候，也遇见了很多人，他们都会自己改软件。当然，也是有很多高三的学子。我还记得，那时候他们自己改出来的手Q的样子。我也还记得，在手Q的登陆界面，点击登陆后，出现的那八个字。最后，以这八个字，送给所有即将参加高考的学子，也就算是自己对那段青春时光的回忆。</p>
<blockquote>
<p><strong>放飞梦想，学子加油</strong></p>
</blockquote>
]]></content>
    
    <summary type="html">
    
      &lt;iframe frameborder=&quot;no&quot; border=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; width=330 height=86 src=&quot;//music.163.com/outchain/player?type=2&amp;id=26361010&amp;auto=1&amp;height=66&quot;&gt;&lt;/iframe&gt;


&lt;p&gt;&lt;img src=&quot;http://upload-images.jianshu.io/upload_images/1602023-83eb730ffd8e7e4d.jpg?imageMogr2/auto-orient/strip%7CimageView2/1/w/500&quot; alt=&quot;怀梦想，致远方！&quot;&gt;&lt;/p&gt;
&lt;p&gt;前几天，随机播放到了这首《光阴的故事》。一想，才知道好长时间没有听过这首歌曲了，一直在我的歌单中沉睡。然后，又泛起一阵回忆。是啊，又到一年高考时，内心久久不能平静。初听这首《光阴的故事》是在《中国合伙人》这部电影。当然，这首歌曲是改遍的，原唱是罗大佑。记得那时还是在高中，看到的这部电影，主题曲就是《光阴的故事》。演唱的就是三个主角，黄晓明、邓超、佟大为。这部电影非常的不错，看的时候也是热血澎湃。想象着，自己什么时候也能达到那种高度。那种年轻人的朝气，那一批有梦想、懂得上进、努力又勤奋的年轻人，深深地打动了我。当时看，真的有心灵震撼的感觉。那时候，思想还是挺单一的，那还是一段青春的回忆。时间真快啊，一年又一年。距离那年，也早已经过去了四五年了，真是一梦匆匆四五年。&lt;/p&gt;
    
    </summary>
    
      <category term="日记" scheme="https://KevinJe.github.io/categories/%E6%97%A5%E8%AE%B0/"/>
    
    
      <category term="随笔" scheme="https://KevinJe.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
    
      <category term="生活" scheme="https://KevinJe.github.io/tags/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="感悟" scheme="https://KevinJe.github.io/tags/%E6%84%9F%E6%82%9F/"/>
    
      <category term="高考" scheme="https://KevinJe.github.io/tags/%E9%AB%98%E8%80%83/"/>
    
      <category term="青春" scheme="https://KevinJe.github.io/tags/%E9%9D%92%E6%98%A5/"/>
    
  </entry>
  
  <entry>
    <title>如何获得精彩人生</title>
    <link href="https://KevinJe.github.io/2017/06/04/%E5%A6%82%E4%BD%95%E8%8E%B7%E5%BE%97%E7%B2%BE%E5%BD%A9%E4%BA%BA%E7%94%9F/"/>
    <id>https://KevinJe.github.io/2017/06/04/如何获得精彩人生/</id>
    <published>2017-06-04T12:35:05.000Z</published>
    <updated>2017-09-22T12:57:01.319Z</updated>
    
    <content type="html"><![CDATA[<p><a href="https://www.youtube.com/watch?v=jY1eNlL6NKs" target="_blank" rel="external">Charlie Munger USC Law Commencement Speech - May 2007</a></p>
<pre><code>chapter 1
</code></pre><p>这几天，我反复在看查理芒格在南加州大学的演讲。看得我就有些难受，可能有些人不认识查理。他其实就是投资大师沃伦巴菲特的合伙人，他们和巴菲特共同管理伯克希尔哈撒韦公司。伯克希尔哈撒韦，我们都知道，是人类迄今为止在大额长期投资方面最成功的公司。几十年间市值涨了13000多倍，现在伯克希尔的股票一股已经达到了250000美元。其实，我对名人一般不感冒，除非是我特别特别崇拜的。因为，就像传记一样，我认为大多数传记都没什么实话。都是出名了之后为自己洗白嘛，编造一些传奇的经历。其实，这也是在激励大众，但是我认为这就是忽悠群众的东西。但我觉得像查理这么大年纪的人，也不会再说什么大话。这篇演讲是在2007年5月23日在南加州大学法学院毕业典礼上发表的，当时他已经是84岁的高龄了。这么算下来，他今年都已经93岁了。就在前几天的5月6日，他竟然还是和巴菲特一起出席了伯克希尔的股东大会，那可是一个93岁的老人了。而且，巴菲特也已经有86岁了。所以，这有可能是他主持的最后一次股东大会。像他们这种人真的是一个成功的人，又有钱，身体又好。像我们，可能到了80、90岁，神智都有可能不清醒了。但人家，还是那么的睿智，那么精力充沛。</p>
<a id="more"></a>
<p>这几天我就一直在看他的这次演讲，我也不知道看了多少遍。反正，只要我一闲下来，就会翻出他的演讲。他的演讲，其实没什么大道理。一点也不像我们这里的成功人士讲的那些大道理。他讲的都是一些与我们生活相关的小事，以及他的人生态度。我就越看就越觉得有道理，就是感觉对自己太有启发了。就像他自己说的那样，未必对每个人而言都是完美的，但它们中许多是具备普遍的价值。比如，对待生活中的得到与失去。他的态度是：</p>
<blockquote>
<p>要想得到你想要的某样东西，最可靠的办法是让你自己配得上它。</p>
</blockquote>
<p>这是一个十分简单的道理，是黄金准则。可是，现实生活中，这种事情往往很少发生。我们大部分人，都不会去思考这类事情的。我们考虑的更多的可能就是如何让食物配得上我的嘴巴。所以，我们成为也没有可能成为什么富人，我们和人家的差距真的不光光是在智力、情商上，思维上也是差了不止多少倍吧。</p>
<p>还有，他指出我们每一个人都有一种自我服务的偏好。就是我们认为自己有资格去做它想做的事情。也就是说，我们往往认为是对的时候，不一定是对的。但我们往往还是会相信自己去做它，这对于大部分人而言，都是适用的，这可能是人类的一种普世的缺陷。就是我们总是过于的相信自己。包括我也在内，有时候我就觉得自己是对的。造成的后果，只能是两个极端，要么非常正确，很乐观。要么，完全的错误，只能以悲观收尾。他举了一个例子来说明这件事情：</p>
<blockquote>
<p>从前有一个人，他是全世界最著名的作曲家，可是他的大部分时间都过的非常的悲惨，原因之一就是他总是透支他的收入。哪位作曲家叫做莫扎特。连莫扎特都无法摆脱这种行为的毒害，我觉得你们更不应该去尝试它。</p>
</blockquote>
<p>确实是这样，不光是莫扎特。像大仲马，写了《三个火枪手》，也是一个非常有名的作家。但这种人，赚得多，花的也多啊。大仲马真是风流之极。情妇无数，私生子也无数，小仲马也是其私生子，直到小仲马七岁时，大仲马才将之认领。大仲马原本生性豪爽不羁，在成名后就变本加厉，非常的奢靡，他一掷千金，经常游历四方，足迹遍及整个欧洲。他不惜花费巨资二十万法郎，在巴黎附近的圣日尔曼昂莱森林里盖了一座新哥特式的“基督山城堡”，在那里款待朋友和情妇们，举行盛宴和舞会。反正就是建造了好多城堡，花钱就像流水。最后就是负债累累，他的晚年非常的贫困。为了抵债，就卖城堡。后来就去他的私生子小仲马那里，对小仲马说“我来你这里就是等死的”。半个月之后，他就去世了，结果遗产只有几块钱，非常的穷困潦倒。</p>
<p>你像我们现实生活也是这样，总会有人去透支信用卡。你用的时候是挺方便的，可是你想过后果吗？你像支付宝好像有一个什么“蚂蚁花呗”，好像也是类似信用卡的机制，这个月用完下个月还。反正我是没有开通，连看都没有看。我就觉得，像这种借贷的生活我是过不下去的，会有一种负罪感。现在社会新闻上也经常报道的，就是那种校园贷。有的学生就是爱慕虚荣或者贪小便宜，就去贷款。结果花几千块钱买部手机，到最后还十几万，非常的悲惨。</p>
<p>芒格，就讲这种小道理。没看之前，我们可能在思维里有这种概念，但是绝对没有上升到生活的智慧这一个层次上。你像他这种人，可能想不富有都难。人家，就是能够看清生活，看到人性的弱点。</p>
<p>他对于情感方面的态度是：</p>
<blockquote>
<p>　嫉妒、怨憎、仇恨和自怜都是灾难性的思想状态。过度自怜可以让人近乎偏执，偏执是最难你转的东西之一，你们不要陷入自怜的情绪中。</p>
</blockquote>
<p>对啊，你像我们每天在生活中。总是会抱怨这个、那个，到最后，还不是什么都改变不了。我们总是倾向于为自我服务，陷入自怜之中。然后，对别人讲那些自怜的故事。最后的结果就是，两个人都陷入自怜之中。这就是一种负面的情绪，我们在生活中遇见的太多了。每天都有那么多的负面情绪，所以聪明的人会想办法去消除负面思想。因为负面情绪，对我们的影响是很大的，也许你觉得很不经意。但是，积累多了，是会生病的，就像抑郁症一样。都是一个积累的过程。所以，我们在生活中能做到的就是消除负面的情绪，不要让它影响到自己。前几天看到一篇文章，看完之后，感觉收获挺大的。那篇文章大概讲：</p>
<blockquote>
<p>你要知道，生活中所发生的事情99%都与你无关，只有那1%的事情与你有关。所以，你只需要去做好那属于你的那1%。如果有一天，你意识到了这一点，就说明你已经成熟了。</p>
</blockquote>
<p>看完之后，就是感觉特别的有道理。虽然，我意识到了这一点，但是也一直没能做好自己那1%的事情。所以，我必须要继续的修炼。你像，芒格讲的也是这个道理。我们要通过训练去摆脱那些自怜的思想，那些负面的情绪。当然，我们也要摆脱掉自我服务偏好的习惯。因为我发现，现实生活中其实有挺多你认为简单的事情，往往别人认为不简单。就像，随手关门、随手关水龙头。我认为，这都是一种习惯的养成。这没有什么不好的。但是，在现实中，可能有很多的不近人意。你认为好的，但是其他人并不这样认为。这是很正常的事情了，我现在都已经习惯了。你像以前的我，可能在我认为正确的情况下，有些比较熟的人做了一些不怎么正确的事情。我还会去劝说一下，但是现在我真的不会了。因为我发现，现在的人的脾气都很大，你认为的正确和人家不一样，搞得别人也很烦。几年前，可能我还真是头脑简单。现在想想，以前做的许多事情真的很蠢。那时候可能真的是年轻气盛。现在，我基本不会再和别人说他哪里做的不好了。就算你说了，也没什么用，别人也不会改变什么。反倒是自己还以为自己跟个救世英雄似得，其实在别人眼中就是一个SB。还装的那么高尚，就是个Low B。</p>
<p>所以，我现在就是做好自己能做的，坚持自己认为正确的，虽然坚持的不一定正确。但我认为这都是对我品质的提升有好处的。像我们学校都是公用的卫生间。公用的东西，你要知道是没人爱护的。这一点，在我的印象中就没好过，尽管我们这里还是很看重公有财产的。公用的卫生间，冲水的装置，用的多了，弹性也不怎么好了。你一脚踩下去，它有可能就不弹回来了，就造成一直在那里流水。我发现好多人不会管这种事情的，别人都觉得这种事情与我无关。也没什么利益，没人会去管的。反正，我每次碰到这种情况，我都是会用手把冲水的装置拽回来，尽管不是我弄的。它弹性不足了，用脚是不行的。你必须用手把它拽回来，就不会再流水了。你像这种事情，很小的事情，都没人去做，更别说去告诉别人你做的不正确了。所以，你认为很小的事情，再别人眼中就不是。也不是，你认为正确的别人就认为正确。所以，我有时候，只能自己对自己说一说，现在的高等教育到底教会了什么。我也只能自己对自己苦笑，摇一摇头，只能自己感慨一下，其他的也做不了什么了。我也不知道其他院校怎么样，但我觉得这个东西，也是有普适性的。我也从不认为自己有多么的高尚，我只是做了一点自己能做的事情。而且，我有认为这是对自己品质的一点提升，我来到这里，知识是带不走了。那么，我总要带走一些什么，我还是希望自己有一些好的品质与素养。反正，我认为，最终决定你高低的古根到底还是品质。尽管，有些人也能成，就像咱们这边的那些土豪了、富二代了。但是在我眼里他也只是个土豪，你像芒格、巴菲特那样的才是真正的富人。不止财富多，思想也丰富，这才是真正的富有。</p>
<p>另外，他还在演讲中提到的关于学习方面的。他就说，你要想取得很高的成就，就要学会终身学习。不然，不会取得很高的成就。他就说，如果你拿着始终观察巴菲特，会发现他醒着的时候，有大半的时间是在看书，剩下的时间用来和一些非常有才干的进行交谈。他说，巴菲特就是一个学习机器。所以，伯克希尔哈撒韦的辉煌一点也不是什么奇迹。而是，人家真的在思考、在钻研。而不是在胡乱的投资。你像我们做事情，可能有的时候就是脑袋一热，很少去认真的思考的。而且，我们也做事情也都是很难去坚持。包括我，也是一样，有时候计划的相当perfect。可是当你去实施的时候，你就会发现，真的很难。不是做的事情难，难的是每天坚持去做那件你想要做的事情。真的，这对于大部分人而言，都太难了。我想这大部分人比例高的惊人。对于我，也是一样，我也想努力的去克服这些外在的因素或者是那些负面的情绪。所以，我现在会看一些时间管理类的书籍，尽管目前来看是没有太大的进展。还有，可能只在国内会有的现象吧。就是所谓的粉丝文化，你比如：某个明星出个轨，我们这边就比当事人还忙，通常单条的微博评论数都是以百万级别的。所以，我们这边的网民非常的伟大。所以，我是不会成为什么东西真正的粉丝的。我觉得一旦成为某些产品、人的粉丝后，那就成脑残了。现在不就是有脑残粉这个词嘛，你一旦说他喜欢的产品、人，他就会喷你，没有什么理智的。所以，我不会去做什么的粉丝。顶多是，某家的产品做得好，我会多光顾几次而已。某个演员演得戏好，我会关注一下。当然，现在不怎么看电视剧这类的了。其余，我不会过多的关注什么。但是，你就看微博上，火一点的明星，你就会发现他的粉丝数会高达几千万，五六千万的应该大把大把的。不是不是不鼓励社交吧，就是觉得现在我们每个人都太忙了。而且总是为别人忙，别人离个婚，人家自己都不在乎，总有些人很在乎嘛。所以，我们都太忙了，只能拿着手机，看着八卦，就是学习了吧。</p>
<p>我其实看的演讲还是挺多的，我就发现稍微成功一点的人，都会强调一点。那就是，终身学习。刚开始，我可能还不太敏感，年少的心总有些轻狂吧。后来，听得多了，经历得多了，就越来越觉得有道理。只是，自己可能真的有时候做不到吧。我也只能对自己有强烈兴趣的事情能够坚持下来。所以，想一想真的是很失败的。二十几岁了，可能要是真的有才能应该成了吧。包括很多人，很早就成了。像盖茨、乔布斯、扎克伯格，都是二十几岁就成了。像我，跟人家一比，顿时无地自容。感觉真的很失败，各个方面吧，都有所缺失。所以成为现在这个样子一点都不奇怪，只能怪自己。</p>
<p>当然，他也强调了</p>
<blockquote>
<p>你们一定要非常勤奋才行</p>
</blockquote>
<p>这个，道理也很简单啊，可能大部分人都懂。但是，往往落实到行动上可能就会有些人做不到。包括我也是，有些事情总是一拖再拖。可能在这个年纪，真的应该有些活力去做事情，但是自己可能真的比较缺少。所以，自己虽然知道要勤奋，但是可能真的行动的比较少。真的愧对青春的感觉，这是一定要在当下乃至今后要改的。</p>
<p>关于生活中的磨难、打击，他就告诉我们，这些都是在所难免的。我们能做到的不是如何躲避，而是在每次磨难中提升自己。并且，提前做好相应的准备。这个就更贴近生活了，我们每天都会遇见各种各样的麻烦。包括，那些负面情绪。我们能做的就是解决麻烦，消除那些对自己不利的负面情绪。但是，能够做到未雨绸缪确实很难。因为，不是每个人的眼光都有那么长远的，我们大部分人都是只看到眼前的东西。这就需要自己不断的修炼了。前一阵子看到一篇文章，大概讲的是：你的境界决定了你在哪个阶层。这篇文章就分了三种境界：做事的人、做式（势）的人、做局的人。这三种境界就对应现实生活的下等阶层、中等阶层、上等阶层。向我们这样的可能就是底层做事的人，有一项技能，只能每天忙忙碌碌，为他人服务，挣着基本工资。高一层，作势的人。精通了某项技能，又热爱思考，善于观察，把握趋势，懂得顺势而为。再高一层，做局的人。就是能够利用身边的资源，进行资源的合理利用，也就是进行布局。只要局布置好了，一运转起来自然钱就到手了，就像投资一样。不断的运作资源，实现增值。确实有些道理，但是大部分人都只是做事的人。毕竟我们现在的大学教育就是如何教你更好的为别人打个工。所以，我们的眼界还是太窄，视野不宽阔。知道的也太少了，尽管现在是信息爆炸时代。但是，我们脑袋还是太空了。</p>
<p>他的演讲大概就讲了这些，感兴趣的可以找来看看。我自己的理解还是不到位，跟人家智者还是相差太远了。看完之后，觉得骨子里面缺少些的东西实在太多了。所以，我就一直在看，试图启发一下自己。每次看，感觉都不一样。虽然，道理讲的都很简单，但是我们有时还真的缺失的太严重。所以说，人家是真的智者，与智者交谈真的会让自己变得有智慧。反正，我是这么认为的。所以，这几天，我一直在看这篇文章。就像读鸡汤一样，也许不是鸡汤。但是，读完，虽然也难受，觉得自己可能真的是达不到那种成就。但是，读完后又会有一种激励的效果，心里就会好受。</p>
<p>其实，我看过的鸡汤也是挺多的，之前读完还会有些效果。但是现在，基本上我看一眼就知道，这篇文章是不是鸡汤。因为鸡汤，大体就那么几种。第一种套路，就是正派的。他就会给你讲故事，比如：有个商人、有个朋友、xx的老公，就是非常有钱（或者贫穷）。然后就非常痛苦， 后来就经受了某种小事或者磨难，就乐善好施、乐于助人。最后，因果轮回，达到了人生的巅峰或者是获得了心灵的平衡。没有什么逻辑，你说这有什么用。是有用的，就管一个小时、半个小时。就是给人自己穷、自己懒、自己馋，找一个借口。你像胖子，管不住嘴、迈不开腿，就拿这个找一个台阶。读完之后，心里会舒坦一点。我穷、我胖怎么了。但是，你过一个小时之后，痛苦就会回来，你没有钱痛苦始终在。你胖，衣服穿不进去，痛苦还在，一换衣服，就都暴露了。生活的压力，就像牙疼一样，始终就那样疼，还要不了你的命。所以，你看完这篇鸡汤，心里就好受那么一个小时、半个小时的。所以，你就必须大量的补充鸡汤。所以，你看朋友圈、微博的鸡汤文满天飞，是有道理的。第二种套路，就是反派的套路。现实生活中，大部分人就都是阿Q。工作时被老板骂，回家被老婆骂，两头受气，你还没有办法。学生也是一样，同学中你总是看那么几个不顺眼，你看老师就是不爽。现实生活中，你怎么办，你不敢办。你就得忍着。你也不敢骂，你骂，就被老师开除了。你也不敢打回去，你有可能打不过人家。你要是按照正派套路的方法，因果报应型的，显然不行。一旦说，因果报应，那你这辈子受气，显然你上辈子是个坏蛋。所以，肯定不行。但是你又不敢骂，那怎么办啊。你就转发别人骂人的，你自己不敢骂啊，就只好转别人的。这样气也就消了不少了。这种鸡汤文也挺多的，现在社交媒体上到处都是。这种骂人鸡汤文的主体思想就是在说，我不是不敢惹你，是我不屑于理你。就是阿Q那种精神嘛，你素质很low。现在有个词是low B ，我就不想理你。生活中，这种情况也很多的，我们现实生活大部分都是阿Q那样。我见到的鸡汤文大概就是上述两种。当然，我也不确定我一直在看的芒格的演讲是不是鸡汤，反正不怎么符合上述现象。我就姑且把它理解成鸡汤吧，反正，这碗鸡汤我是干了。</p>
<p>今年，芒格93岁，巴菲特86岁。前几天还出席了股东大会，两个人坐在台上，一点也看不出是那么大年纪的人。依然回答着提问，对事情仍然有独特的见解。还时不时的讲个笑话。我看芒格坐在那里，也是时不时的会往嘴里送一些薯片还是饼干啊，就在那里嚼。反正，一点也不像是一个93岁的老人。看到人家这么大年纪还没退休，还在不断的学习。真的是挺敬仰的，同时就感觉自己太失败了。</p>
<p>最后，祝福这个93岁的老人，身体健康。</p>
<pre><code>chapter 2
</code></pre><p>第二部分，我想写一下我遇见的一位老师。他可能是我上大学以来我从心里认为的最好的老师，没有之一。虽然，只是一位公共选修课的老师。但我觉得，他的智慧、阅历、知识一样对我有很大的启发。他在我心里也是一位智者。所以，就将这两篇文章写在一起吧。</p>
<p>其实，刚开始，就是误打误撞选上了他的课。讲法律的，你们可能讲法律的我很枯燥。但他讲的很立体、很形象，虽然有些东西我也不大感兴趣。但是，与其他人的课相比，不知道要好多少倍。你包括讲课时，他也总是会讲述他自己的故事。虽然，他讲的课也许不是最好的。但我在他的身上看到的更多是那种优秀的品质。而且，我发现他的一些观点与我是特别相符的，所以，我很尊敬他或者是有点崇拜他，虽然我尽量的不崇拜人。但是，我真的觉得他是一位智者。与他交谈，我总是感觉收获了许多，尤其实在思维的转变上。</p>
<p>他现在也年近花甲了，有些事情，包括社会上的现象看的真的是比较透彻。而这些，我们往往很少去想，我们想的最多的可能是今天吃点什么呢。所以，他时常发表他的见解、他的立场。我听了以后，就感觉跟我的性格有些像。但我肯定远远不及他，只是在有些事情上我认为我和他的理解是很吻合的。你包括交朋友这件事上，他也说他自己没什么朋友。他就说，朋友首先思维要在一个层面上，不在一个层级上是说不到一起的。还有，当你真正困难时，你交那么多朋友，没有几个愿意帮助的。我在这方面也是这样的。不是说，自己有多么的功利，我也比较讨厌功利化。也许，交朋友这件事是能锻炼人。但我也是这么想的嘛，就是，能说到一起的太少了。思维不在一个层面上，你包括价值观、世界观、生活观、认知观，能找到相同的少之又少。前几天，也是看到一篇文章，大概讲述的是：你看一个人，往往不能看他说什么，你要看他怎么做。然后，他就举了一个例子。例子讲的大概是，公司职员整天抱怨公司各个方面都不好，上司也不怎么样。但是，等到下来任务时候，一个比一个努力，一个比一个爱出风头。就是想让自己脱颖而出嘛。这种情况在学校里也是普遍现象。当然，我也没有歧视别人的意思。我在这里举这个例子，就是想说明，人这种动物，有些时候是自相矛盾的。所以，有时候，可能你觉得能说到一起的，往往一起共事的时候就不行了。所以，为什么叫“处对象”呢！你得先处一处，才知道合不合适。说的就是这个道理，其实，大部分人都是言行不一的。所以，哪里有那么多的朋友啊。能真正交到的太少了。</p>
<p>关于教育，他的理解是，现在的教育都是病态的教育。当然，我也是这么认为的。可能是我自己读的学校比较烂吧，反正我就觉得我现在接受的不是教育。更多的是表面上的形势主义。我真的可能在这里什么都带不走。想一想真的是很可悲的事情。而且，我们大多数人包括我自己。在这种生活下，真的打磨的没有什么棱角了。我也不知道是谁的错，但我还是相信是学校管理的问题。当然，大部分人还是埋怨生源不好。但是，是什么导致了生源不好，不还是学校出问题导致的。所以，管理层平庸，学校也就平庸吧。但是，我读的学校就是这个水平的学校，我还能奢望些什么呢？有一次，他说的一句话，我也觉得特别有感触。他就说，我们大部分人都是提升国民素质中的牺牲品。我就觉得，特别正确，然后就摇头苦笑表示赞同。假如，我能为提升国民素质做出一份贡献，我还是很愿意的。但是，现实的情况是，我认为大学生应该素质挺高的吧。毕竟，读了那么多年书，应该知道自己该做什么不该做什么有了解吧。可是，我感觉可能大部分的素质都是不怎么及格吧。反正，可能大部分都是独生子女吧，都是非常的有个性。当然，别认为我素质高，我素质也很低。</p>
<p>包括教育方面的，他也是说要上就上最好的学校。这一点，以前你问我我肯定是不同意的。但是，自从来到这里以后，我的观念就变了。以前，我认为读什么学校都无所谓。所以，可能也是年少轻狂，就来到了这里。但是，现在真的观念变了。读最好的不一定，每一个人的素质就高或者怎么样。因为每个群体中，总会有些不一样。我还是相信大部分人的素质要比我们高吧。其次，就是环境的影响。这个太重要了，环境真的影响人。你和什么人相处就会成为什么样子。这种影响都是潜移默化的，想一想，你要在一个地方生活四年甚至更久。环境一定会对你产生影响的。再其次，最好的学校，人的做事方式，思维方式，肯定不会和我们一样的。而这种思维也很重要，就像我们每天思维上谈论的可能是游戏。而人家，思维上可能是在发掘一些新的创业机会、如何把握住商机。你的思维就会决定你的思考方式，思考方式又会确定你将如何做。你如何做，就又会决定于你的视野，你的见识，你的眼光。所以，像我可能视野就比较窄，眼光也比较短。所以，你和好的学校的学生差距是越来越大的。所以，想追上人家的可能性非常小。不是没有可能追上，而是这种可能性比较小。他也是这么理解的，还举了例子。大概是，普通小轿车就不要去和保时捷比了，那你肯定跑不过他啊。所以，我们能做的就是在自己的平台上发挥作用，成为最好的自己。他就说，你就比同行人前进半步。所以说，不同的学校，就是可能会产生莫大的不同。你的眼界就决定了你走多远。像我这样的，就是当初眼界看的不够开，然后不努力，就来到了这里。一切都是有原因的。所以，从某些方面讲，我是自找的，活该如此，也不值得同情。</p>
<p>有些事情，真的要自己经历，你才知道走错了。要不然你是不会肯认错的，像我，现在就正在为年少轻狂无知、眼界过小、视野不够开阔而买单。</p>
<p>关于投资方面的，他说：最重要的就是选对方向，并且要一直持有，就是坚持。投资方面的我虽然不大懂，但是他这句话是有普世意义的。不管做什么，你都需要选择正确吧。选择哪所学校、找什么工作、和谁结婚、在哪里定居。这几件都是人生的大事情。你都要正确地做出选择。你像我这样的，第一件就选择错了。而你选择能够正确的前提就是，要足够的了解，视野要足够的开阔。像我，就是视野太窄了，就走到今天这样，可悲吧。说回投资，他就说，你需要三到五年去弄清楚投资，然后从几千支股票中，找出那几只正确的股票买下它。这样，你就成功一半了。然后，买下了，你就要长期持有啊。你不能，明天涨价了，就出手卖了。像真正懂股票的买巴菲特的股票，就没卖过。所以，这第二件事情，也需要你去坚持，当然更需要眼光长远了。不能哪天降到不行了，再拿在手里你就担心会赔了，就卖了。所以，这也是很难的，这就需要你视野了。一定要看得开、看得远。可能大部分人都做不到。这也就是为什么富人只占1%，剩下的99%都是普通人。你像巴菲特，很难有人做到他那种成就的。</p>
<p>还有一个因素我比较敬佩的就是，他已经把他儿子送到美国了，并且已经在美国定居了。所以，我认为他真是一位智者，一名好老师，也是一位好父亲吧。当然，我并不是说我父亲不好。我的父亲当然有我的父亲的好，而且，我也认为我的父亲很伟大。然后，他也说了把儿子送出国的原因。他就说，在美国，你是人才，你会得到尊重。我也没去过美国，我也不知道是不是真的如此。但我还是相信，毕竟美国的顶尖大学是世界上最多的。近代以来，所有的重大发现，基本上都有美国。所以，我还是相信那种环境是会产生这种效果的。事实就在那里，没有办法不相信。所以，可能真的是在美国那种环境下容易塑造素养高的人吧。</p>
<p>我现在能理解的大概就这些吧，总之，我认为这是一位好老师，智者。平时，虽然也不怎么点名，但我是没有缺过席。因为，我觉得这对我的思维、认知是有帮助的，而且能增加我的认为素养。就是这样。但是，可能大部分人都没怎么去过。所以，我也不知道其他人都是怎么想的。也许是我有问题吧。但是，我还是认为我做的正确。因为，现在的大的环境是：你做什么都要和其他人一样，我们这边就是这个样子。你要是做的和其他人不一样，你就是另类，大家就不承认你，笑话你。你比如：别人都在那里看课本，我就在那里看其他的，他就认为你看的是闲书。别人就是在学习，我就是在闲的没事干。这种情况很多的。但是，我也不是要故意要和别人做不一样的事情。我只是觉得有时候我们太按照规矩办事了，结果呢？不还是来到了这种院校。而且，这种被要求一致性倾向已经成为了我们思维习惯的一部分。所以，有时候，我发现我做的和其他人不一样，我就有点发慌。因为，你很可能在其他人眼中是一个另类了。现在，我还是这么认为的，我选的这个课是正确的。所以，我应该做的没有错。</p>
<p>还记得，最后一堂课，他讲的：年轻人犯错，上帝都会原谅，但是你犯了这个错。你怎么去弥补，你选了一个没用的、没前景的、社会不需要的专业，你怎么去弥补。我感觉就是在说自己啊，你花了几年的时间去学一个对你以后没用的专业，没有办法弥补。所以，不管面对什么事情，思维一定要在正确的位置。不要轻易地做出选择，在正确的时间要做正确的事情吧。他就说嘛，你认为正确的不一定是正确的，要做深入的思考。所以，在你做出选择时，思考一下对自己有没有帮助，不要人云亦云。比如：要不要去上课，去上课要不要听。这些都应该思考一下，自己这方面可能真的很缺少。我们现在做事情可能真是思考的太少了。每天看着新闻联播，一看，祖国形势大好，我还担心什么呢？然后，就什么都不想了，想一想教育确实挺可怕的。让人变得不会思考，然后教会了我们如何更好的为别人打个工。</p>
<p>现在，这位老师的课已经结课了。他也应该飞去美国了吧。我本打算，找个机会和他谈一谈。然后，由于个人原因吧，就没实现这个想法。也本打算要一个联系方式，其实也有听多次机会的。但，还是没实现。可能，自己在这方面的缺陷还是比较大的。但是，我也觉得，相遇即是有缘，我也认真的把他的那些优秀的思维落实到自己的思想中。这也许就够了，所以，还真的要谢谢这位老师。他说，他已经不打算在教书了，但是公共选修课还是会上。他不想再这样妥协下去了，这不是真正的教育，我还是很敬佩他的。所以，他是我来大学以来遇见的最好的老师，也很有智慧。年近花甲，可能真的对现实看得透彻。与这种智者交谈，身心都是愉悦的。说的话处处都在点子上，就是很有智慧，生活的赢家。</p>
<p>最后，真的谢谢这位老师，吴老师。</p>
<p><strong>山高路远，有缘再见。</strong></p>
<p>写在最后：这篇文章已经断断续续的写了两周吧，直到今天终于写完了。其实，就是自己太懒了。所以，以后尽量不为自己找理由。最近，就一直在看芒格的演讲，看完真的触动挺大的。然后，就遇见了吴老师，听了他讲的课，也让我的思维产生共鸣。然后，就有了这篇文章。我认为，他们两个人都是智者，也都是生活的赢家。与他们对完话，就是感觉心灵有一种震撼的感觉。所以，我们真的应该多与智者对话。学习那种优秀的思维方式，拓展视野，开阔眼界。总之，多思考，勤奋，努力，不断学习，成为最好的自己。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jY1eNlL6NKs&quot;&gt;Charlie Munger USC Law Commencement Speech - May 2007&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chapter 1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这几天，我反复在看查理芒格在南加州大学的演讲。看得我就有些难受，可能有些人不认识查理。他其实就是投资大师沃伦巴菲特的合伙人，他们和巴菲特共同管理伯克希尔哈撒韦公司。伯克希尔哈撒韦，我们都知道，是人类迄今为止在大额长期投资方面最成功的公司。几十年间市值涨了13000多倍，现在伯克希尔的股票一股已经达到了250000美元。其实，我对名人一般不感冒，除非是我特别特别崇拜的。因为，就像传记一样，我认为大多数传记都没什么实话。都是出名了之后为自己洗白嘛，编造一些传奇的经历。其实，这也是在激励大众，但是我认为这就是忽悠群众的东西。但我觉得像查理这么大年纪的人，也不会再说什么大话。这篇演讲是在2007年5月23日在南加州大学法学院毕业典礼上发表的，当时他已经是84岁的高龄了。这么算下来，他今年都已经93岁了。就在前几天的5月6日，他竟然还是和巴菲特一起出席了伯克希尔的股东大会，那可是一个93岁的老人了。而且，巴菲特也已经有86岁了。所以，这有可能是他主持的最后一次股东大会。像他们这种人真的是一个成功的人，又有钱，身体又好。像我们，可能到了80、90岁，神智都有可能不清醒了。但人家，还是那么的睿智，那么精力充沛。&lt;/p&gt;
    
    </summary>
    
      <category term="日记" scheme="https://KevinJe.github.io/categories/%E6%97%A5%E8%AE%B0/"/>
    
    
      <category term="随笔" scheme="https://KevinJe.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
    
      <category term="生活" scheme="https://KevinJe.github.io/tags/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="感悟" scheme="https://KevinJe.github.io/tags/%E6%84%9F%E6%82%9F/"/>
    
  </entry>
  
  <entry>
    <title>使用jsoup解析你想要的内容</title>
    <link href="https://KevinJe.github.io/2017/06/03/%E4%BD%BF%E7%94%A8jsoup%E8%A7%A3%E6%9E%90%E4%BD%A0%E6%83%B3%E8%A6%81%E7%9A%84%E5%86%85%E5%AE%B9/"/>
    <id>https://KevinJe.github.io/2017/06/03/使用jsoup解析你想要的内容/</id>
    <published>2017-06-03T09:52:23.000Z</published>
    <updated>2017-09-22T13:30:26.264Z</updated>
    
    <content type="html"><![CDATA[<p>有些时候，我们做APP时，也许会为没有相应的API而发愁。看到某些网站上某些内容很好，却没有办法获取到。最近，接触到了jsoup，用它就可以轻松获取到网站上的内容了。jsoup是什么呢？jsoup 是一款<a href="http://lib.csdn.net/base/javase" target="_blank" rel="external">Java</a> 的HTML解析器，可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API，可通过DOM，CSS以及类似于<a href="http://lib.csdn.net/base/jquery" target="_blank" rel="external">jQuery</a>的操作方法来取出和操作数据。</p>
<a id="more"></a>
<p>jsoup的主要功能如下：</p>
<ol>
<li>从一个URL，文件或字符串中解析HTML；</li>
<li>使用DOM或CSS选择器来查找、取出数据；</li>
<li>可操作HTML元素、属性、文本。</li>
</ol>
<blockquote>
<p><strong>jsoup官方文档</strong>：<a href="https://jsoup.org/cookbook/" target="_blank" rel="external">https://jsoup.org/cookbook/</a><br><strong>中文文档</strong>：<a href="http://www.open-open.com/jsoup/" target="_blank" rel="external">http://www.open-open.com/jsoup/</a></p>
</blockquote>
<p>在Android Studio中直接在Gradle中加入以下依赖</p>
<blockquote>
<p>compile ‘org.jsoup:jsoup:1.10.2’</p>
</blockquote>
<p>然后，你就可以找到一个喜欢的网站去解析你想要的内容了。</p>
<p>下面简单说一下如何使用，因为自己之前听过，但是最近采用，把自己使用的一些心得写出来。</p>
<p>我们以泡在网上的日子的<a href="http://www.jcodecraeer.com/plus/list.php?tid=4" target="_blank" rel="external">综合资讯</a>栏目做一个例子。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-28401489a064eb2a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="泡在网上的日子"></p>
<p>在浏览器中点击查看网页源代码，查看整个网页的源代码。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-85f541366babcc41.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="网页源代码"></p>
<p>可以看到，全都是标签。所以，最好懂一点点前端的知识，使用起来会更容易。下面来看简单的应用。</p>
<figure class="highlight dart"><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="comment">//从一个URL加载一个Document对象。</span></div><div class="line">Document <span class="built_in">document</span> = Jsoup.connect(<span class="string">"http://www.jcodecraeer.com/plus/list.php?tid=4"</span>).<span class="keyword">get</span>();</div><div class="line"><span class="comment">//选择整个文章列表所在的节点</span></div><div class="line"><span class="built_in">Element</span> element = <span class="built_in">document</span>.select(<span class="string">"div.col-md-9"</span>).first();</div><div class="line"><span class="comment">//选择文章列表下每一篇文章所在的详情页的节点</span></div><div class="line">Elements detailElements = element.select(<span class="string">"div.archive-detail"</span>);</div><div class="line">Log.d(TAG, <span class="string">"文章总数 "</span> + detailElements.size());</div></pre></td></tr></table></figure>
<p>上面在<code>Element element = document.select(&quot;div.col-md-9&quot;).first();</code>最后接了一个<code>first();</code>方法，意思就是找出第一个<code>class = div.col-md-9</code>的div的那一项。然后，找到了<code>class = div.col-md-9</code>的div，也就找到了文章列表。所以，就要选择文章的详情。<code>select(&quot;div.archive-detail&quot;)</code>就会去找到div下的所有名字为<code>archive-detail</code>的div。用<code>size()</code>看一下有多少条数据。</p>
<p>看一下，打印的日志：</p>
<blockquote>
<p>05-25 20:47:22.881 22784-22865/com.example.jsouptest D/MainActivity: 文章总数 15</p>
</blockquote>
<p>没错，每一页最多15条数据。</p>
<figure class="highlight lasso"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">//循环遍历每一项</span></div><div class="line">for (int i = <span class="number">0</span>; i &lt; detailElements.size(); i++) &#123;</div><div class="line">     <span class="comment">//循环遍历详情中的一项</span></div><div class="line">     Element detailElement = detailElements.get(i);</div><div class="line">     <span class="comment">//选择一项中的标题所在的节点</span></div><div class="line">     Element articleTitle = detailElement.<span class="keyword">select</span>(<span class="string">"h3 a"</span>).first();</div><div class="line">     <span class="comment">//选择一项中内容所在的节点</span></div><div class="line">     Elements articleContent = detailElement.<span class="keyword">select</span>(<span class="string">"p"</span>);</div><div class="line">     <span class="comment">//得到标题所对应的链接</span></div><div class="line">     <span class="keyword">Log</span>.d(<span class="built_in">TAG</span>, <span class="string">"article href "</span> +articleTitle.attr(<span class="string">"href"</span>));</div><div class="line">     <span class="comment">//得到文章标题</span></div><div class="line">     <span class="keyword">Log</span>.d(<span class="built_in">TAG</span>, <span class="string">"articleTitle title "</span> +articleTitle.text());</div><div class="line">     <span class="comment">//得到文章内容</span></div><div class="line">     <span class="keyword">Log</span>.d(<span class="built_in">TAG</span>, <span class="string">"articleContent  "</span> + articleContent.text());</div><div class="line">     <span class="comment">//必须使用这种索引的方式取得值，因为有的文章没有图片</span></div><div class="line">     Elements elements = element.getElementsByIndexEquals(i).<span class="keyword">select</span>(<span class="string">"div.covercon"</span>);</div><div class="line">     <span class="comment">//选择文章图片所在的节点，并且取得图片的值</span></div><div class="line">     <span class="keyword">Log</span>.d(<span class="built_in">TAG</span>, <span class="string">"articleImage "</span>+elements.<span class="keyword">select</span>(<span class="string">"img"</span>).attr(<span class="string">"src"</span>));</div><div class="line">     <span class="keyword">Log</span>.d(<span class="built_in">TAG</span>, <span class="string">"getData: ==========================================================="</span>);</div><div class="line"> &#125;</div></pre></td></tr></table></figure>
<p>最后的打印结果：</p>
<figure class="highlight groovy"><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></pre></td><td class="code"><pre><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.881</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> article href <span class="regexp">/a/</span>wangzhantuijian<span class="regexp">/waiwenfanyi/</span><span class="number">2017</span><span class="regexp">/0523/</span><span class="number">7968.</span>html</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.881</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> articleTitle title Java 之父 James Gosling 宣布加入亚马逊 AWS</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.881</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> articleContent  著名计算机科学家、Java 之父 James Gosling 宣布 加盟亚马逊 AWS 服务，成为云计算巨头的杰出工程师。James Gosling 也在其 Facebook 主页上确认了这一消息。 亚马逊证实 Gosling 已加入了公司，但没有对他将要做的工作提出任何进一步的说明。尽管目前尚不</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.911</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> <span class="string">getData:</span> <span class="regexp">/uploads/</span><span class="number">20170523</span>/<span class="number">1495549009281427</span>-lp.jpg</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.911</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> <span class="string">getData:</span> ===========================================================</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.911</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> article href <span class="regexp">/a/</span>anzhuokaifa<span class="regexp">/anzhuozixun/</span><span class="number">2017</span><span class="regexp">/0515/</span><span class="number">7947.</span>html</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.911</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> articleTitle title 绿色守护作者发起Android 绿色应用公约</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.911</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> articleContent  安卓最大的问题，不是系统本身，而是第三方app毫无节制的消耗系统资源。比如无休止的唤醒，各种不必要的通知等。而往往规模越大的公司，在这方面做得越差，这些应用往往属于必备应用，导致装完这些应用之后，几乎无法在保证流畅性的前提下继续安装其它应用了</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.921</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> <span class="string">getData:</span> <span class="regexp">/uploads/</span><span class="number">170515</span>/<span class="number">1</span><span class="number">-1</span>F5151TG3334.png</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.921</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> <span class="string">getData:</span> ===========================================================</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.921</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> article href <span class="regexp">/a/</span>anzhuokaifa<span class="regexp">/anzhuozixun/</span><span class="number">2017</span><span class="regexp">/0515/</span><span class="number">7945.</span>html</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.921</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> articleTitle title Google 推出新的 Android 系统更新方案，碎片化问题有救了？</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.921</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> articleContent  Android 已经是世界上第一大移动设备操作系统，但对于很多 Android 手机用户来说，他们手上的“第一大”操作系统，却并不是最新版本的，用户在抱怨得不到更新，Android 的所有者 Google 当然也在发愁。 （图自： Android ） 根据 Google 官方最新的统计显示</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.931</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> <span class="string">getData:</span> <span class="regexp">/uploads/</span>allimg<span class="regexp">/170515/</span><span class="number">1</span><span class="number">-1</span>F5151403080-L.png</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.931</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> <span class="string">getData:</span> ===========================================================</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.931</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> article href <span class="regexp">/a/</span>wangzhantuijian<span class="regexp">/waiwenfanyi/</span><span class="number">2017</span><span class="regexp">/0511/</span><span class="number">7937.</span>html</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.931</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> articleTitle title Windows 商店封杀 Chrome 和 Firefox 等第三方浏览器</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.931</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> articleContent  微软上周正式宣布Windows <span class="number">10</span> S时，很多人都想知道Windows <span class="number">10</span>的这个版本是否真的是对消费者友好。 Windows <span class="number">10</span> S绝对不是Windows RT，但是，Windows <span class="number">10</span> S用户将不得不使用微软Edge和Bing作为其默认的浏览器和搜索引擎，我们不太可能看到Google Chrome或其他流</div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.941</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> <span class="string">getData:</span> </div><div class="line"><span class="number">05</span><span class="number">-25</span> <span class="number">20</span>:<span class="number">47</span>:<span class="number">22.941</span> <span class="number">22784</span><span class="number">-22865</span><span class="regexp">/com.example.jsouptest D/</span><span class="string">MainActivity:</span> <span class="string">getData:</span> ===========================================================</div></pre></td></tr></table></figure>
<p>可以看到已经将文章解析出来了，第一条就是Java之父Gosling去亚马逊的事，哈哈。</p>
<p>其实，注释已经很清楚了。下面说一下使用的细节。</p>
<ol>
<li><p>首先，要先找到你想要内容在网页源代码的位置。然后，就可以用Document对象进行<code>select(&quot;div.className&quot;)</code>。当然，你也可以偷懒，直接就<code>select(&quot;.className&quot;)</code>也是一样可以的。其实，class并不一定要放div<br>标签的。假如，有这样的<code>&lt;article class=&quot;article-content&quot;&gt;</code>那你就要<code>select(&quot;article.article-content&quot;)</code>了，当然也可以<code>select(&quot;.article-content&quot;)</code>。不过不推荐偷懒的写法，毕竟以后看起来会好理解，学会变通吧。</p>
</li>
<li><p>取值的过程中一定，先要选择<code>select()</code>值外面的class，然后在<code>attr(&quot;key&quot;)</code>。这样就可以得到键key所对应的值value了。</p>
</li>
<li><p>只想得到每一个纯文本，像上面的标题一样。<code>&lt;h3&gt;&lt;a href=&quot;/a/wangzhantuijian/waiwenfanyi/2017/0523/7968.html&quot; title=&quot;Java 之父 James Gosling 宣布加入亚马逊 AWS&quot; &gt;Java 之父 James Gosling 宣布加入亚马逊 AWS&lt;/a&gt;&lt;/h3&gt;</code>。源代码是这个样子的。可以看到，标题横跨两个标签，这时可以<code>select(“h3 a”)</code>两个标签间以空格间隔。就选择了标题所在的标签。然后可以调用<code>text()</code>方法就会返回一个纯文本。</p>
</li>
<li><p>和上面一样如果外面还包裹一个 div名字叫做article-detail 就要<code>select(&quot;div.article-detail h3 a&quot;)</code>这样就可以选择到标题所在的节点了，这是一步到位。</p>
</li>
<li><p>上面的文章中有些是有图片的，有些没有，我们在用时如果不处理，就会造成错误。因为，你解析出的文章数是15，而图片数是10。这样，你如果不处理，就会造成前十篇文章都有图片，但事实上前十篇文章有的不一定有图片。刚开始，我就是没处理，就一直报角标越界异常 ArrayIndexOutOfBoundsException 。因为在第十一篇文章时没有图片了，你还去试图访问自然就出问题了。所以我们不应该提前将每一个图片拿出来，我们应该根据索引加载对应文章的图片就行了。也就是<code>element.getElementsByIndexEquals(i)</code>的作用。<br><strong>这个问题在于文章的图片标签不在文章详情标签里，而是自己单独一个标签，所以我们应该按照索引去取。</strong> 上面的源代码有体现他们两者的位置。</p>
</li>
<li><p>如果像要获得某一节点下的html代码，只需要调用<code>html()</code>就能给你返回对应的html代码。假如网页文字是粗体显示的，你也想在APP中实现和网页一样的效果，你只要调用<code>html()</code>，然后就可以使用<code>TextView.setText(Html.fromHtml(articleContent))</code>。这样，就可以在Android显示粗体了。</p>
</li>
<li><p>上面在去对应的链接过程中，有的直接在html中返回的是相对路径。这时，如果你想取到绝对路径。可以这样：<code>link.attr(&quot;abs:href&quot;)</code>，就是在链接标签<code>href</code>前加上<code>abs:</code>。当然，不想自己拼接，也有对应的方法， <a href="http://jsoup.org/apidocs/org/jsoup/nodes/Node.html#absUrl(java.lang.String" target="_blank" rel="external">Node.absUrl(String key)</a>)。</p>
</li>
</ol>
<p>总结一下，使用方法：</p>
<ol>
<li>找到心仪的网页</li>
<li>用Jsoup.connect()获取网页的document</li>
<li>查看网页源码，找到你想要的地方，使用Element.select(String<br>selector)</li>
<li>用Node.attr(String key)或者Element.text()方法把数据抽出来</li>
</ol>
<p>最后，不会的地方，要学会勤奋的去查文档，或者利用好Google。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;有些时候，我们做APP时，也许会为没有相应的API而发愁。看到某些网站上某些内容很好，却没有办法获取到。最近，接触到了jsoup，用它就可以轻松获取到网站上的内容了。jsoup是什么呢？jsoup 是一款&lt;a href=&quot;http://lib.csdn.net/base/javase&quot;&gt;Java&lt;/a&gt; 的HTML解析器，可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API，可通过DOM，CSS以及类似于&lt;a href=&quot;http://lib.csdn.net/base/jquery&quot;&gt;jQuery&lt;/a&gt;的操作方法来取出和操作数据。&lt;/p&gt;
    
    </summary>
    
      <category term="Android" scheme="https://KevinJe.github.io/categories/Android/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="Jsoup" scheme="https://KevinJe.github.io/tags/Jsoup/"/>
    
  </entry>
  
  <entry>
    <title>大学那些事儿</title>
    <link href="https://KevinJe.github.io/2017/03/18/%E5%A4%A7%E5%AD%A6%E9%82%A3%E4%BA%9B%E4%BA%8B%E5%84%BF/"/>
    <id>https://KevinJe.github.io/2017/03/18/大学那些事儿/</id>
    <published>2017-03-18T02:27:40.000Z</published>
    <updated>2017-09-22T12:57:40.258Z</updated>
    
    <content type="html"><![CDATA[<blockquote>
<p>开头，先放上我最喜欢的BGM，是链接到Youtube的。Youtube，已经被墙了，需要翻墙才能看到。什么？不会翻墙，好吧，真是我国的合格好公民。老哥，那你只能自己想办法，或者选择不看了。毕竟，你不一定喜欢。<br><a href="https://www.youtube.com/watch?v=qycqF1CWcXg" target="_blank" rel="external">Background Music Instrumentals - relaxdaily - B-Sides N°1</a></p>
</blockquote>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-6af41d4eb6747767.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/420" alt="追梦者"></p>
<p>我博客的上一篇文章，同时也是我博客的第一篇文章，被我投稿到了互联网的专题，没想到有幸被收录了。这令我很是高兴，对我来说更是一种激励吧。但令我更没有想到的是，在我着手写这篇文章时，我的上一篇文章已经有100左右的阅读量了。在网络上，有这么多的人看我的文章，这还是第一次。所以，更是给了我，很大的激励作用。不光是阅读量创下了历史记录，评论量也创造了我的历史。其中评论最多的就是鼓励我了，在此，对各位的帮助、鼓励表示由衷的感激。同时，上篇文章还有一人赞赏，这也是我有史以来，得到的第一次赞赏。我同样知道是在鼓励我，真的谢谢。我以前的时候也不怎么写作，所以写的文章难免思维混乱。但，这里是我自己的博客，我可以想写些什么就写些什么吧。</p>
<a id="more"></a>
<p>最近，我一直在复习Android基础知识，打算着整理一篇文章出来。但是，自从我写完上次文章以来，我发现我的写作热情一直未退去。总想着写点什么东西。毕竟这里是我的博客，我可以任意的写作。今天，就来说说我自己为何学习Andriod。虽然我还是比较的菜，我的故事显然也不是什么励志的故事。现在人们不都是喜欢看鸡汤文嘛，但我的故事可能就离鸡汤文比较远了，毕竟我现在真的什么也没有。在这里写出来，也是想留给自己以后来看吧。不知道，几年后者几十年后的自己再看到这些会不会有一些不一样的感受呢！</p>
<p>本人是一名，非985、非211工程的全国二流大学的一名非计算机专业的在校大学生，也就是常说的非科班出身。看到这里，你就会知道了，我的故事也不会是什么励志的，也不是什么所谓的逆袭。这些我显然都不贴边。至于，为什么来到这里学习，完全是误打误撞，有些我自己还没有想明白。但我能知道的就是我来到这里确确实实的是后悔了。首先，先不谈论学校方面，就谈选专业方面，我可能就完全的失败了。现在学的专业，我真的是不怎么喜欢，可以说，也没有什么兴趣。我想，来到这里的大部分人也都如此。我不相信在刚刚经历了高考的一些17、18岁的少年真的想明白了自己想要什么吗？反正我那个时候是不知道自己想要什么的，也不清楚未来该做些什么。就这样，填了高考志愿来到了这个学校。简直就是一个笑话一样，但我也不相信就我一个人会犯这样的笑话。我想在这种考试制度下肯定会有许多人和我有着类似的经历。但是没有办法，要么回去，要么回来。</p>
<p>所以，在我心中一直都有一个心结，那就是我不如退学算了。这种想法一直都在我心中，只是我没有办法和父母说出来。没有任何办法，虽然我向来是向往自由主义的。但是，我的骨子里毕竟还是传统的。虽然，我的父母对我是很开放的。但，毕竟他们的骨子里，还是和今天的中国父母没有什么区别。他们也很看重知识，他们不知道你在学校做了什么，反正，他们知道的就是，你只要在学校就能学到知识。所以，虽然我早就有退学的心思了。但是，对我而言，我是更不想伤害到我的父母的。也许他们会同意我这么做，但并不会是开心的同意。显然，这对他们的伤害更大。所以，我也始终没能开口去说出。其次，我肯定是没有乔布斯、比尔盖茨的才能的，人家的理想是改变世界。最后，也确实是改变了世界。虽然人家退学了，但是人家有才能啊，像我这样的什么也没有，显然就真的是很可悲。有时候，想想我自己，我真的是感觉很可怕。十几年都在读书了，到头一来，可能什么也不会做。这真的是很可悲，可悲到想哭的勇气都没有了，也简直是无法想象的事实。所以，我也不知道我的路在何方。人家都说，路在脚下，可是我连脚下的路都不熟悉，我怎么走向远方呢。我还在一直的困惑，此时已是2016年的3月。</p>
<p>开学后，我还是回到了学校，尽管心里千百般的不情愿。但往往事与愿违也不是自己可以控制的。回到学校的心情自然是不好的，因为自己整天显得无所事事。虽然照常上课，但我通常是不怎么听的。只是做些自己想要做的事情。因为，要我无面对一些不喜欢的事情，我的内心往往是很痛苦的。尽管我不会轻易地说出来，但是有些事情就是这样，自己心里明白这么做的道理就好了。有些事情，你就是不能捅破那层窗户纸的。没有必要去说出来，让别人同情你或者是怎么样的，反正我是不习惯这样的。这也就是我为什么没什么朋友的原因吧。接下来，又是一学期将要过去。心里当然还在想着那个心结，就是我到底要不要继续这么去大学里混日子。偶然的原因在知乎闲逛，就逛到了Android专区。然后，就看到很多前辈自学的经历，很是触动。所以，我就决定看一看自己是不是也能通过自学得到一些什么。于是乎，我便打算开始自学了。</p>
<p>于是，2016年6月开始接触Android。我也总算是找到一些自己喜欢的事情做了。然后，那一整个假期。除了吃饭、睡觉，其余的时间我基本上都在学习Android。我还记得，那一个假期，我哪里都没有去过，整天就是把自己圈在屋子里。之后的3个月左右，我大概将Android基础部分学了一遍。但我知道这还远远不够，因为IT领域的更新速度实在太快了。我看的入门书籍还是基于Android 4.0基础上的，而Google此时早已推出了Android 6.0。新技术也是层出不穷，总之，这一个阶段我走的真是异常的艰难。好在，坚持了下来。2016年9月，我还是又回到了学校，这一次我好像没有以往那么重的包袱了。因为，我觉得我已经找到我感兴趣的事情了。其实，一切都是表象，这里的道路远远没有想象中那么好走。刚开始，还好，我每天都会写一写Android。但是，我越来越发现自己的进步突然间变得好慢。</p>
<p>这时，我的内心其实是急躁的。我不知道该如何度过这一时期，就好像一头横冲直撞的公牛一样没有了方向。加上每天多多少少还要上课，毕竟我不是科班，所以还是耽误我蛮多时间的。然后，我还没有办法彻底的不上课，而且我原本以为晚上的好时光也被上自习代替了。这时的我的内心，真的是五味陈杂，我再一次陷入泥沼。我不知道，这和高中为了应试进行备考有什么差别。难道每天脑子里就该想如何考试，如何拿高分，这就是我上大学的意义吗？那时，我就在想，如果再给我重来一次的机会。我宁愿退学。这种所谓的大学，不读也罢，没有什么好留恋的。所以这一时期，我内心是憔悴的。我不知道该哭还是该笑。在这种制度下，我发现我真的没有存在的价值。也是到现在我才明白了，为什么人家都努力上985院校。不是为了别的，只是为了遇见一些不一样的人，能有一个不一样的环境。毕竟，遇见的人注定会对你有影响的。就像人们当初评价Facebook一样，为什么Facebook会成功，很大程度上是由于它在哈佛学校诞生的。再加上扎克伯格身边的人来自于各个名校，这就更利于Facebook在各个高校的传播。在美国，私立学校是比公立学校好很多的。所以，人家常春藤在校生就有天生的优越感。人家每天在寝室谈论的都是如何改变世界，成为世界的主宰。最后，人家就是改变了世界。</p>
<p>而我们呢？每天就真的像是那种奴隶一样的生活，每天该做些什么、不该做什么都是安排好的。每天都是循规蹈矩，完全的被人家赶着走的感觉。反正这种方式，我显然是受不了的，尤其是我向往那种自由主义的生活。我们每天也只能吹吹牛皮。像我这样的，连吹牛皮都没有人听。不是我不想交朋友，是我不想变得那么功利、自私。其实，人都是自私的，哪里有什么无私。人类在原始社会的野蛮时代，为了活下去就不得不争夺资源。于是就有了各个部落，你要想活下去，就是把自己部落外的人视为敌人。所以，那时的人的脑子里就是仇恨，你必须要下手狠一些，要不然就可能被别人杀。人类从原始文明到现在唯一没有改变的，就是如何毁灭对手。不管是原始社会的部落战争，还是各个朝代的战争，包括现在的商业战争。目的就是如何毁灭对手，活下去。所以，人类从原始社会到现在的基因里就没有无私，反而自私的基因是一直都存在的，就是与生俱来的东西。所以，在现在自私就是对的，你无私在别人看来才是有病，所以大家好像见不到很多无私的人。其实，谁都很自私，只不过有的不表现出来罢了。比如：我们在学校难免不了考试，然后你一考试，就会造成一个尴尬而又普遍的事情，就是它非要给你分出一个三六九等。所以，总是在考试后，有人就会问问这个人，问问那个人，你考了多少分。我对这种事情真的是厌恶到极点，从初中到现在这种事情一直在发生。但是，我还没办法不回答，但是在我心里是不怎么愿意的。我认为，你问分数，无疑就是比一比嘛，或者说叫做分享。但是，我还在处于一个社会主义国家，做什么事情不都讲究个分享精神嘛。毕竟我们要追求的是共产主义。我还是很爱国的，所以，每次有人问我这种问题，我还不能不回答，显得你不是一个国家的人似得。所以，我通常都会回答他，但是我的心里是极度不愿意的。因为，在我心里分数只是一个客观的反应，他也并不代表一切。我们何必要在自己心里分出个三六九等呢！这种事情，我是觉得真的没有一点意义。我这个人最烦的、及其厌恶的就是以成绩来看待一个人，你成绩再好，品德差的一塌糊涂，对不起，我们真的做不了朋友。你成绩再差，但是，你的品德是没有问题的，那这种人就是值得信任的，值得交往的。所以，在大家都喜欢这样做的时候，我一直都不做，而且从初中一直到现在我也没这么做过。我是不是也很自私。所以，你像我这样的又自私又没什么好习惯又不会聊天又不是现在流行语描绘的高富帅。所以，想我这样的没什么朋友好像就不奇怪了。像我这样的，顶多是喝了酒，和自己吹吹牛皮，就这样。</p>
<p>在学校还有一种现象很奇怪，既然扯到这里了，就谈一下吧。比如：下课了，总有人会说道“这节课听明白了吗？我什么都没听懂”。对这种现象，我虽然很无语，但是已经见怪不怪了，毕竟这种事情已经经历了很多次了。所以现在我要分享一段，我关注的公号，也是我在网络上很敬仰的一名老程序员的感悟吧。</p>
<blockquote>
<p>如果有人心里有疑问的话，说明不是一个好学生，作为一个好学生，在经历了这么多年的教育以后，还有疑问，说明教育的不太成功。一个成功的教育产品，应该不能有任何思考，如果你有思考，那也不能说出来；如果你又思考又勇敢还要说出来，那最起码不要写出来；即使以上的规则都违反了，又思考，又说又写，最后的底线是不要签名，不要按手印。如果非得作死，不遵守以上所有规则，发生什么事情都不要感到惊讶。以上的规则适用于朝鲜，因为朝鲜是个神奇的国家。</p>
<p>和今天一样，在古代的时候，统治者也需要下层的人傻乎乎的，古代使用的方法是不让你学习，一个字都不认识，别人说啥你就信啥，老老实实种一辈子地，农夫，山泉，有点田，这辈子好好积德，下辈子再说。现在不行了，因为如果你是文盲的话，没法赚更多的钱养活上层阶级，比如说如果你是文盲的话，去血汗工厂都没法干活，说明书也不认识，所以，必须得让这些奴隶掌握一定的知识，但是，掌握知识是很危险的，万一你胡乱读书，一下子开窍了呢？这对统治阶级是个非常大的威胁。这个世界上有两件事情是最难的，一件事是把你的思想装进别人的脑袋，另一件事情是把别人的钱装进你的口袋。现在的统治阶级需要同时完成这两件事情。使用的方法是垄断教育，只能接受一种教育，古代的时候，不认字的人因为不认字，上别人的当，现在的人，因为受了教育，上文字的当。而且，垄断了教育以后，可以批量生产能干活的笨蛋。所以呢，要搞清楚自己是不是上了当，这个是很难的。反正我是搞不清楚，我每天看新闻联播，政府告诉我，只要听他的就不会上当，所以，我觉得我没有上当吧，呵呵。</p>
</blockquote>
<p>不知道别人读后，什么想法，反正我读了之后发现：我操，说的太TM的对了。哪里有那么多的疑问，课本上怎么说的你就怎么学就是了。中国的学生好像都是这么一个德行，XXXX是XXXX课本上就是这么说的，这就说明教育还是很成功的。至少一大部分人，不会去质疑课本。在他们心里统治者说的就是对的，课本就是真理。好吧，对于这种人，我也无话可说了。还有，也不要质疑为什么我不能访问Google、Facebook、Twitter…就是不让你访问，你哪里来的那么多的疑问，没事多看看书吧。其实，有时候答案是比问题多的，你就安安心心的待在给你开放的圈子里不是很好嘛。这个圈子里那么多东西还不够你玩的。百度，淘宝，支付宝，QQ，微信，微博…。然后，在你的圈子里你一定会有这种感慨：我们这里非常美好，人民生活非常富足，都已经达到了小康水平，我们这里建设的都是共产主义，哪里是你外边的世界可以相比的，我们这里各种东西，根本不用什么Google，Facebook…我们在圈子里晒娃、晒美食、晒旅行，无比的满足，根本不用与外界联系，我们大天朝可是什么都不缺，我们这里无比的幸福，我们有这些就够了。外面的网站还都是英文的，我们不需要，天天看着新闻联播，多好的生活啊。唉。。。于是，我们什么也不想，做的非常对，人家的目的就是这样的，所以我们这些人的觉悟还是非常的高，让人家很是放心。说到这里，你不要以为我是不爱国的，我非常的爱国，我也没有要黑别人的意思。如果有一天你通过搜索引擎看到了我这篇文章，千万不要喷我或是怎么的。现在的这些网络水军我是非常的怕。首先，我是非常的爱国的，我都已经读了十几年的书了，我的脑袋早就已经被洗的白白的了，不会有什么其他的思想了。其次，我写这些文章主要是给自己看的，自娱自乐，也不是要黑别人，也没有人知道我这个博客地址，如果你不感兴趣，那么就请出门左转，谢谢老哥。</p>
<p>就在我内心极度焦虑的这一阶段，我仿佛又回到了一年前来到这里那种心情。最好的办法，无非就是逃避，可是我又能逃去哪里。这一阶段内心极度厌学，退学的欲望占据了上风。我甚至还想到过，挂科了我就直接退学了，也就解脱了。后来，也不知道是运气好还是差，没有挂科。可是，一切似乎是那么的无助，我又能怎么样，我什么也改变不了。这一段时间，我也拒绝和家里人通话，有那么两个月吧。没敢和家里人通话，因为我怕我的话语引起家人不必要的担心，这也是我一直不想的。甚至，那一天是我父亲的生日，我都没能鼓起勇气去打一个电话。从早晨一直犹豫到晚上。期间，姐姐还微信告诉我，让我打一个电话。所以，在这件事情上我是一直以来都是内疚的。这一阶段直到我回到家里才找到所谓的归属感、安全感。</p>
<p>现在，想一想这一个阶段发生的事情，感觉自己的做法中带着那么几分稚嫩。毕竟，无论如何，也不要自己难为自己，不要自己伤害自己。毕竟，没什么大不了的，改变不了环境，就去做自己喜欢的事情，让自己变得更好。</p>
<p>时间来到了2017年，放假回到家中，抛除了各种杂念，继续Android。又是整整一个假期，我没有出过家门。对于Android方面也总算是有些进步了。这时的我，比以往感觉要好得多。不止在Android方面，而在于对事件的分析不再那么的绝对，而是多了一些理性。我想，这得益于阅读的积累吧。我们这一代人，大部分都是独生子女吧。大部分不想动脑，不想动手，更不想动力。总把自己看的高高在上的样子，其实是什么也不会干。说到这里，突然想起一个人，叫做Huffman。1952年，当时他还在MIT读书，还是个学生。他也不想上课，他就和老师说，我能不能不上课啊，我觉得你这个课我都会了。然后老师说，那行，你不上课，你得给我展示一下你不上课的理由啊。但是，人家Huffman同学觉得老师说得对，就去展示。像我这样的，顶多展示一下，我特别能睡觉，早晨8点的课都不上。后来，这位不想上课的年轻人设计了一个算法，来压缩。老师一看，这堂课你就不用上了，就是满分。因此，这位不想上课的同学，设计的算法，就是Huffman编码，压缩算法中的王牌，影响一直到今天。用今天一名网红，但是我还是觉得他是一名老师嘛。后来出来创办了锤子科技的罗永浩老师的一句名言来说，就是彪悍的人生不需要解释。其实，我也什么不会干。也只能敬仰一下。所以，有些事情，我宁愿不做。你可以说我这种人活的很悲哀，我也是这样看待自己的。就像大学里的一个怪像，每个人都想着，我如何如何考下来一个证书。然后，有了证书好像就能走向人生的羊癫疯，赢取白富美。然后，就不惜一切代价考了一次又一次，根本不懂所谓的原理，就是我死记硬背也把你考下来。所以，当时看到这种现象我真的震惊了，这TM的怎么和大学期末考试似得，这是所谓的国考嘛。所以，我不怎么考证的，我认为这种证书考不考意义也不大了。是在考记忆力嘛，抱歉，我真不能陪你玩。也许，有了这种证书后，会进入所谓的好企业。但是，如果我是企业的老板，我显然不会招收这种毕业生。毕竟，你就没有所谓的快速学习能力。而我认为，一个企业，要的就是你的学习能力，能够快速掌握某项技能。也许你是可以不靠真本事考下来证书，但是我不相信你能不靠真本事就在一个企业站住脚，每个月领那么几千的工资。也许，会可能，毕竟社会主义，每个人都有那么一种说不清楚的优越感，迷之自信。其实，考证，也不过越来越功利化。你去看看有多少培训机构就知道，这都催生出一条产业链了，我天朝的教育实在是强。就像教育一样，显然就是为了钱、利益。就连我们现在的宗教，不也是这样吗？耶稣为什么而死，如果不是他一招釜底抽薪断了希伯来教的财路，创立基督教，自称天使，我想他还不至于被犹大出卖，钉死在十字架上。一切都是为了钱。宗教都如此，何况我们普通人呢？或许，你该笑我，连尝试也不敢尝试，还敢在这里吹牛皮。对啊，我也很嘲笑自己，有些事情只能讲给自己听，所以你是该嘲笑我，我也没有什么好反驳的，我也没办法反驳。也许，我真的不适合活在这个时代。从某些方面来讲，我想我已经脱离了这个时代，在时代边缘垂死挣扎吧。前面提到了阅读，我想说我们这一代人太缺乏阅读了，只是会看课本。确实是中国式的好学生啊，我也不想说什么了。只是想说，我们这一代人的生活完全陷入电子产品中，它太好玩了，以至于我们控制不住自己，所以有时间多做些阅读吧。还有，前一阵子，我在那里看一本书。然后，走过来一个人，看到我在那里，说了一句“看闲书呢？”。对于这种人，我也不想和他多探讨些什么，就是有种对牛弹琴的感觉。在这里，说下我的观点。首先，阅读是为了增加阅历，开阔眼界，增长见识，虽然短时间你感觉没有什么影响。但是，我相信这个东西在于积累。其次，就是思考为什么人家能写出这样的文章，你却不行。能写出一本出，是要具有大局观念，同时还要有很好的逻辑思维。所以，我认为这种能力很重要，是要靠阅读来积累沉淀的。年轻人，不要急躁，放慢脚步，少一些碎片阅读，多一些真正的书本式阅读，增加增加自己的眼界吧。所以，我认为书没有闲与不闲之分，你认为这本书能增长你的见识，你又对这本书感兴趣，那你就去读就好了，这本书就是OK的，这就是一本好书。所以，对于这种人，我是真想一句话“我操你大爷的，你懂什么？”怂回去。当然，这种人，我更多的是不想和他多说一些什么，我感觉根本就不在一个频道上，浪费时间。但是，我是没有这种攻击性人格的，我也不会当面说出这种话，也就自己写出来给自己看。最近，还有一件事情，就是美国要在韩国部署什么萨德系统。然后，我大天朝人民就坐不住了，各种抵制、制裁。好吧，对于这种事情，也不是一次两次了。我大天朝之前就抵制过日本、菲律宾了，害怕你个韩国不成？然后，我们这些吃瓜群众，尤其是记者，坐不住了。新闻啊，这新闻一定要把握好。我想说的是，你有时间能不能去看看书了，是不是应该把自己的本职工作做好啊。但是，我是怕了，只能说，我大天朝威武。其实，我建议在每次抵制行动前，你先手握爪机向天问，你就知道抵制洋货到底有多难了。什么时候，也能像韩国似得把那些在其位不谋其职的官员给弹劾下台啊。如果，有一天真能这样，那我可就佩服到五体投地啊，刮目相看啊。希望在我有生之年能够见到这一幕，所以，我大天朝群众还需要努力啊。</p>
<p>之后，在开学又回到了学校。临行前，母亲对我说“做什么事情都要有始有终”。嗯，我懂得母亲的意思，这句话也一直会留在我的心里的。所以，我想不出意外的话，我还是会把本科读完吧。然后，去做些喜欢的事情。至于，真正做什么，我也不清楚，可能我真的是活的比较失败的人吧。我现在也不清楚，我们来到这个世界到底是为了什么，我们存在的意义又是什么？古代都说，四十不惑，那我就等到不惑之年再回头来看待这个问题吧。如果到了不惑之年，还是搞不清这个问题，只能说明我是彻彻底底活的很失败。</p>
<p>回到学校后，我终于有一种看清的感觉。终于，在放下一切后的那种释怀感。有些东西注定就是这样，你自己的力量微不足道，你什么也改变不了，看清现实，无疑是最完美的解决办法。凡事不要和自己较劲，要去想如何让自己过的更舒服，想做点什么就做点什么。趁着年轻，也要想着怎么疯狂一下。生命很短暂，和地球的年龄相比，不过是几分钟和一年的关系。所以，不要活的那么纠结，对自己好一些。现在，终于每天可以有很长的时间做自己喜欢的事情。虽然，还是有些人、有些事情、有些制度，不愿去面对。但我也再不想去计较什么，因为于我而言，更值得我关注的事情还有很多，我也不必再在类似的问题上去浪费时间。有些事情，注定没有结果，所以把握过程，做些自己喜欢的事情，让自己变得更美好。这才是我当下乃至以后最重要也是最应该去做的事情。</p>
<p>这一个阶段，我自己感觉是变化最大的。尤其是在心理上，对待事情能够理性的分析了，性格因素少一些。以前，可能是性格因素多一些，理性少一些。总之，这一个阶段可能是自我来到这里以后过的为数不多的我很满意的时期。我开始，变的阳光、自信、坦然。每天早晨看到太阳升起，新的一天开始，每一刻都是崭新的。</p>
<p>不知不觉，这篇博客扯得有点多，也就写着写着写了这么长。也基本把我自大学以来这一阶段的生活写出了一个大概，也写出了我一直想要说出的一些话。幸亏我生长在这个言论自由的时代，我才敢写出我内心的一些想法以及对于一些事情的看法。如有写的不当，请见谅，因为这只是我一家之言。所以，在这里感谢党、感谢政府、感谢人民的教导，我才有勇气说出我想说的话。</p>
<p>其实，写博客就是给自己看的。显然，在这个后博客时代，我已经显得格格不入了。但是，我一路走来得到了网络上好多不知名人士的帮助，在这里我想表达我对你们帮助的感谢之情。今天的搜索引擎这么发达，虽然我这个博客域名没有几个人知道，但我相信总会有人会看到的。假如，你也和我有着相似的经历，或者也正迷茫着。然后，读了我这篇博客，你的内心哪怕是有那么一秒钟的触动。或者是，读出了鸡汤文的味道，虽然我这不是鸡汤文。那么，我想我这篇博客就没有白写。其实，现实生活中，我就是那种向往自由主义的人。虽然，在这个现实的社会，会被人嘲笑。但是，我一直认为，自由才是引爆点。拥有自由，才会有创造的灵感，一个社会才会因此进步。我们可以回顾近两百年，人类文明的发展史，你就会发现，其中的每一项重大发现基本上都会有美国人的贡献。我也并不是故意夸大美国，可是这真的就是事实。美国，也是大家所公认的具有自由、民主、法制的国家，所以我们有时候确实应该学习一些美好的文化。所以，我一直都是向往那种自由的环境，尽管别人可能以为我就是一精神病。但是，我还是希望，中国能够引领这一个时代，创造出改变世界的重大发现，这也一直是我向往的。其实，写到这里，接近结尾，我也在纠结要不要发布出去。然后，自由软件、开源文化的精神告诉我：发出去，让其他人看到。虽然，可能只有我自己看到。但是，也就算是对自由、开源文化的一点追求吧。</p>
<p>最后，引用《中国合伙人》（American Dreams In China）的一句话作为结尾，与大家共勉：</p>
<blockquote>
<p>如果额头终将刻上皱纹，你只能做到它不刻在你的心上。</p>
</blockquote>
<p><strong>皱纹，刻在脸上那是沧桑，刻在心上那是苍老。</strong></p>
<p>后记：<br>2016年的10月我开始在简书写博客，后来因为上面的种种原因，博客一直的断更。而这篇文章是在断更前已经写了一小部分，但是一直没有发布出来。直到前些天，我决定搭建这个博客，打算一直坚持将博客写下去。然后，我就又续写了之后的一大部分。算是自己对这一阶段的认识与总结。现在，我认为写博客是一个好的习惯。因为在现实生活中，人们宁愿相信好听的假话，也不愿意听刺耳的真话。所以，我决定以后尽量的减少说话，尤其是说废话，让人感觉很无聊的废话是浪费时间的。有这些时间，还不如玩玩手机，毕竟现在的人真的是对手机产生了重度依赖。还有，写博客可以练习写作能力以及逻辑思维能力。就像我现在写的这一篇可能就没什么逻辑，我现在正处于想到什么就写什么的阶段，希望以后会有进步。所以，博客以后会坚持写下去，我认为这就是正确的，我也希望我会一直坚持下去。</p>
]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;开头，先放上我最喜欢的BGM，是链接到Youtube的。Youtube，已经被墙了，需要翻墙才能看到。什么？不会翻墙，好吧，真是我国的合格好公民。老哥，那你只能自己想办法，或者选择不看了。毕竟，你不一定喜欢。&lt;br&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=qycqF1CWcXg&quot;&gt;Background Music Instrumentals - relaxdaily - B-Sides N°1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;http://upload-images.jianshu.io/upload_images/1602023-6af41d4eb6747767.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/420&quot; alt=&quot;追梦者&quot;&gt;&lt;/p&gt;
&lt;p&gt;我博客的上一篇文章，同时也是我博客的第一篇文章，被我投稿到了互联网的专题，没想到有幸被收录了。这令我很是高兴，对我来说更是一种激励吧。但令我更没有想到的是，在我着手写这篇文章时，我的上一篇文章已经有100左右的阅读量了。在网络上，有这么多的人看我的文章，这还是第一次。所以，更是给了我，很大的激励作用。不光是阅读量创下了历史记录，评论量也创造了我的历史。其中评论最多的就是鼓励我了，在此，对各位的帮助、鼓励表示由衷的感激。同时，上篇文章还有一人赞赏，这也是我有史以来，得到的第一次赞赏。我同样知道是在鼓励我，真的谢谢。我以前的时候也不怎么写作，所以写的文章难免思维混乱。但，这里是我自己的博客，我可以想写些什么就写些什么吧。&lt;/p&gt;
    
    </summary>
    
      <category term="日记" scheme="https://KevinJe.github.io/categories/%E6%97%A5%E8%AE%B0/"/>
    
    
      <category term="随笔" scheme="https://KevinJe.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
    
      <category term="生活" scheme="https://KevinJe.github.io/tags/%E7%94%9F%E6%B4%BB/"/>
    
      <category term="感悟" scheme="https://KevinJe.github.io/tags/%E6%84%9F%E6%82%9F/"/>
    
  </entry>
  
  <entry>
    <title>Android之旅2-Android四大组件之BroadcastReceiver篇</title>
    <link href="https://KevinJe.github.io/2017/03/10/Android%E4%B9%8B%E6%97%852-Android%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6%E4%B9%8BBroadcastReceiver%E7%AF%87/"/>
    <id>https://KevinJe.github.io/2017/03/10/Android之旅2-Android四大组件之BroadcastReceiver篇/</id>
    <published>2017-03-10T13:20:57.000Z</published>
    <updated>2017-09-22T12:58:19.149Z</updated>
    
    <content type="html"><![CDATA[<p>今天继续我们的Android之旅，上一篇写了Android四大组件之一的Activity的知识。今天，我们继续来复习Andorid四大组件之一的BroadcastReceiver。</p>
<p>何为BroadcastReceiver呢？其实从字面就可以看出了，就是广播接收者。那这个广播的作用是什么呢？其实Android中的广播机制和现实中的广播没有什么差别。想一想现实中的广播，是不是有通知消息的用途啊。同样，Android中的广播机制也与此是类似的作用。即，通过发送广播通知系统中的某些组件该干什么了。这干什么的逻辑完全在于你自己想实现什么功能。当然，既然可以发送广播，一定也是有接受广播的功能的。</p>
<a id="more"></a>
<p>在Android中广播有两种类型，标准广播和有序广播。</p>
<p>标准广播（<strong>Normal broadcasts</strong>，发送方式：Context.sendBroadacst()），这是一种完全异步执行的广播，广播发出后，没有确定的顺序，广播接收者通常会在同一个时刻接收到这条广播消息。这通常意味着是高效的，但也意味着，在广播发出后是无法进行截断的。</p>
<p>有序广播（<strong>Ordered broadcasts</strong>，发送方式：Context.sendOrderedBroadcast()）,这是一种同步执行的广播，广播发出后，在同一时刻，只会有一个广播接收者接收到这一条广播。每一个广播接收者都是按顺序进行执行的。所以，前一个广播接收者既可以将这条广播传递出去，也可以完全的截断广播，使下一个广播接收者无法接收到广播。可以在AndroidManifest.xml文件中<receiver>标签下设置 android:priority属性，来匹配优先级，优先级越高，就越早接收到广播。</receiver></p>
<p>在这里要分清楚一点，虽然广播也是通过Intent进行传递的。但是通过Intent发送广播的机制与通过Intent开启另一个Activity的机制是截然不同的。开启的Activity是处于前台并且可以与用户进行交互的。而发送广播是一个后台的操作，用户并不能意识到。</p>
<p>下面来看一下具体的使用方法：</p>
<p>##接收广播##<br>如何接收广播呢？想要接收广播就要用到如何注册广播，并且在注册的逻辑中添加想要监听的广播就可以了。</p>
<p>广播的注册方式有两种，是四大组件中最为特殊的一个。其余的三个组件都必须在AndroidManifest.xml文件中进行注册声明，而BroadcastReceiver既可以在AndroidManifest.xml文件中静态注册，也可以通过代码进行动态的注册。</p>
<ul>
<li>静态注册</li>
</ul>
<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">receiver</span> <span class="attr">android:name</span>=<span class="string">"com.example.kevin.receiver.BootReceiver"</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">intent-filter</span>&gt;</span></div><div class="line">                <span class="tag">&lt;<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"android.intent.action.BOOT_COMPLETED"</span>/&gt;</span></div><div class="line">            <span class="tag">&lt;/<span class="name">intent-filter</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">receiver</span>&gt;</span></div></pre></td></tr></table></figure>
<p>在AndroidManifest.xml中的注册BroadcastReceiver与注册Activity没有什么太大的区别，只是标签为<receiver>。android:name来指定具体的是注册的哪一个广播。在 <intent-filter>中声明具体想要接收的广播。这里接收的是手机boot加载完成，也就是监听开机这一动作。在Android中，开机时，系统就会发送android.intent.action.BOOT_COMPLETED这条广播。</intent-filter></receiver></p>
<p>然后在代码中可以像下面这样写：<br><figure class="highlight scala"><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">public <span class="class"><span class="keyword">class</span> <span class="title">BootReceiver</span> <span class="keyword">extends</span> <span class="title">BroadcastReceiver</span> </span>&#123;</div><div class="line">   <span class="meta">@Override</span></div><div class="line">    public void onReceive(<span class="type">Context</span> context, <span class="type">Intent</span> intent) &#123;</div><div class="line">        <span class="type">Toast</span>.makeText(context,<span class="string">"Boot加载成功"</span>, <span class="type">Toast</span>.<span class="type">LENGTH_LONG</span>).show();</div><div class="line">&#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>新建一个广播继承自BroadcastReceiver，然后重写其onReceive()方法，只要系统接收到了相应的广播就会执行onReceive()方法。这里只是打印了一句吐司。</p>
<ul>
<li>动态注册</li>
</ul>
<p>动态注册就要用到registerReceiver(BroadcastReceiver, IntentFilter)这一方法了。可以看到第一个参数就是一个BroadcastReceiver。第二个参数应该也是见过的，在AndroidManifest.xml文件中用来约束具体要监听哪一个广播，同样在这里也是一样的作用。</p>
<figure class="highlight scala"><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></pre></td><td class="code"><pre><div class="line">public <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> <span class="type">IntentFilter</span> intentFilter;</div><div class="line">    <span class="keyword">private</span> <span class="type">NetworkChangeReceiver</span> networkChangeReceiver;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">protected</span> void onCreate(<span class="type">Bundle</span> savedInstanceState) &#123;</div><div class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line">        intentFilter = <span class="keyword">new</span> <span class="type">IntentFilter</span>();</div><div class="line">        <span class="comment">//当网络状态改变时，系统会发出下面这条广播</span></div><div class="line">        intentFilter.addAction(<span class="string">"android.net.conn.CONNECTIVITY_CHANGE"</span>);</div><div class="line">        networkChangeReceiver = <span class="keyword">new</span> <span class="type">NetworkChangeReceiver</span>();</div><div class="line">        registerReceiver(networkChangeReceiver, intentFilter);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="class"><span class="keyword">class</span> <span class="title">NetworkChangeReceiver</span> <span class="keyword">extends</span> <span class="title">BroadcastReceiver</span> </span>&#123;</div><div class="line"></div><div class="line">        <span class="meta">@Override</span></div><div class="line">        public void onReceive(<span class="type">Context</span> context, <span class="type">Intent</span> intent) &#123;</div><div class="line">            <span class="type">Toast</span>.makeText(context, <span class="string">"网络状态改变了"</span>, <span class="type">Toast</span>.<span class="type">LENGTH_SHORT</span>).show();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">protected</span> void onDestroy() &#123;</div><div class="line">        <span class="keyword">super</span>.onDestroy();</div><div class="line">        <span class="keyword">if</span> (networkChangeReceiver != <span class="literal">null</span>) &#123;</div><div class="line">            unregisterReceiver(networkChangeReceiver);</div><div class="line">        &#125;</div><div class="line"></div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>以上就是动态的注册了广播接收者来监听网络状态的改变。现在可以通过手动的改变网络状态，可以发现吐司确实是可以弹出来的，这就是动态注册。最后，记得在onDestroy()中调用unregisterReceiver()方法来取消注册。</p>
<p>##发送广播##<br>既然我们已经知道了广播分为标准广播和有序广播，那么它们是如何来进行广播的发送的呢？</p>
<ul>
<li>发送标准广播</li>
</ul>
<figure class="highlight aspectj"><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></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line">   <span class="keyword">protected</span> <span class="function"><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">       Button button = (Button) findViewById(R.id.button);</div><div class="line">       button.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() &#123;</div><div class="line">           <span class="meta">@Override</span></div><div class="line">           <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>&#123;</div><div class="line">               Intent intent = <span class="keyword">new</span> Intent();</div><div class="line">               intent.setAction(<span class="string">"com.coustom.BroadcastReceiver"</span>);</div><div class="line">               <span class="comment">//发送标准广播</span></div><div class="line">               sendBroadcast(intent);</div><div class="line">           &#125;</div><div class="line">       &#125;);</div><div class="line">   &#125;</div></pre></td></tr></table></figure>
<p>这里就是点击一下按钮就发送一条com.coustom.BroadcastReceiver这样的广播了，当然既然是通过Intent进行发送广播的操作，就可以用Intent的putExtra()方法来携带一些数据。</p>
<p>下面再演示一下如何通过静态注册和动态注册来接收上面自定义的广播。<br><strong>1.静态注册</strong></p>
<ol>
<li>在清单文件中注册</li>
</ol>
<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">receiver</span> <span class="attr">android:name</span>=<span class="string">".MyBroadcastReceiver"</span>&gt;</span></div><div class="line">           <span class="tag">&lt;<span class="name">intent-filter</span>&gt;</span></div><div class="line">               <span class="tag">&lt;<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"com.coustom.BroadcastReceiver"</span>/&gt;</span></div><div class="line">           <span class="tag">&lt;/<span class="name">intent-filter</span>&gt;</span></div><div class="line"> <span class="tag">&lt;/<span class="name">receiver</span>&gt;</span></div></pre></td></tr></table></figure>
<ol>
<li>重写对应的onReceive()方法</li>
</ol>
<figure class="highlight scala"><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="class"><span class="keyword">class</span> <span class="title">MyBroadcastReceiver</span> <span class="keyword">extends</span> <span class="title">BroadcastReceiver</span> </span>&#123;</div><div class="line"></div><div class="line">      <span class="meta">@Override</span></div><div class="line">      public void onReceive(<span class="type">Context</span> context, <span class="type">Intent</span> intent) &#123;</div><div class="line">          <span class="type">Toast</span>.makeText(context, <span class="string">"收到了自定义的广播"</span>, <span class="type">Toast</span>.<span class="type">LENGTH_SHORT</span>).show();</div><div class="line">      &#125;</div><div class="line">  &#125;</div></pre></td></tr></table></figure>
<p><strong>2.动态注册</strong></p>
<figure class="highlight scala"><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></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"> <span class="keyword">protected</span> void onCreate(<span class="type">Bundle</span> savedInstanceState) &#123;</div><div class="line">     <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line">     <span class="type">IntentFilter</span> intentFilter = <span class="keyword">new</span> <span class="type">IntentFilter</span>();</div><div class="line">     intentFilter.addAction(<span class="string">"com.custom.BroadcastReceiver"</span>);</div><div class="line">     <span class="type">MyBroadcastReceiver</span> myBroadcastReceiver = <span class="keyword">new</span> <span class="type">MyBroadcastReceiver</span>();</div><div class="line">     registerReceiver(myBroadcastReceiver,intentFilter);</div><div class="line"> &#125;</div><div class="line"></div><div class="line"> <span class="class"><span class="keyword">class</span> <span class="title">MyBroadcastReceiver</span> <span class="keyword">extends</span> <span class="title">BroadcastReceiver</span> </span>&#123;</div><div class="line">     <span class="meta">@Override</span></div><div class="line">     public void onReceive(<span class="type">Context</span> context, <span class="type">Intent</span> intent) &#123;</div><div class="line">         <span class="type">Toast</span>.makeText(context, <span class="string">"收到了自定义的广播"</span>, <span class="type">Toast</span>.<span class="type">LENGTH_SHORT</span>).show();</div><div class="line">     &#125;</div><div class="line"> &#125;</div></pre></td></tr></table></figure>
<p>就是通过IntentFilter的addAction()方法来监听指定的广播，然后用 registerReceiver()就可以实现动态的注册广播。</p>
<ul>
<li>发送有序广播</li>
</ul>
<figure class="highlight aspectj"><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></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line">  <span class="keyword">protected</span> <span class="function"><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">      Button button = (Button) findViewById(R.id.button);</div><div class="line">      button.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() &#123;</div><div class="line">          <span class="meta">@Override</span></div><div class="line">          <span class="keyword">public</span> <span class="function"><span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>&#123;</div><div class="line">              Intent intent = <span class="keyword">new</span> Intent();</div><div class="line">              intent.setAction(<span class="string">"com.coustom.BroadcastReceiver"</span>);</div><div class="line">              <span class="comment">//发送有序广播</span></div><div class="line">              sendOrderedBroadcast(intent,<span class="keyword">null</span>);</div><div class="line">          &#125;</div><div class="line">      &#125;);</div><div class="line">  &#125;</div></pre></td></tr></table></figure>
<p>和标准广播最大的不同就是调用了sendOrderedBroadcast()来发送有序广播，第一个参数仍然是Intent，第二个参数与权限有关，传入null就可以了。<br>接收这广播的方法和上面的相同。</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">receiver</span> <span class="attr">android:name</span>=<span class="string">".MyBroadcastReceiver"</span>&gt;</span></div><div class="line">            <span class="tag">&lt;<span class="name">intent-filter</span> <span class="attr">android:priority</span>=<span class="string">"100"</span> &gt;</span></div><div class="line">                <span class="tag">&lt;<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"com.coustom.BroadcastReceiver"</span>/&gt;</span></div><div class="line">            <span class="tag">&lt;/<span class="name">intent-filter</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">receiver</span>&gt;</span></div></pre></td></tr></table></figure>
<p>只是这里多了android:priority=”100”，来设置接收广播的优先级。如果你有创建了另一个BroadcastReceiver，并且设置它的android:priority=”50”。那么，MyBroadcastReceiver就会先接收到广播。并且既然MyBroadcastReceiver可以先接收到广播，那么它就可以决定是否将广播继续传播下去。如下所示，就表示MyBroadcastReceiver调用了 abortBroadcast()，表示不想让广播继续传播。于是，其他优先级低的广播就都接受不到这条广播了。<br><figure class="highlight scala"><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"><span class="class"><span class="keyword">class</span> <span class="title">MyBroadcastReceiver</span> <span class="keyword">extends</span> <span class="title">BroadcastReceiver</span> </span>&#123;</div><div class="line"></div><div class="line">        <span class="meta">@Override</span></div><div class="line">        public void onReceive(<span class="type">Context</span> context, <span class="type">Intent</span> intent) &#123;</div><div class="line">            <span class="type">Toast</span>.makeText(context, <span class="string">"收到了自定义的广播"</span>, <span class="type">Toast</span>.<span class="type">LENGTH_SHORT</span>).show();</div><div class="line">            <span class="comment">//终止广播的传递</span></div><div class="line">            abortBroadcast();</div><div class="line">        &#125;</div><div class="line">    &#125;</div></pre></td></tr></table></figure></p>
<p>##本地广播##<br> 前面的广播，不论是标准的还是有序的，都面临一个问题。即：我们发出的广播，任何其他的程序都能接收到，我们也可以接收来自其他程序的广播。这样就会带来一些安全上的问题，所以Android中还提供了LocalBroadcastManager来对广播进行管理，也就是本地广播。通过LocalBroadcastManager发送的广播只能在应用程序内部传递，也只能接受程序内部发出的广播。<br>下面来看一下具体的用法：</p>
<figure class="highlight scala"><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></pre></td><td class="code"><pre><div class="line">public <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123;</div><div class="line">    <span class="keyword">private</span> <span class="type">LocalBroadcastManager</span> localBroadcastManager;</div><div class="line">    <span class="keyword">private</span> <span class="type">LocalBroadcastReceiver</span> localBroadcastReceiver;</div><div class="line">    <span class="keyword">private</span> <span class="type">Context</span> mContext;</div><div class="line"></div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">protected</span> void onCreate(<span class="type">Bundle</span> savedInstanceState) &#123;</div><div class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState);</div><div class="line">        mContext = <span class="keyword">this</span>;</div><div class="line">        <span class="comment">//得到本地广播管理者</span></div><div class="line">        localBroadcastManager = <span class="type">LocalBroadcastManager</span>.getInstance(mContext);</div><div class="line">        <span class="type">Button</span> button = (<span class="type">Button</span>) findViewById(<span class="type">R</span>.id.button);</div><div class="line">        button.setOnClickListener(<span class="keyword">new</span> <span class="type">View</span>.<span class="type">OnClickListener</span>() &#123;</div><div class="line">            <span class="meta">@Override</span></div><div class="line">            public void onClick(<span class="type">View</span> v) &#123;</div><div class="line">                <span class="type">Intent</span> intent = <span class="keyword">new</span> <span class="type">Intent</span>();</div><div class="line">                intent.setAction(<span class="string">"com.coustom.LocalBroadcastReceiver"</span>);</div><div class="line">                <span class="comment">//利用本地广播管理者发送本地广播</span></div><div class="line">                localBroadcastManager.sendBroadcast(intent);</div><div class="line">            &#125;</div><div class="line">        &#125;);</div><div class="line">        <span class="type">IntentFilter</span> intentFilter = <span class="keyword">new</span> <span class="type">IntentFilter</span>();</div><div class="line">        intentFilter.addAction(<span class="string">"com.coustom.LocalBroadcastReceiver"</span>);</div><div class="line">        localBroadcastReceiver = <span class="keyword">new</span> <span class="type">LocalBroadcastReceiver</span>();</div><div class="line">        <span class="comment">//利用本地广播管理者注册本地广播</span></div><div class="line">        localBroadcastManager.registerReceiver(localBroadcastReceiver, intentFilter);</div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="class"><span class="keyword">class</span> <span class="title">LocalBroadcastReceiver</span> <span class="keyword">extends</span> <span class="title">BroadcastReceiver</span> </span>&#123;</div><div class="line"></div><div class="line">        <span class="meta">@Override</span></div><div class="line">        public void onReceive(<span class="type">Context</span> context, <span class="type">Intent</span> intent) &#123;</div><div class="line">            <span class="type">Toast</span>.makeText(context, <span class="string">"收到了本地的自定义的广播"</span>, <span class="type">Toast</span>.<span class="type">LENGTH_SHORT</span>).show();</div><div class="line">            abortBroadcast();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">    <span class="meta">@Override</span></div><div class="line">    <span class="keyword">protected</span> void onDestroy() &#123;</div><div class="line">        <span class="keyword">super</span>.onDestroy();</div><div class="line">        <span class="keyword">if</span> (localBroadcastManager != <span class="literal">null</span>) &#123;</div><div class="line">            <span class="comment">//利用本地广播管理者取消注册本地广播</span></div><div class="line">            localBroadcastManager.unregisterReceiver(localBroadcastReceiver);</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>可以看到通过<br><code>localBroadcastManager = LocalBroadcastManager.getInstance(mContext);</code><br>获取到了本地广播管理者的实例。<br>之后的发送广播、注册广播、取消注册广播都是通过localBroadcastManager来进行的。这也就是本地广播与普通广播不同的地方。</p>
<p>以下是官方文档在最后给出的，我的理解还不到位，但还是写出来吧。</p>
<p>##BroadcastReceiver的生命周期##</p>
<blockquote>
<p>一个BroadcastReceiver存在的时期就是在调用onReceive()方法所持续的时间。一旦这个方法返回了，系统就会认为对象已经执行接受，不再活跃了。这对于你在onReceive()中可以执行什么有很大的影响。异步操作在这里执行是不可能的，因为你必须从这个方法中返回并且处理异步操作，但是这时BroadcastReceiver已经不再活跃，它的进程可能再异步操作完成之前被系统杀死。</p>
</blockquote>
<p>##进程的生命周期##</p>
<blockquote>
<p>一个在执行BroadcastReceiver的方法的进程（也就是在执行其onReceive()<br>方法）被考虑是一种前台的进程，它会在系统中持续的运行除非在系统内存极紧张的情况下才会考虑杀死它。一旦onReceive()这个方法返回了，BroadcastReceiver就不再活跃了。它的宿主的进程此时对于其他正在运行的组件是重要的。这里尤为重要，如果那个进程仅仅维系一个BroadcastReceiver（一个普遍的例子：对于一个应用用户可能从没或者最近没有与之交互），一旦onReceive()这个方法执行完了，系统就会考虑这个进程是空的，就会主动的杀死它。提供可用的资源给其他更重要的进程。</p>
</blockquote>
<p>官方文档最后还给出了一条建议：</p>
<blockquote>
<p>This means that for longer-running operations you will often use a Service<br> in conjunction with a BroadcastReceiver to keep the containing process active for the entire time of your operation.<br>对于长时间运行的操作，你可以将BroadcastReceiver和Service同时应用，来保证进程在整个你的操作时期都保持活跃状态。<br>这里的建议好像就类似于桌面的小部件的实现方法，就是AppWidget。</p>
</blockquote>
<p>##写在最后##<br>上面提到了Service，它也是Android四大组件的一种，关于与Service有关的知识我们以后再来复习吧。BroadcastReceiver也就先说这么多吧，其中的部分理解还是不够深刻，我们今天就到这里了。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天继续我们的Android之旅，上一篇写了Android四大组件之一的Activity的知识。今天，我们继续来复习Andorid四大组件之一的BroadcastReceiver。&lt;/p&gt;
&lt;p&gt;何为BroadcastReceiver呢？其实从字面就可以看出了，就是广播接收者。那这个广播的作用是什么呢？其实Android中的广播机制和现实中的广播没有什么差别。想一想现实中的广播，是不是有通知消息的用途啊。同样，Android中的广播机制也与此是类似的作用。即，通过发送广播通知系统中的某些组件该干什么了。这干什么的逻辑完全在于你自己想实现什么功能。当然，既然可以发送广播，一定也是有接受广播的功能的。&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="https://KevinJe.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="BroadcastReceiver" scheme="https://KevinJe.github.io/tags/BroadcastReceiver/"/>
    
  </entry>
  
  <entry>
    <title>Android之旅1-Android四大组件之Activity篇</title>
    <link href="https://KevinJe.github.io/2017/03/10/Android%E4%B9%8B%E6%97%851-Android%E5%9B%9B%E5%A4%A7%E7%BB%84%E4%BB%B6%E4%B9%8BActivity%E7%AF%87/"/>
    <id>https://KevinJe.github.io/2017/03/10/Android之旅1-Android四大组件之Activity篇/</id>
    <published>2017-03-10T13:18:14.000Z</published>
    <updated>2017-09-22T12:55:14.763Z</updated>
    
    <content type="html"><![CDATA[<p>关于为什么要写属于自己的博客，我在前一篇文章中也提到了。主要是归纳自己这一个时期所学到的知识，帮助自己更好的理解，同时一旦忘了某一处，还可以有个地方快速的查到。我也打算在写这些博客期间，将官方文档回顾一遍。然后，还有自己的Java方面也会一起回顾一遍，毕竟自己的Java功底薄弱啊。虽然，我的英文水平很渣，但我相信我会坚持下去的。总之，写博客是一种好的习惯，希望自己可以一直坚持下去。</p>
<p>本篇作为Android的开篇，自然就会写最常见的组件Activity了。</p>
<a id="more"></a>
<blockquote>
<p>一.Activity的基本使用</p>
<pre><code>1. 创建Activity
2. 开启一个Activity
</code></pre><p>   二.Activity间的数据传递过程</p>
<pre><code>1. 向下一个Activity传递数据
2. 返回数据给上一个Activity
</code></pre><p>   三.Activity的生命周期</p>
<pre><code>1. Activity的生命周期的回调方法以及回调过程
2. 保存Activity的状态
</code></pre><p>   四.Activity的启动模式</p>
</blockquote>
<p>什么是Activity，你可以翻译成<code>活动</code>，但我总觉得这么翻译有些不妥，还是不能翻译出官方的味道。有的翻译成<code>视图</code>，这种感觉还会好一些。<br>那么还是让我们看一下官方文档关于Activity的描述：</p>
<ul>
<li>#####Activity#####</li>
</ul>
<blockquote>
<p>An Activity is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map. Each activity is given a window in which to draw its user interface. The window typically fills the screen, but may be smaller than the screen and float on top of other windows.</p>
</blockquote>
<p>一个Activity是一个应用组件，它提供了一块屏幕，方便与用户之间进行某些交互。像，打电话，拍照，发送邮件，或者是浏览地图。每一个Activity被给予一块窗口，去绘制它自己的用户界面。这个窗口可以填满整个屏幕，但是也可以是比屏幕更小或者悬浮在其他窗口之上。<br>上面的官方文档的介绍已经很清楚明了了，下面看一下如何使用Activity。</p>
<ul>
<li>##创建Activity##<blockquote>
<p>To create an activity, you must create a subclass of Activity(or an existing subclass of it). In your subclass, you need to implement callback methods that the system calls when the activity transitions between various states of its lifecycle, such as when the activity is being created, stopped, resumed, or destroyed.</p>
</blockquote>
</li>
</ul>
<p>首先要新建一个Activity。并且继承Activity，实现其中需要的回调方法，其中最重要的两个回调方法是<em>onCreate()</em>和<em>onPause()</em>。</p>
<blockquote>
<p><strong>onCreate()</strong><br>You must implement this method. The system calls this when creating your activity. Within your implementation, you should initialize the essential components of your activity. Most importantly, this is where you must callsetContentView() to define the layout for the activity’s user interface.</p>
</blockquote>
<p>onCretate()是一个必须要实现的方法，系统在创建你的Activity时回调这个方法。在这里你可以初始化你的Activity所必需的组件。最重要的是，你需要调用setContentView()方法，来规范用户交互界面。</p>
<blockquote>
<p><strong>onPause()</strong><br>The system calls this method as the first indication that the user is leaving your activity (though it does not always mean the activity is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back).</p>
</blockquote>
<p>onPause()是在用户首次离开你的Activity时调用的。所以在这个方法中，你应该保留用户的数据，给用户一个好的交互体验。</p>
<ul>
<li><p>###实现UI界面###<br>完成用户界面的绘制，通常最常用的方式就是在一个xml文件中定义好Activity所需要的布局，然后在Activity的onCreate()方法中使用setContentView()方法就可以让一个布局与Activity关联起来。</p>
</li>
<li><p>###在清单文件中进行声明###<br>每一个Activity都应该在manifest文件中定义，也就是只有在这个清单文件中有过定义，系统才会识别出你是一个Activity。下面是具体需要在什么地方定义的结点。<activity android:name=".ExampleActivity">。这句话就声明ExampleActivity是一个Activity，前面的点.代表省略的包名。</activity></p>
</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="tag">&lt;<span class="name">manifest</span> <span class="attr">...</span> &gt;</span></div><div class="line">    <span class="tag">&lt;<span class="name">application</span> <span class="attr">...</span> &gt;</span> </div><div class="line">         <span class="tag">&lt;<span class="name">activity</span> <span class="attr">android:name</span>=<span class="string">".ExampleActivity"</span> /&gt;</span>  </div><div class="line">         ...</div><div class="line">    <span class="tag">&lt;/<span class="name">application</span> <span class="attr">...</span> &gt;</span> </div><div class="line">         ...</div><div class="line"><span class="tag">&lt;/<span class="name">manifest</span> &gt;</span></div></pre></td></tr></table></figure>
<ul>
<li>####使用 intent filters####<br>使用下面的intent-filter声明一个当前的Activity是一个主活动，当启动应用时首先加载的页面就是当前这个Activity。</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"> <span class="tag">&lt;<span class="name">activity</span> <span class="attr">android:name</span>=<span class="string">".ExampleActivity"</span> <span class="attr">android:icon</span>=<span class="string">"@drawable/app_icon"</span>&gt;</span> </div><div class="line">      <span class="tag">&lt;<span class="name">intent-filter</span>&gt;</span>  </div><div class="line">            <span class="tag">&lt;<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"android.intent.action.MAIN"</span> /&gt;</span>   </div><div class="line">            <span class="tag">&lt;<span class="name">category</span> <span class="attr">android:name</span>=<span class="string">"android.intent.category.LAUNCHER"</span> /&gt;</span>  </div><div class="line"><span class="tag">&lt;/<span class="name">intent-filter</span>&gt;</span></div><div class="line"><span class="tag">&lt;/<span class="name">activity</span>&gt;</span></div></pre></td></tr></table></figure>
<p>总结一下如何使用Activity：</p>
<blockquote>
<ol>
<li>新建一个Activity并且继承Activity。</li>
<li>实现其中想实现的回调方法。共有七种回调方法，分别是：onCreate()、onRestart()、onStart() 、onResume()、onPause()、onStop()、onDestory()。</li>
<li>使用setContentView()方法将你的布局与Activity之间进行绑定。而布局一般都是以xml文件的形式进行定义的。这里也就涉及到了Android中的控件。</li>
<li>在manifest文件中进行注册，Android四大组件都必须在清单文件中进行注册，同时BroadcastReceiver也支持动态注册。</li>
</ol>
</blockquote>
<ul>
<li>##开启一个Activity##<br>上面讲到了如何使用Activity，但是仅仅有一个Activity是不是也太单调了。而我们现在市面上的应用都是有好多个Activity所组成。所以这里就要用到Activity之间的跳转。而Activity之间的跳转就要用到上面提到过的Intent了。</li>
</ul>
<p>官方文档中关于Intent的解释为：</p>
<blockquote>
<p>You can start another activity by calling startActivity(), passing it an Intent that describes the activity you want to start. The intent specifies either the exact activity you want to start or describes the type of action you want to perform (and the system selects the appropriate activity for you, which can even be from a different application). An intent can also carry small amounts of data to be used by the activity that is started.</p>
</blockquote>
<p>你可以启动一个其他的activity通过调用, 并传递一个Intent，它用于描述Activity。 intent指定了你想要启动的Activity,或者指定了你想展现的动作（系统帮你选择合适的Activity，它可能来自于其他的程序）。 intent也可以携带比较小量的数据，用于启动Activity。</p>
<p>从上面可以看出Intent分为两种类型，即显式Intent和隐式Intent。</p>
<p><strong>显式Intent</strong>：在你自己的应用中，你经常会简单地启动一个已知的Activity, 通过创建一个明确的intent。这个intent指定了Activity的类名。如下为启动一个名为SignInActivity的Activity：</p>
<figure class="highlight fortran"><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="keyword">Intent</span> <span class="keyword">intent</span> = new <span class="keyword">Intent</span>(this, SignInActivity.<span class="keyword">class</span>);</div><div class="line">startActivity(<span class="keyword">intent</span>);</div></pre></td></tr></table></figure>
<p><strong>隐式Intent</strong>：当你的应用中没有相应的功能时，你就可以通过隐式Intent来调用系统的或者是设备上其他的应用来完成相应的功能。类似的如：发送邮件，发送短信，拨打电话。如下为调用手机中具有浏览网页功能的应用访问一个网页：</p>
<figure class="highlight fortran"><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="keyword">Intent</span> <span class="keyword">intent</span> = new <span class="keyword">Intent</span>(<span class="keyword">Intent</span>.ACTION_VIEW);</div><div class="line"><span class="keyword">intent</span>.setData(Uri.parse(<span class="string">"http://www.google.com"</span>));</div><div class="line">startActivity(<span class="keyword">intent</span>);</div></pre></td></tr></table></figure>
<p>这就是隐式Intent最有价值的地方，你可以创建一个Intent来描述你想要做什么，系统会为你从其他的应用中选择合适的Activity来处理。</p>
<ul>
<li>###Activity间进行数据传递###<br><strong>向下一个Activity传递数据</strong><br>Intent中提供了的一系列的putExtra()方法的重载，可以把我们想要传递的数据暂存在Intent中，启动另外一个Activity后，再从Intent中取出数据就可以了。</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-56c281213deac1fb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Intent一系列的putExtra()方法"></p>
<p>比如在FirstActivity中将一个字符串传递到SecondActivity中，可以如下操作：</p>
<p>1.在FirstActivity中将数据保存在Intent中。<br><figure class="highlight fortran"><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">String <span class="keyword">data</span>=<span class="string">"Hello SecondActivity"</span>;</div><div class="line"><span class="keyword">Intent</span> <span class="keyword">intent</span> = new <span class="keyword">Intent</span>(FirstActivity.this,SecondActivity.<span class="keyword">class</span>);</div><div class="line"><span class="keyword">intent</span>.putExtra(<span class="string">"extra_data"</span>,<span class="keyword">data</span>);</div><div class="line">startActivity(<span class="keyword">intent</span>);</div></pre></td></tr></table></figure></p>
<p>2.在SecondActivity中取出Intent中的数据，getIntent()方法。<br><figure class="highlight fortran"><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="keyword">Intent</span> <span class="keyword">intent</span> = getIntent();</div><div class="line">String <span class="keyword">data</span> = <span class="keyword">intent</span>.getStringExtra(<span class="string">"extra_data"</span>);</div><div class="line"><span class="built_in">Log</span>.d(<span class="string">"SecondActivity"</span>, <span class="keyword">data</span>);</div></pre></td></tr></table></figure></p>
<ul>
<li>如果在上一个Activity传递过来的是字符串，在下一个Activity中取出时就应该用getStringExtra()。上一个Activity传递过来的是整型数据，在下一个Activity中取出时就应该用getIntExtra()。以此类推。</li>
</ul>
<p><strong>返回数据给上一个Activity</strong><br>如何返回数据给上一个Activity呢？这里就用到了startActivityForResult()，这个方法与startActivity()一样，都可以启动另一个Activity。但不同的是通过startActivityForResult()来启动Activity，你便可以重写onActivityResult()方法来得到上一个Activity中你想得到的数据。具体看代码：</p>
<p>1.在FirstActivity中使用startActivityForResult()跳转到SecondActivity。这个方法接受两个参数，其中第二个参数为请求码，需要传入唯一值。<br><figure class="highlight fortran"><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="keyword">Intent</span> <span class="keyword">intent</span> = new <span class="keyword">Intent</span>(FirstActivity.this, SecondActivity.<span class="keyword">class</span>);</div><div class="line">startActivityForResult(<span class="keyword">intent</span>, <span class="number">1</span>);</div></pre></td></tr></table></figure></p>
<p>2.在SecondActivity中仍然通过putExtra()方法，将数据存到Intent中。这里调用了一个setResult()方法，这个方法很重要，专门用来返回数据给上一个活动。其中的第一个参数为结果码。<br><figure class="highlight mipsasm"><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">Intent intent = new Intent()<span class="comment">;</span></div><div class="line">intent.putExtra(<span class="string">"data_return"</span>, <span class="string">"Hello FirstActivity"</span>)<span class="comment">;</span></div><div class="line">setResult(RESULT_OK, intent)<span class="comment">;</span></div><div class="line">finish()<span class="comment">;</span></div></pre></td></tr></table></figure></p>
<p>3.在FirstActivity中重写onActivityResult()方法，将从SecondActivity中传递过的数据取出来。由于可能在一个Activity中调用startActivityForResult()去启动很多不同的Activity，因此需要检查请求码requestCode，判断数据的来源。<br><figure class="highlight aspectj"><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="meta">@Overrideprotected</span> <span class="function"><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"> <span class="keyword">super</span>.onActivityResult(requestCode, resultCode, data);   </div><div class="line"> <span class="keyword">switch</span> (requestCode)&#123;    </div><div class="line">    <span class="keyword">case</span> <span class="number">1</span>:         </div><div class="line">         <span class="keyword">if</span> (requestCode==RESULT_OK)&#123;      </div><div class="line">            String returnData = data.getStringExtra(<span class="string">"data_return"</span>);                </div><div class="line">            Log.d(<span class="string">"FirstActivity"</span>, returnData);   </div><div class="line">         &#125;         </div><div class="line">         <span class="keyword">break</span>;     </div><div class="line">    <span class="keyword">default</span>:         </div><div class="line">         <span class="keyword">break</span>;  </div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>##结束一个Activity##<br>下面来说说如何结束一个Activity。</p>
<blockquote>
<p>You can shut down an activity by calling its finish() method. You can also shut down a separate activity that you previously started by calling finishActivity()</p>
</blockquote>
<p>你可以调用finish()方法结束一个Activity，你也可以调用finishActivity()方法来结束之前开启的Activity。但是，一般情况下都不会这么做，因为Activity有其自己的生命周期。掌握这些生命周期的回调方法就可以轻松实现Activity从创建到销毁的整个过程。</p>
<p>##Activity的生命周期##<br>Activity类中定义了七个回调的方法，覆盖了Activity生命周期的每一个环节。</p>
<p>1.onCreate()<br>当Activity被第一次创建时会调用此方法，你需要在此完成所需要的的初始化操作，加载布局或者绑定数据。</p>
<p>2.onStart()<br>这个方法在Activity可见之前，也就是活动即将可见，但在onCreate()方法后调用。</p>
<p>3.onResume()<br>这个方法在Activity准备好和用户进行交互时调用，此时的Activity会处于栈顶的位置，即Activity已经完全可见。</p>
<p>4.onPause()<br>这个方法在系统准备去启动或者恢复另一个Activity的时候调用。在这里应该保存一些关键的数据，停止动画，或者将一些消耗CPU的资源释放掉。这个方法执行的一定要快，因为另一个Activity的onResume()方法要在这一个Activity的onPause()执行完后才会执行。</p>
<p>5.onStop()<br>这个方法在完全不可见的时候调用。也就是当另一个Activity完全覆盖了当前的Activity就会调用此方法。当不是完全的覆盖时，例如是一个对话框式的Activity覆盖当前的Activity，则只会执行onPause()方法，而不会执行onStop()方法。</p>
<p>6.onDestroy()<br>这个方法在Activity被销毁之前调用，之后活动状态变为销毁的状态。是Activity会接受的最后一个回调方法。它的回调是因为调用了finish()方法，或者是系统为了节省空间销毁了它。这两种场景可以用onFinishing()方法判断出来。</p>
<p>7.onRestart()<br>这个方法在Activity被重新启动之前会调用。也就是在停止状态变为运行状态之前会调用。</p>
<p><em>其中onPause(),onStop(),onDestroy()这三个回调方法之后，是可以被系统杀死的，也就是回收资源。因为onPause()方法这三个中最先执行，onPause()方法是有保证在进程被杀死之前调用的。在系统内存紧急的情况下，onStop(),onDestroy()方法可能不会执行。因此，就需要你在onPause()方法中保存那些重要的数据，如：用户的输入。但在这个方法中不能做过多的操作，因为另一个Activity的onResume()方法要在这一个Activity的onPause()执行完后才会执行，如果在这里做了大量的操作，是会带来极差的用户体验的，因为用户的等待时间变长了。</em></p>
<p>以上的七个方法除去onRestart()之外，是两两相对的，从而将Activity分为三种生存期。</p>
<p> <strong>1.entire lifetime</strong><br>即完整生存期，就是Activity在onCreate()和onDestroy()方法之间所经历的过程。一般情况下，一个Activity要在onCreate()方法中完成各种出的初始化的操作，而在onDestroy()方法中完成资源的释放。</p>
<p><strong>2.visible lifetime</strong><br>即可见生存期，就是Activity在onStart()和onStop()方法之间所经历的过程。在这一时期，Activity对于用户是可见的，并且可以与之交互。在这两个方法之间，你可以在Activity中维持你想向用户展示的资源。比如：你可已在onStart()方法中注册一个广播，来监听UI的变化，并且在onStop()方法中进行取消注册。</p>
<p><strong>3. foreground lifetime</strong><br>即前台生存期，就是Activity在onResume()和onPause()方法之间所经历的过程。在这一过程中，Activity是位于屏幕上可见的，并且可以获得输入的焦点，也就是Activity处于运行的状态。此时，Activity是可以与用户进行交互的，我们平时接触的最多的也就是这一状态下的Activity了。同时，处于前台的Activity可能经常性的改变状态，在这两个方法中维护数据一定要是轻量级的，以避免用户的等待。</p>
<p>以下是官方文档给出的一张生命周期图，可以帮助我们更好的理解Activity的生命周期。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-0ea517c41200bea6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Activity的生命周期图"></p>
<p><strong>Activity完整的生命周期回调方法</strong><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><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></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">ExampleActivity</span> <span class="keyword">extends</span> <span class="title">Activity</span> </span>&#123; </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">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">       <span class="comment">// The activity is being created.   </span></div><div class="line"> &#125;  </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">onStart</span><span class="params">()</span></span>&#123;       </div><div class="line">         <span class="keyword">super</span>.onStart();     </div><div class="line">        <span class="comment">// The activity is about to become visible.  </span></div><div class="line">  &#125;    </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">onResume</span><span class="params">()</span></span>&#123;  </div><div class="line">         <span class="keyword">super</span>.onResume();    </div><div class="line">        <span class="comment">// The activity has become visible (it is now "resumed").   </span></div><div class="line"> &#125;    </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">onPause</span><span class="params">()</span></span>&#123;       </div><div class="line">         <span class="keyword">super</span>.onPause();       </div><div class="line">        <span class="comment">// Another activity is taking focus (this activity is about to be "paused"). </span></div><div class="line">   &#125;   </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">onStop</span><span class="params">()</span> </span>&#123;   </div><div class="line">         <span class="keyword">super</span>.onStop();     </div><div class="line">        <span class="comment">// The activity is no longer visible (it is now "stopped")   </span></div><div class="line"> &#125;  </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">onDestroy</span><span class="params">()</span> </span>&#123;   </div><div class="line">        <span class="keyword">super</span>.onDestroy();     </div><div class="line">       <span class="comment">// The activity is about to be destroyed.   </span></div><div class="line"> &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>###保存Activity的状态###<br>如何在Activity的状态改变后保存数据呢？</p>
<p>下面官方文档给出了图告诉我们如何进行Activity状态改变后如何进行保存数据的操作。</p>
<p><img src="http://upload-images.jianshu.io/upload_images/1602023-c67d7cea9c53e267.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="Activity的状态保存"></p>
<p>在一个Activity执行onPause()和onStop()方法后，Activity的状态仍然会保留。所以此时，再回到应用的前台，所有的状态就会恢复。但是，系统一旦回收内存，就有可能造成Activity被销毁了。此时，回到前台，就会重新执行onCreate()方法或者onRestart()方法，所以其中的数据也就都没有了。所以要想要这种情况之下进行重要数据的保存，就要用到onSaveInstanceState()这一回调方法了。但是，这一方法并不保证会在Activity销毁前一定执行，官方文档给出的解释是：用户可能通过Back键显式的离开你的Activity。所以，要调用onSaveInstanceState()就要在onStop()或者onPause()之前调用。</p>
<p>同时，官方文档还提到了一点。即使你没有实现这一方法，Android中几乎所有的组件都有默认还原数据的功能。但前提是你给了这个组件一个独一无二的id，即android:id。如果你不想使用这个功能，可以显式的关闭它，通过 设置android:saveEnabled属性为 “false”，或者调用setSaveEnabled()方法。</p>
<p>onSaveInstanceState()这一方法在Activity被销毁之前调用，这个方法携带一个Bundle类型的参数，并且是以键值对方式保存数据的，如： putString()和[putInt()。</p>
<p>下面看一下具体的实现：</p>
<p>1.在MainActivity中调用onSaveInstanceState(),进行数据的保存。<br><figure class="highlight aspectj"><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="keyword">protected</span> <span class="function"><span class="keyword">void</span> <span class="title">onSaveInstanceState</span><span class="params">(Bundle outState)</span> </span>&#123; </div><div class="line">   <span class="keyword">super</span>.onSaveInstanceState(outState);   </div><div class="line">   String tempData=<span class="string">"something you  just typed "</span>;    </div><div class="line">   outState.putString(<span class="string">"data"</span>,tempData);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>2.如何进行数据的恢复呢？其实onCreate()方法带有一个Bundle类型的参数，这个方法一般为null，但是Activity被系统回收之前有通过onSaveInstanceState()保存数据的话，这个参数就会带有之前保存的所有数据。当然，你也可以通过onRestoreInstanceState()方法来取回数据，它同样也携带了一个Bundle类型的参数。<br><figure class="highlight lasso"><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">@Override</div><div class="line"><span class="keyword">protected</span> <span class="literal">void</span> onCreate(Bundle savedInstanceState) &#123;        </div><div class="line">    super.onCreate(savedInstanceState);</div><div class="line">    <span class="keyword">if</span> (savedInstanceState != <span class="built_in">null</span>) &#123;  </div><div class="line">        <span class="built_in">String</span> <span class="built_in">data</span> =  savedInstanceState.getString(<span class="string">"data"</span>);  </div><div class="line">        <span class="keyword">Log</span>.d(<span class="string">"MainActivity"</span>, <span class="built_in">data</span>);&#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p><em>官方文档还给出一点检验Activity的还原能力的方法，就是旋转手机屏幕的方向，观察保存的数据是否能够还原。其中说到这种方法很重要，因为用户会经常性的做此动作。反正我是不会经常性的旋转屏幕，它既然提到了，就了解一下吧。</em></p>
<p>###Activity之间的协同###<br>在官方文档的最后，提到了如何协调各个Activity之间的关系。其实也就是遵循Activity的生命周期。它还给出一个例子：在Activity A 中开启Activity B的顺序是什么？</p>
<p>1.先执行Activity A的onPause()方法。<br>2.然后Activity B 的onCreate(),onStart(),onResume()方法会接连的执行，Acitivity B此时已经获取了焦点。<br>3.如果此时Activity A不可见了，它的onStop()方法会得到执行。</p>
<p>##Activity的启动模式##<br>在Android启动模式一共有四种，分别是standrd，singleTop，singleTask，singleInstance。可以在AndroidManifest.xml中给<activity>标签指定android:launchMode属性来选择相应的启动模式。你也可以通过Intent携带一个Flag来指定你通过startActivity()方法来启动的另一个Activity的启动模式。当然，另一个Activity也可以在AndroidManifest.xml中指定自己的启动模式。那你就会问了一个Activity出现了两种启动模式，那这个Activity执行谁的命令啊！官方文档给出的答复是，自己在AndroidManifest.xml中定义的失效，上一个Activity从Intent中携带的Flag是什么模式，这一个Activity就执行什么模式。</activity></p>
<p><strong>1.standrd</strong><br>这是Activity的默认启动模式，Android使用返回栈来管理Activity。在这种模式下，系统不会在意这个Activity是否在返回栈存在，每次启动都会创建该Activity的一个实例。假如在Activity A 中启动Activity A，就会一直创建实例。在栈中就是 A-A-A，一直是叠加的。所以现在想要退出Activity，就需要按三次Back键。</p>
<p><strong>2.singleTop</strong><br>这种启动模式，在启动Activity时会判断栈顶是否已经存在了该Activity，如果存在了该Activity就不会重复的创建实例出来，而是直接复用栈顶的Activity。还是上面的例子，在栈中的样子是A，对，仅创建一次。所以，只需要按一次Back键就可以退出Activity。如果现在栈中是这样的A-B，Activity B在栈顶，我在Activity B中启动Activity A。此时栈中的情景是A-B-A。又创建了一个A的实例，因为A不在栈顶。</p>
<p><strong>3.singleTask</strong><br>这种启动模式，在启动Activity时会判断栈中方是否已经存在了该Activity，如果栈中存在，就会直接使用该实例，并将此Activity之上的所有Activity都出栈。也就是说此时栈中只能有这一个实例。如果栈中没有实例，就会新建一个Activity。</p>
<p><strong>4.singleInstance</strong><br>这种启动模式是这四种当中最为特殊和复杂的一种了。声明为singleInstance模式的Activity会单独存在在一个返回栈中，不管是哪一个应用程序来访问此Activity都是共用的同一个返回栈，也就解决了共享Activity实例的问题。</p>
<p>##写在最后##<br>关于Activity相关的知识讲到这里也就算是基本完成了，这其中也仍有许多的不足，也有很多的知识没能吃透。但好在，我是看着官方文档写出的这篇文章，还是有一点进步的。其中仍然还有些知识没写到这里，其中的部分知识的理解上也存在着偏差，这些就要留在以后来解决。以后，如果我对某个知识点有新的认识，仍然会更新出来。本篇关于Activity的总结到此也就结束了。</p>
]]></content>
    
    <summary type="html">
    
      &lt;p&gt;关于为什么要写属于自己的博客，我在前一篇文章中也提到了。主要是归纳自己这一个时期所学到的知识，帮助自己更好的理解，同时一旦忘了某一处，还可以有个地方快速的查到。我也打算在写这些博客期间，将官方文档回顾一遍。然后，还有自己的Java方面也会一起回顾一遍，毕竟自己的Java功底薄弱啊。虽然，我的英文水平很渣，但我相信我会坚持下去的。总之，写博客是一种好的习惯，希望自己可以一直坚持下去。&lt;/p&gt;
&lt;p&gt;本篇作为Android的开篇，自然就会写最常见的组件Activity了。&lt;/p&gt;
    
    </summary>
    
      <category term="Android基础" scheme="https://KevinJe.github.io/categories/Android%E5%9F%BA%E7%A1%80/"/>
    
    
      <category term="Android" scheme="https://KevinJe.github.io/tags/Android/"/>
    
      <category term="Activity" scheme="https://KevinJe.github.io/tags/Activity/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="https://KevinJe.github.io/2017/03/10/hello-world/"/>
    <id>https://KevinJe.github.io/2017/03/10/hello-world/</id>
    <published>2017-03-10T12:45:08.000Z</published>
    <updated>2017-09-22T12:57:12.135Z</updated>
    
    <content type="html"><![CDATA[<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width="330" height="86" src="//music.163.com/outchain/player?type=2&id=22829933&auto=1&height=66"></iframe>


<p><img src="http://upload-images.jianshu.io/upload_images/1602023-8bfbf95bb96b95cd.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/320" alt="诗与远方"></p>
<p>2016年6月的某一天，对于别人来说可能平淡无奇，但对于我而言，意义很大。这一天，我成功的在terminal中打印出了<code>Hello World</code>。对计算机编程感兴趣的人可能已经知道了我正在做什么。没错，向世界致敬，<code>Hello World</code>。世界，我来了。从这一天开始，我开始了学习Java的旅程，也是我自学Android走出的第一步。彼时，距离Android发布已经满8年了。而我，作为一个初学者，一切才刚刚开始……</p>
<a id="more"></a>
<p>现在的时间是2016年的10月末了，这么算下来，自学的时间也有四个月到五个月了吧。在这期间，一路的跌跌撞撞，其中充满了各种的坎坷。但好在，我坚持下来了。其中的个中滋味，也许只有自己才能知道。在这过程中，我也曾无数次的质问过自己还应不应该走下去。但每当我做出一个界面或者是修复了一个在别人眼中或许都不值得一提的bug，我却感觉到了从未有过的欢乐。也许这就是兴趣吧！虽然我现在仍然是一个编程白痴，但我还是决定学习计算机。在经历了无数次的类似的经历，我也算是有了一点Android基础。当然，这也是很令我感到兴奋的。所以，我更加的坚定了自学Android的信念。</p>
<p>虽然是有了一些Android的基础，但是随着学习的深入，仍然会感到很多的力不从心。有些时候，我是不知道自己下一步该做些什么的。这个问题一直也是困扰了我很长时间的。毕竟我是从一个编程白痴一点一点走过来的。在学习的方法技巧上，也有许多不好的习惯，这些都是需要我以后去改变的。所以，我决定搭建一个博客来对自己所学到的知识进行总结、归纳、演绎。</p>
<p>其实想搭建一个自己的blog这个想法，从我开始自学Android开始就一直在我脑海中酝酿。当然，我还是最希望自己搭建一个属于自己的blog。但是我毕竟也是从一个编程白痴走过来的，我也发现搭建一个自己的博客成本还是很高的。这个愿望就留到以后来折腾吧。所以，我在简书开通了自己的博客。</p>
<p>其实，第三方的博客很多，而且大多数人也都在使用第三方的博客。为什么我会选择简书呢，我一向喜欢简洁的风格，而简书就是那种很简洁的风格，所以第一眼看到简书，我就爱上它了。</p>
<p>这里以后就是我分享自己故事的地方，不论是生活中的、或是学习过程中的故事我也都会记录在这里。毕竟人生在世，总要给自己留一点痕迹吧。还有一点，就是我不大愿意在社交网络上分享自己的故事。现在不都是流行晒这个、晒那个嘛。但我对这种东西不感兴趣，也不敏感，甚至还有一些厌恶。所以，从某种程度上来说，我好像已经脱离了这个时代。在这里写出我的故事也就算是倾诉一下自己内心的想法吧，留给自己以后来看、来回忆。</p>
<p>最后，我还是希望自己能够在这条路上一直走下去，不论遇到什么，都一直坚持下去。</p>
<p><em>你好，世界，我来了。</em></p>
<p>最后的最后，借用苹果教主乔布斯的一句话来表达自己此刻的想法。</p>
<blockquote>
<p>You’ve got to find what you love</p>
</blockquote>
<hr>
<p><strong>我已经找到了我所喜爱的，我想我早已经无路可退。</strong></p>
<hr>
<p>Update:上面的文字是我在2016年10月份写于简书的，几个月过去了。自己也有了一点小小的进步，所以就想以后一直坚持写博客，记录自己生活中的点点滴滴。但愿这一次可以坚持写下去，这样就又有了一个兴趣，还可以锻炼一下写作的能力。本来打算在简书长期写博客的，就在前几天心血来潮的想要搭建属于自己的博客。于是，花了小半天的时间搭建了这个博客。当然，这个博客是基于Github Pages和Hexo的。最后，在此，感谢为开源事业做出贡献的开发者们。没有你们的奉献，一切都不会这么容易，感谢。最后的最后，还是老传统，既然来到了新的世界。就自然想到了，Hello World 。</p>
]]></content>
    
    <summary type="html">
    
      &lt;iframe frameborder=&quot;no&quot; border=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; width=330 height=86 src=&quot;//music.163.com/outchain/player?type=2&amp;id=22829933&amp;auto=1&amp;height=66&quot;&gt;&lt;/iframe&gt;


&lt;p&gt;&lt;img src=&quot;http://upload-images.jianshu.io/upload_images/1602023-8bfbf95bb96b95cd.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/320&quot; alt=&quot;诗与远方&quot;&gt;&lt;/p&gt;
&lt;p&gt;2016年6月的某一天，对于别人来说可能平淡无奇，但对于我而言，意义很大。这一天，我成功的在terminal中打印出了&lt;code&gt;Hello World&lt;/code&gt;。对计算机编程感兴趣的人可能已经知道了我正在做什么。没错，向世界致敬，&lt;code&gt;Hello World&lt;/code&gt;。世界，我来了。从这一天开始，我开始了学习Java的旅程，也是我自学Android走出的第一步。彼时，距离Android发布已经满8年了。而我，作为一个初学者，一切才刚刚开始……&lt;/p&gt;
    
    </summary>
    
      <category term="日记" scheme="https://KevinJe.github.io/categories/%E6%97%A5%E8%AE%B0/"/>
    
    
      <category term="随笔" scheme="https://KevinJe.github.io/tags/%E9%9A%8F%E7%AC%94/"/>
    
  </entry>
  
</feed>
