朱红的泪

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

                          作者:朱红的泪(V5)   

    感谢豆子兄弟的指点 . 本文是参照他的代码在经过一点改造 .

       豆子的灵感是来自于 Google 的 Contacts( 联系人 ) 的实现 , 其中有一个类叫做 PinnedHeaderListView( 固定头的列表),本文也不做过多的解释,因为Google的程序中已经写得很清楚了.

本列表是根据自己需要专门实现类似于QQ列表的功能,所以可能扩展性不是很强,自己满足就好.

QQ: 列表显示效果

 /**  

* QQ 列表

* @author 朱红的泪 (V5)

*

*/

public class QQListView extends ExpandableListView implements OnScrollListener,

       OnGroupClickListener {

    public QQListView(Context context, AttributeSet attrs, int defStyle) {

       super (context, attrs, defStyle);

       registerListener();

    }

 

    public QQListView(Context context, AttributeSet attrs) {

       super (context, attrs);

       registerListener();

    }

 

    public QQListView(Context context) {

       super (context);

       registerListener();

    }

 

    /**

    * Adapter 接口 . 列表必须实现此接口 .

    */

    public interface QQHeaderAdapter {

       public static final int PINNED_HEADER_GONE = 0;

       public static final int PINNED_HEADER_VISIBLE = 1;

       public static final int PINNED_HEADER_PUSHED_UP = 2;

 

       /**

       * 获取 Header 的状态

       *

       * @param groupPosition

       * @param childPosition

       * @return

       *        PINNED_HEADER_GONE,PINNED_HEADER_VISIBLE,PINNED_HEADER_PUSHED_UP 其中之一

       */

       int getQQHeaderState( int groupPosition, int childPosition);

 

       /**

       * 配置 QQHeader, 让 QQHeader 知道显示的内容

       *

       * @param header

       * @param groupPosition

       * @param childPosition

       * @param alpha

       */

       void configureQQHeader(View header, int groupPosition,

              int childPosition, int alpha);

 

       /**

       * 设置组按下的状态 .

       *

       * @param groupPosition

       * @param status

       */

       void setGroupClickStatus( int groupPosition, int status);

 

       /**

       * 获取组按下的状态

       *

       * @param groupPosition

       * @return

       */

       int getGroupClickStatus( int groupPosition);

    }

 

    private static final int MAX_ALPHA = 255;

 

    private QQHeaderAdapter mAdapter ;

    /**

    * 用于在列表头显示的 View,mHeaderViewVisible 为 true 才可见

    */

    private View mHeaderView ;

    /**

    * 列表头是否可见

    */

    private boolean mHeaderViewVisible ;

    private int mHeaderViewWidth ;

    private int mHeaderViewHeight ;

 

    public void setHeaderView(View view, float height) {

       mHeaderView = view;

       AbsListView.LayoutParams lp = new AbsListView.LayoutParams(

              ViewGroup.LayoutParams. MATCH_PARENT , ( int ) height);

       view.setLayoutParams(lp);

       if ( mHeaderView != null ) {

           setFadingEdgeLength(0);

       }

       requestLayout();

    }

 

    private void registerListener() {

       setOnScrollListener( this );

       setOnGroupClickListener( this );

    }

 

    /**

    * 点击 HeaderView 触发的事件

    */

    private void headerViewClick() {

       long packedPosition = getExpandableListPosition( this

              .getFirstVisiblePosition());

       int groupPosition = ExpandableListView

              .getPackedPositionGroup(packedPosition);

       if ( mAdapter .getGroupClickStatus(groupPosition) == 1) {

           this .collapseGroup(groupPosition);

           mAdapter .setGroupClickStatus(groupPosition, 0);

           // 没设置这个会出现一些奇怪的问题 , 暂时不知道为什么

           this .setSelectedGroup(groupPosition);

       }

    }

 

    private float mDownX ;

    private float mDownY ;

 

    /**

    * 如果 HeaderView 是可见的 , 此函数用于判断是否点击了 HeaderView, 并对做相应的处理 ,

    * 因为 HeaderView 是画上去的 , 所以设置事件监听是无效的 , 只有自行控制 .

    */

    @Override

    public boolean onTouchEvent(MotionEvent ev) {

       if ( mHeaderViewVisible ) {

           switch (ev.getAction()) {

           case MotionEvent. ACTION_DOWN :

              mDownX = ev.getX();

              mDownY = ev.getY();

              if ( mDownX <= mHeaderViewWidth && mDownY <= mHeaderViewHeight ) {

                  return true ;

              }

              break ;

           case MotionEvent. ACTION_UP :

              float x = ev.getX();

              float y = ev.getY();

              float offsetX = Math.abs(x - mDownX );

              float offsetY = Math.abs(y - mDownY );

              // 如果 HeaderView 是可见的 , 点击在 HeaderView 内 , 那么触发 headerClick()

              if (x <= mHeaderViewWidth && y <= mHeaderViewHeight

                     && offsetX <= mHeaderViewWidth

                     && offsetY <= mHeaderViewHeight ) {

                  if ( mHeaderView != null ) {

                     headerViewClick();

                  }

                  return true ;

              }

              break ;

           default :

              break ;

           }

       }

       return super .onTouchEvent(ev);

    }

 

    @Override

    public void setAdapter(ExpandableListAdapter adapter) {

       super .setAdapter(adapter);

       mAdapter = (QQHeaderAdapter) adapter;

    }

 

    /**

    * 点击了 Group 触发的事件 , 要根据根据当前点击 Group 的状态来

    */

    @Override

    public boolean onGroupClick(ExpandableListView parent, View v,

           int groupPosition, long id) {

       if ( mAdapter .getGroupClickStatus(groupPosition) == 0) {

           mAdapter .setGroupClickStatus(groupPosition, 1);

           parent.expandGroup(groupPosition);

           parent.setSelectedGroup(groupPosition);

       } else if ( mAdapter .getGroupClickStatus(groupPosition) == 1) {

           mAdapter .setGroupClickStatus(groupPosition, 0);

           parent.collapseGroup(groupPosition);

       }

       // 返回 true 才可以弹回第一行 , 不知道为什么

       return true ;

    }

 

    @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();

       }

    }

 

    private int mOldState = -1;

 

    @Override

    protected void onLayout( boolean changed, int left, int top, int right,

           int bottom) {

       super .onLayout(changed, left, top, right, bottom);

       final long flatPostion =getExpandableListPosition(getFirstVisiblePosition());

       final int groupPos = ExpandableListView

              .getPackedPositionGroup(flatPostion);

       final int childPos = ExpandableListView

              .getPackedPositionChild(flatPostion);

       int state = mAdapter .getQQHeaderState(groupPos, childPos);

       if ( mHeaderView != null && mAdapter != null && state != mOldState ) {

           mOldState = state;

           mHeaderView .layout(0, 0, mHeaderViewWidth , mHeaderViewHeight );

       }

       configureHeaderView(groupPos, childPos);

    }

 

    public void configureHeaderView( int groupPosition, int childPosition) {

       if ( mHeaderView == null || mAdapter == null

              || ((ExpandableListAdapter) mAdapter ).getGroupCount() == 0) {

           return ;

       }

 

       int state = mAdapter .getQQHeaderState(groupPosition, childPosition);

       switch (state) {

       case QQHeaderAdapter. PINNED_HEADER_GONE : {

           mHeaderViewVisible = false ;

           break ;

       }

 

       case QQHeaderAdapter. PINNED_HEADER_VISIBLE : {

           mAdapter .configureQQHeader( mHeaderView , groupPosition,

                  childPosition, MAX_ALPHA );

           if ( mHeaderView .getTop() != 0) {

              mHeaderView .layout(0, 0, mHeaderViewWidth , mHeaderViewHeight );

           }

           mHeaderViewVisible = true ;

           break ;

       }

 

       case QQHeaderAdapter. PINNED_HEADER_PUSHED_UP : {

           View firstView = getChildAt(0);

           int bottom = firstView.getBottom();

           // intitemHeight = 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 .configureQQHeader( mHeaderView , groupPosition,

                  childPosition, 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());

       }

    }

 

    @Override

    public void onScroll(AbsListView view, int firstVisibleItem,

           int visibleItemCount, int totalItemCount) {

       final long flatPos = getExpandableListPosition(firstVisibleItem);

       int groupPosition = ExpandableListView.getPackedPositionGroup(flatPos);

       int childPosition = ExpandableListView.getPackedPositionChild(flatPos);

 

       configureHeaderView(groupPosition, childPosition);

    }

 

    @Override

    public void onScrollStateChanged(AbsListView view, int scrollState) {

 

    }

}

 

邮箱:weiwutan@gmail.com

posted on 2011-12-18 00:55  朱红的泪  阅读(6255)  评论(4编辑  收藏  举报