Android-section-list分组列表

系统 1657 0

分组列表项目源码地址:

http://code.google.com/p/android-section-list/

android中listiew仿组向上滚动特效 原文地址:

http://www.cnblogs.com/xiaoQLu/archive/2011/12/20/2293732.html


非常感谢 kylin17 哟一嗨 同学发现的bug,确实存在此问题,已修正

MySectionIndexer.java中 public int getPositionForSection( int section)方法第一个判断有误,应该为大于等于,已修改,源码重新上传

手机qq上有这样一个特效:当前分组的好友,向上滚动时,在顶部会出现一个透明的框,当下一个分组到达时,会把上一个分组慢慢顶上去,觉得这个特效蛮有意思,就研究了一下,android自带的通讯录分组就有这个特效,这里是自己仿写的一个,部分源码从通讯录中扣出来的

实现原理:

前提条件,假设所有的数据已经分好组

1.listview中每一个item都默认有一个分组标签,但是只显示此分组下面的第一个,其他的默认不显示

2.滚动的时候,判断每一个分组的状态,是向上滚动,还是完全显示,或者隐藏,主要是取当前item所在的分组跟(下一个分组-1=当前分组)相比,如果相等,说明是向上流动,否则是隐藏

3.获取当前分组的状态后,就可以放置分组的位置了,这里使用view.layout(int left,int top,int rigth,int bottom) ,其他left是0,right是分组标签的长度,top和bottom是需要计算的,用ViewGroup.getChileAt(0)获取listview中第一个孩子的view,然后用bottom=view.getBottom获取底部距离父窗口的位置,最后得到两者之差y=bottom-标题框的高度,用这个差就可以得出顶部和底部的位置,就是top和bottom的值。

关键类解析

PinnedHeaderListView.java 这个是实现listview分组的关键,当然布局文件中的listview也要使用这个类,里面有个接口,adapter要实现此接口,是滚动时回调用,其中getPinnedHeaderState()是用来分组标签状态的,

它的3种状态都在此接口中定义,configurePinnedHeader()是用来设置分组标签的标题,也是相当于组中的组名,此类中的configHeaderView()就是放置分组使用的,结合上面的分析跟这个方法研究这个类

        
          /*
        
        
          
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      
        
        
          http://www.apache.org/licenses/LICENSE-2.0
        
        
          
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 
        
        
          */
        
        
          package
        
         com.demo.sectionlistview;


        
          import
        
         android.content.Context;

        
          import
        
         android.graphics.Canvas;

        
          import
        
         android.util.AttributeSet;

        
          import
        
         android.view.View;

        
          import
        
         android.widget.ListAdapter;

        
          import
        
         android.widget.ListView;


        
          /**
        
        
          
 * A ListView that maintains a header pinned at the top of the list. The
 * pinned header can be pushed up and dissolved as needed.
 
        
        
          */
        
        
          public
        
        
          class
        
         PinnedHeaderListView 
        
          extends
        
         ListView {

    
        
          /**
        
        
          
     * Adapter interface.  The list adapter must implement this interface.
     
        
        
          */
        
        
          public
        
        
          interface
        
         PinnedHeaderAdapter {

        
        
          /**
        
        
          
         * Pinned header state: don't show the header.
         
        
        
          */
        
        
          public
        
        
          static
        
        
          final
        
        
          int
        
         PINNED_HEADER_GONE = 0;

        
        
          /**
        
        
          
         * Pinned header state: show the header at the top of the list.
         
        
        
          */
        
        
          public
        
        
          static
        
        
          final
        
        
          int
        
         PINNED_HEADER_VISIBLE = 1;

        
        
          /**
        
        
          
         * Pinned header state: show the header. If the header extends beyond
         * the bottom of the first shown element, push it up and clip.
         
        
        
          */
        
        
          public
        
        
          static
        
        
          final
        
        
          int
        
         PINNED_HEADER_PUSHED_UP = 2;

        
        
          /**
        
        
          
         * Computes the desired state of the pinned header for the given
         * position of the first visible list item. Allowed return values are
         * {
        
        
          @link
        
        
           #PINNED_HEADER_GONE}, {
        
        
          @link
        
        
           #PINNED_HEADER_VISIBLE} or
         * {
        
        
          @link
        
        
           #PINNED_HEADER_PUSHED_UP}.
         
        
        
          */
        
        
          int
        
         getPinnedHeaderState(
        
          int
        
         position);

        
        
          /**
        
        
          
         * Configures the pinned header view to match the first visible list item.
         *
         * 
        
        
          @param
        
        
           header pinned header view.
         * 
        
        
          @param
        
        
           position position of the first visible list item.
         * 
        
        
          @param
        
        
           alpha fading of the header view, between 0 and 255.
         
        
        
          */
        
        
          void
        
         configurePinnedHeader(View header, 
        
          int
        
         position, 
        
          int
        
         alpha);
    }

    
        
          private
        
        
          static
        
        
          final
        
        
          int
        
         MAX_ALPHA = 255;

    
        
          private
        
         PinnedHeaderAdapter mAdapter;
    
        
          private
        
         View mHeaderView;
    
        
          private
        
        
          boolean
        
         mHeaderViewVisible;

    
        
          private
        
        
          int
        
         mHeaderViewWidth;

    
        
          private
        
        
          int
        
         mHeaderViewHeight;

    
        
          public
        
         PinnedHeaderListView(Context context) {
        
        
          super
        
        (context);
    }

    
        
          public
        
         PinnedHeaderListView(Context context, AttributeSet attrs) {
        
        
          super
        
        (context, attrs);
    }

    
        
          public
        
         PinnedHeaderListView(Context context, AttributeSet attrs, 
        
          int
        
         defStyle) {
        
        
          super
        
        (context, attrs, defStyle);
    }

    
        
          public
        
        
          void
        
         setPinnedHeaderView(View view) {
        mHeaderView = view;

        
        
          //
        
        
           Disable vertical fading when the pinned header is present
        
        
        
          //
        
        
           TODO change ListView to allow separate measures for top and bottom fading edge;
        
        
        
          //
        
        
           in this particular case we would like to disable the top, but not the bottom edge.
        
        
        
        
          if
        
         (mHeaderView != 
        
          null
        
        ) {
            setFadingEdgeLength(0);
        }
        requestLayout();
    }

    @Override
    
        
          public
        
        
          void
        
         setAdapter(ListAdapter adapter) {
        
        
          super
        
        .setAdapter(adapter);
        mAdapter = (PinnedHeaderAdapter)adapter;
    }

    @Override
    
        
          protected
        
        
          void
        
         onMeasure(
        
          int
        
         widthMeasureSpec, 
        
          int
        
         heightMeasureSpec) {
        
        
          super
        
        .onMeasure(widthMeasureSpec, heightMeasureSpec);
        
        
          if
        
         (mHeaderView != 
        
          null
        
        ) {
            measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);
            mHeaderViewWidth = mHeaderView.getMeasuredWidth();
            mHeaderViewHeight = mHeaderView.getMeasuredHeight();
        }
    }

    @Override
    
        
          protected
        
        
          void
        
         onLayout(
        
          boolean
        
         changed, 
        
          int
        
         left, 
        
          int
        
         top, 
        
          int
        
         right, 
        
          int
        
         bottom) {
        
        
          super
        
        .onLayout(changed, left, top, right, bottom);
        
        
          if
        
         (mHeaderView != 
        
          null
        
        ) {
            mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
            configureHeaderView(getFirstVisiblePosition());
        }
    }

    
        
          public
        
        
          void
        
         configureHeaderView(
        
          int
        
         position) {
        
        
          if
        
         (mHeaderView == 
        
          null
        
        ) {
            
        
          return
        
        ;
        }

        
        
          int
        
         state = mAdapter.getPinnedHeaderState(position);
        
        
          switch
        
         (state) {
            
        
          case
        
         PinnedHeaderAdapter.PINNED_HEADER_GONE: {
                mHeaderViewVisible = 
        
          false
        
        ;
                
        
          break
        
        ;
            }

            
        
          case
        
         PinnedHeaderAdapter.PINNED_HEADER_VISIBLE: {
                mAdapter.configurePinnedHeader(mHeaderView, position, MAX_ALPHA);
                
        
          if
        
         (mHeaderView.getTop() != 0) {
                    mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
                }
                mHeaderViewVisible = 
        
          true
        
        ;
                
        
          break
        
        ;
            }

            
        
          case
        
         PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP: {
                View firstView = getChildAt(0);
                
        
          int
        
         bottom = firstView.getBottom();

        
          //
        
        
                          int itemHeight = firstView.getHeight();
        
        
        
        
          int
        
         headerHeight = mHeaderView.getHeight();
                
        
          int
        
         y;
                
        
          int
        
         alpha;
                
        
          if
        
         (bottom < headerHeight) {
                    y = (bottom - headerHeight);
                    alpha = MAX_ALPHA * (headerHeight + y) / headerHeight;
                } 
        
          else
        
         {
                    y = 0;
                    alpha = MAX_ALPHA;
                }
                mAdapter.configurePinnedHeader(mHeaderView, position, alpha);
                
        
          if
        
         (mHeaderView.getTop() != y) {
                    mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y);
                }
                mHeaderViewVisible = 
        
          true
        
        ;
                
        
          break
        
        ;
            }
        }
    }

    @Override
    
        
          protected
        
        
          void
        
         dispatchDraw(Canvas canvas) {
        
        
          super
        
        .dispatchDraw(canvas);
        
        
          if
        
         (mHeaderViewVisible) {
            drawChild(canvas, mHeaderView, getDrawingTime());
        }
    }
}
      


MySectionIndexer.java类,主要是用来提供分组的数据的,主要包括,String[]mSections-->所有的组名,int[] mPositions-->每一个组名在listivew中的位置,当然,他们的长度应该是相同的。

        
          package
        
        
           com.demo.sectionlistview;


        
        
          import
        
        
           java.util.Arrays;


        
        
          import
        
        
           android.widget.SectionIndexer;


        
        
          public
        
        
          class
        
         MySectionIndexer 
        
          implements
        
        
           SectionIndexer{
    
        
        
          private
        
        
          final
        
         String[] mSections;
        
          //

        
        
          private
        
        
          final
        
        
          int
        
        
          [] mPositions;
    
        
        
          private
        
        
          final
        
        
          int
        
        
           mCount;
    
    
        
        
          /**
        
        
          
     * 
        
        
          @param
        
        
           sections
     * 
        
        
          @param
        
        
           counts
     
        
        
          */
        
        
          public
        
         MySectionIndexer(String[] sections, 
        
          int
        
        
          [] counts) {
        
        
        
          if
        
         (sections == 
        
          null
        
         || counts == 
        
          null
        
        
          ) {
            
        
        
          throw
        
        
          new
        
        
           NullPointerException();
        }
        
        
        
          if
        
         (sections.length !=
        
           counts.length) {
            
        
        
          throw
        
        
          new
        
        
           IllegalArgumentException(
                    
        
        "The sections and counts arrays must have the same length"
        
          );
        }
        
        
        
          this
        
        .mSections =
        
           sections;
        mPositions 
        
        = 
        
          new
        
        
          int
        
        
          [counts.length];
        
        
        
          int
        
         position = 0
        
          ;
        
        
        
          for
        
         (
        
          int
        
         i = 0; i < counts.length; i++
        
          ) {
            
        
        
          if
        
        (mSections[i] == 
        
          null
        
        
          ) {
                mSections[i] 
        
        = ""
        
          ;
            } 
        
        
          else
        
        
           {
                mSections[i] 
        
        =
        
           mSections[i].trim(); 
            }
            
            mPositions[i] 
        
        =
        
           position;
            position 
        
        +=
        
           counts[i];
        }
        mCount 
        
        =
        
           position;
    }
    
    @Override
    
        
        
          public
        
        
           Object[] getSections() {
        
        
        
          //
        
        
           TODO Auto-generated method stub
        
        
          return
        
        
           mSections;
    }

    @Override
    
        
        
          public
        
        
          int
        
         getPositionForSection(
        
          int
        
        
           section) {
        
        
        
          //
        
        
          change by lcq 2012-10-12 section > mSections.length以为>= 
        
        
          if
        
         (section < 0 || section >=
        
           mSections.length) {
            
        
        
          return
        
         -1
        
          ;
        }
System.out.println(
        
        "lcq:section:"+
        
          section);
        
        
        
          return
        
        
           mPositions[section];
    }

    @Override
    
        
        
          public
        
        
          int
        
         getSectionForPosition(
        
          int
        
        
           position) {
        
        
        
          if
        
         (position < 0 || position >=
        
           mCount) {
            
        
        
          return
        
         -1
        
          ;
        }
        
        
        
          //
        
        
          注意这个方法的返回值,它就是index<0时,返回-index-2的原因
        
        
        
          //
        
        
          解释Arrays.binarySearch,如果搜索结果在数组中,刚返回它在数组中的索引,如果不在,刚返回第一个比它大的索引的负数-1
        
        
        
          //
        
        
          如果没弄明白,请自己想查看api
        
        
          int
        
         index =
        
           Arrays.binarySearch(mPositions, position);
        
        
        
          return
        
         index >= 0 ? index : -index - 2; 
        
          //
        
        
          当index小于0时,返回-index-2,
        
        
                  
    }

}
        
      

  当然,adapter也灰常重要,这里简单分析下,因为具体使用时,会根据情况使用不同的adapter,比如说,有数据库的,可以使用SimpleCursorAdapter,也可以使用SimpleAdapter等等,这里使用的原始的listAdapter,比较麻烦,这里要实现上面提到的PinnedHeaderAdapter,还要实现SectionIndexer,主要是用来根据实际位置查找分组的索引,以及根据索引返回组名在实际listview中的位置(这里有点不太好讲,不太懂的,仔细看源码和api)

其他的就是一些adapter的基本应用以及一些android 的基本知识,这里不在讲述,不懂的请提问。

源码下载地址:http://files.cnblogs.com/xiaoQLu/DemoSectionListView_Plus.rar



Android-section-list分组列表


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论