[转载]【Android】实现类似QQ列表 – 朱红的泪 – 博客园.
感谢豆子兄弟的指点 . 本文是参照他的代码在经过一点改造 .
豆子的灵感是来自于 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