在很多应用中,看到这样的listview:listview滑动过程中分组标题固定在上方,当第二个组滑上来时,第一个组才跟着上滑,下一个组固定,直到该组也滑出上边缘。世上无难事只怕有心人,在github上就有人做出来了,而且效果很好(后来发现安卓自带应用中联系人应用就是这样的,估计github的作者也是仿照着联系人做出来的吧)。

先看截图:

   

PinnedSectionListView继承自listview,众所周知listview的每个子view都是按顺序跟着滚动的,要实现联系人listview的效果还真的找不到思路。看了PinnedSectionListView之后,感觉要改造一个现有的控件,一般都是通过重绘子view来实现的。ViewGroup(ListView继承自它)重绘子view的方法是dispatchDraw。

看看PinnedSectionListView在dispatchDraw中有那些特别的处理:

1
      <div class="line number2 index1 alt1">
        2
      </div>
      
      <div class="line number3 index2 alt2">
        3
      </div>
      
      <div class="line number4 index3 alt1">
        4
      </div>
      
      <div class="line number5 index4 alt2">
        5
      </div>
      
      <div class="line number6 index5 alt1">
        6
      </div>
      
      <div class="line number7 index6 alt2">
        7
      </div>
      
      <div class="line number8 index7 alt1">
        8
      </div>
      
      <div class="line number9 index8 alt2">
        9
      </div>
      
      <div class="line number10 index9 alt1">
        10
      </div>
      
      <div class="line number11 index10 alt2">
        11
      </div>
      
      <div class="line number12 index11 alt1">
        12
      </div>
      
      <div class="line number13 index12 alt2">
        13
      </div>
      
      <div class="line number14 index13 alt1">
        14
      </div>
      
      <div class="line number15 index14 alt2">
        15
      </div>
      
      <div class="line number16 index15 alt1">
        16
      </div>
      
      <div class="line number17 index16 alt2">
        17
      </div>
      
      <div class="line number18 index17 alt1">
        18
      </div>
      
      <div class="line number19 index18 alt2">
        19
      </div>
      
      <div class="line number20 index19 alt1">
        20
      </div>
      
      <div class="line number21 index20 alt2">
        21
      </div>
      
      <div class="line number22 index21 alt1">
        22
      </div>
      
      <div class="line number23 index22 alt2">
        23
      </div>
      
      <div class="line number24 index23 alt1">
        24
      </div>
      
      <div class="line number25 index24 alt2">
        25
      </div>
    </td>
    
    <td class="code">
      <div class="container">
        <div class="line number1 index0 alt2">
          `@Override`
        </div>
        
        <div class="line number2 index1 alt1">
          `protected void dispatchDraw(Canvas canvas) {`
        </div>
        
        <div class="line number3 index2 alt2">
          `    ``super``.dispatchDraw(canvas);`
        </div>
        
        <div class="line number4 index3 alt1">
          `    ``if` `(mPinnedSection != ``null``) {`
        </div>
        
        <div class="line number5 index4 alt2">
          `        ``// prepare variables`
        </div>
        
        <div class="line number6 index5 alt1">
          `        ``int pLeft = getListPaddingLeft();`
        </div>
        
        <div class="line number7 index6 alt2">
          `        ``int pTop = getListPaddingTop();`
        </div>
        
        <div class="line number8 index7 alt1">
          `        ``View view = mPinnedSection.view;`
        </div>
        
        <div class="line number9 index8 alt2">
          `        ``// draw child`
        </div>
        
        <div class="line number10 index9 alt1">
          `        ``canvas.save();`
        </div>
        
        <div class="line number11 index10 alt2">
          `        ``int clipHeight = view.getHeight() +`
        </div>
        
        <div class="line number12 index11 alt1">
          `                ``(mShadowDrawable == ``null` `? 0 : Math.min(mShadowHeight, mSectionsDistanceY));`
        </div>
        
        <div class="line number13 index12 alt2">
          `        ``canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);`
        </div>
        
        <div class="line number14 index13 alt1">
          `        ``canvas.translate(pLeft, pTop + mTranslateY);`
        </div>
        
        <div class="line number15 index14 alt2">
          `        ``drawChild(canvas, mPinnedSection.view, getDrawingTime());`
        </div>
        
        <div class="line number16 index15 alt1">
          `        ``if` `(mShadowDrawable != ``null` `&& mSectionsDistanceY &gt; 0) {`
        </div>
        
        <div class="line number17 index16 alt2">
          `            ``mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),`
        </div>
        
        <div class="line number18 index17 alt1">
          `                    ``mPinnedSection.view.getBottom(),`
        </div>
        
        <div class="line number19 index18 alt2">
          `                    ``mPinnedSection.view.getRight(),`
        </div>
        
        <div class="line number20 index19 alt1">
          `                    ``mPinnedSection.view.getBottom() + mShadowHeight);`
        </div>
        
        <div class="line number21 index20 alt2">
          `            ``mShadowDrawable.draw(canvas);`
        </div>
        
        <div class="line number22 index21 alt1">
          `        ``}`
        </div>
        
        <div class="line number23 index22 alt2">
          `        ``canvas.restore();`
        </div>
        
        <div class="line number24 index23 alt1">
          `    ``}`
        </div>
        
        <div class="line number25 index24 alt2">
          `}`
        </div>
      </div>
    </td>
  </tr>
</table>

关键在于```canvas.translate(pLeft, pTop + mTranslateY);意思是在绘制mPinnedSection的时候,listview滑动了多长的距离,就将canvas移动多少的距离,使mPinnedSection`始终在可见的范围内固定不变。

使用方法:

1.在xml布局文件中将ListView替换成PinnedSectionListView

1
      <div class="line number2 index1 alt1">
        2
      </div>
      
      <div class="line number3 index2 alt2">
        3
      </div>
      
      <div class="line number4 index3 alt1">
        4
      </div>
      
      <div class="line number5 index4 alt2">
        5
      </div>
    </td>
    
    <td class="code">
      <div class="container">
        <div class="line number1 index0 alt2">
          `&lt;com.hb.views.PinnedSectionListView`
        </div>
        
        <div class="line number2 index1 alt1">
          `    ``android:id=``"@android:id/list"`
        </div>
        
        <div class="line number3 index2 alt2">
          `    ``android:layout_width=``"match_parent"`
        </div>
        
        <div class="line number4 index3 alt1">
          `    ``android:layout_height=``"wrap_content"`
        </div>
        
        <div class="line number5 index4 alt2">
          `    ``/&gt;`
        </div>
      </div>
    </td>
  </tr>
</table>

2.让你的ListAdapter继承PinnedSectionListAdapter接口,最简单的做法是只增加isItemViewTypePinned方法,该方法必须在item为pinned的情况下返回true。

1
      <div class="line number2 index1 alt1">
        2
      </div>
      
      <div class="line number3 index2 alt2">
        3
      </div>
      
      <div class="line number4 index3 alt1">
        4
      </div>
      
      <div class="line number5 index4 alt2">
        5
      </div>
      
      <div class="line number6 index5 alt1">
        6
      </div>
      
      <div class="line number7 index6 alt2">
        7
      </div>
      
      <div class="line number8 index7 alt1">
        8
      </div>
      
      <div class="line number9 index8 alt2">
        9
      </div>
    </td>
    
    <td class="code">
      <div class="container">
        <div class="line number1 index0 alt2">
          `// Our adapter class implements 'PinnedSectionListAdapter' interface`
        </div>
        
        <div class="line number2 index1 alt1">
          `class MyPinnedSectionListAdapter extends BaseAdapter implements PinnedSectionListAdapter {`
        </div>
        
        <div class="line number3 index2 alt2">
          `     ``...`
        </div>
        
        <div class="line number4 index3 alt1">
          `     ``// We implement this method to return 'true' for all view types we want to pin`
        </div>
        
        <div class="line number5 index4 alt2">
          `     ``@Override`
        </div>
        
        <div class="line number6 index5 alt1">
          `     ``public boolean isItemViewTypePinned(int viewType) {`
        </div>
        
        <div class="line number7 index6 alt2">
          `         ``return` `viewType == &lt;type to be pinned&gt;;`
        </div>
        
        <div class="line number8 index7 alt1">
          `     ``}`
        </div>
        
        <div class="line number9 index8 alt2">
          `}`
        </div>
      </div>
    </td>
  </tr>
</table>

项目地址:https://github.com/beworker/pinned-section-listview

💬 评论