楼主是在平板上测试的,图片稍微有点大,大家看看效果就好

接下来贴源码:

PinnedHeaderExpandableListView.java

要注意的是 在 onGroupClick方法中parent.setSelectedGroup(groupPosition)这句代码的作用是点击分组置顶,

我这边不需要这个效果,QQ也没有用到,所以给注释了,大家如果需要可以解开注释

package com.xiaos.view;import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnGroupClickListener;public class PinnedHeaderExpandableListView extends ExpandableListView implements OnScrollListener,OnGroupClickListener {public PinnedHeaderExpandableListView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);registerListener();}public PinnedHeaderExpandableListView(Context context, AttributeSet attrs) {super(context, attrs);registerListener();}public PinnedHeaderExpandableListView(Context context) {super(context);registerListener();}/*** Adapter 接口 . 列表必须实现此接口 .*/public interface HeaderAdapter {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 getHeaderState(int groupPosition, int childPosition);/*** 配置 Header, 让 Header 知道显示的内容* @param header* @param groupPosition* @param childPosition* @param alpha*/void configureHeader(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 HeaderAdapter mAdapter;/*** 用于在列表头显示的 View,mHeaderViewVisible 为 true 才可见*/private View mHeaderView;/*** 列表头是否可见*/private boolean mHeaderViewVisible;private int mHeaderViewWidth;private int mHeaderViewHeight;public void setHeaderView(View view) {mHeaderView = view;AbsListView.LayoutParams lp = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);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);}else{this.expandGroup(groupPosition);mAdapter.setGroupClickStatus(groupPosition, 1);}this.setSelectedGroup(groupPosition);}private float mDownX;private float mDownY;/*** 如果 HeaderView 是可见的 , 此函数用于判断是否点击了 HeaderView, 并对做相应的处理 ,* 因为 HeaderView 是画上去的 , 所以设置事件监听是无效的 , 只有自行控制 .*/@Overridepublic 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);}@Overridepublic void setAdapter(ExpandableListAdapter adapter) {super.setAdapter(adapter);mAdapter = (HeaderAdapter) adapter;}/*** * 点击了 Group 触发的事件 , 要根据根据当前点击 Group 的状态来*/@Overridepublic boolean onGroupClick(ExpandableListView parent,View v,int groupPosition,long id) {if (mAdapter.getGroupClickStatus(groupPosition) == 0) {mAdapter.setGroupClickStatus(groupPosition, 1);parent.expandGroup(groupPosition);//Header自动置顶//parent.setSelectedGroup(groupPosition);} else if (mAdapter.getGroupClickStatus(groupPosition) == 1) {mAdapter.setGroupClickStatus(groupPosition, 0);parent.collapseGroup(groupPosition);}// 返回 true 才可以弹回第一行 , 不知道为什么return true;}@Overrideprotected 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;@Overrideprotected 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.getHeaderState(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.getHeaderState(groupPosition, childPosition);switch (state) {case HeaderAdapter.PINNED_HEADER_GONE: {mHeaderViewVisible = false;break;}case HeaderAdapter.PINNED_HEADER_VISIBLE: {mAdapter.configureHeader(mHeaderView, groupPosition,childPosition, MAX_ALPHA);if (mHeaderView.getTop() != 0){mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);}mHeaderViewVisible = true;break;}case HeaderAdapter.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.configureHeader(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) {//分组栏是直接绘制到界面中,而不是加入到ViewGroup中drawChild(canvas, mHeaderView, getDrawingTime());}}@Overridepublic 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);}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}
}

PinnedHeaderExpandableAdapter.java 适配器

实现了PinnedHeaderExpandableListView中HeaderAdapter接口

package com.xiaos.adapter;import android.content.Context;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.TextView;import com.xiaos.pinnedheaderexpandable.R;
import com.xiaos.view.PinnedHeaderExpandableListView;
import com.xiaos.view.PinnedHeaderExpandableListView.HeaderAdapter;public class PinnedHeaderExpandableAdapter extends BaseExpandableListAdapter implements HeaderAdapter{
private String[][] childrenData;
private String[] groupData;
private Context context;
private PinnedHeaderExpandableListView listView;
private LayoutInflater inflater;public PinnedHeaderExpandableAdapter(String[][] childrenData,String[] groupData
,Context context,PinnedHeaderExpandableListView listView){
this.groupData = groupData;
this.childrenData = childrenData;
this.context = context;
this.listView = listView;
inflater = LayoutInflater.from(this.context);
}@Override
public Object getChild(int groupPosition, int childPosition) {
return childrenData[groupPosition][childPosition];
}@Override
public long getChildId(int groupPosition, int childPosition) {
return 0;
}@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
} else {
view = createChildrenView();
}
TextView text = (TextView)view.findViewById(R.id.childto);
text.setText(childrenData[groupPosition][childPosition]);
return view;
}@Override
public int getChildrenCount(int groupPosition) {
return childrenData[groupPosition].length;
}@Override
public Object getGroup(int groupPosition) {
return groupData[groupPosition];
}@Override
public int getGroupCount() {
return groupData.length;
}@Override
public long getGroupId(int groupPosition) {
return 0;
}@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
} else {
view = createGroupView();
} ImageView iv = (ImageView)view.findViewById(R.id.groupIcon);if (isExpanded) {
iv.setImageResource(R.drawable.btn_browser2);
}
else{
iv.setImageResource(R.drawable.btn_browser);
}TextView text = (TextView)view.findViewById(R.id.groupto);
text.setText(groupData[groupPosition]);
return view;
}@Override
public boolean hasStableIds() {
return true;
}@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}private View createChildrenView() {
return inflater.inflate(R.layout.child, null);
}private View createGroupView() {
return inflater.inflate(R.layout.group, null);
}@Override
public int getHeaderState(int groupPosition, int childPosition) {
final int childCount = getChildrenCount(groupPosition);
if (childPosition == childCount - 1) {
return PINNED_HEADER_PUSHED_UP;
} else if (childPosition == -1
&& !listView.isGroupExpanded(groupPosition)) {
return PINNED_HEADER_GONE;
} else {
return PINNED_HEADER_VISIBLE;
}
}@Override
public void configureHeader(View header, int groupPosition,
int childPosition, int alpha) {
String groupData = this.groupData[groupPosition];
((TextView) header.findViewById(R.id.groupto)).setText(groupData);}private SparseIntArray groupStatusMap = new SparseIntArray(); @Override
public void setGroupClickStatus(int groupPosition, int status) {
groupStatusMap.put(groupPosition, status);
}@Override
public int getGroupClickStatus(int groupPosition) {
if (groupStatusMap.keyAt(groupPosition)>=0) {
return groupStatusMap.get(groupPosition);
} else {
return 0;
}
}
}

MainActivity.java主Activity

package com.xiaos.pinnedheaderexpandable;import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnGroupClickListener;import com.xiaos.adapter.PinnedHeaderExpandableAdapter;
import com.xiaos.view.PinnedHeaderExpandableListView;public class MainActivity extends Activity{private PinnedHeaderExpandableListView explistview;private String[][] childrenData = new String[10][10];private String[] groupData = new String[10];private int expandFlag = -1;//控制列表的展开  private PinnedHeaderExpandableAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.layout_main);initView();initData();}/*** 初始化VIEW*/private void initView() {explistview = (PinnedHeaderExpandableListView)findViewById(R.id.explistview);}/*** 初始化数据*/private void initData() {for(int i=0;i<10;i++){groupData[i] = "分组"+i;}for(int i=0;i<10;i++){for(int j=0;j<10;j++){childrenData[i][j] = "好友"+i+"-"+j;}}//设置悬浮头部VIEWexplistview.setHeaderView(getLayoutInflater().inflate(R.layout.group_head,explistview, false));adapter = new PinnedHeaderExpandableAdapter(childrenData, groupData, getApplicationContext(),explistview);explistview.setAdapter(adapter);//设置单个分组展开//explistview.setOnGroupClickListener(new GroupClickListener());}class GroupClickListener implements OnGroupClickListener{@Overridepublic boolean onGroupClick(ExpandableListView parent, View v,int groupPosition, long id) {if (expandFlag == -1) {// 展开被选的groupexplistview.expandGroup(groupPosition);// 设置被选中的group置于顶端explistview.setSelectedGroup(groupPosition);expandFlag = groupPosition;} else if (expandFlag == groupPosition) {explistview.collapseGroup(expandFlag);expandFlag = -1;} else {explistview.collapseGroup(expandFlag);// 展开被选的groupexplistview.expandGroup(groupPosition);// 设置被选中的group置于顶端explistview.setSelectedGroup(groupPosition);expandFlag = groupPosition;}return true;}}
}

布局文件

child.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="40dip"android:background="#FFFFFF" ><ImageViewandroid:id="@+id/groupIcon"android:layout_width="40dp"android:layout_height="40dp"android:paddingLeft="10dp"android:src="@null" /><TextViewandroid:id="@+id/childto"android:layout_width="match_parent"android:layout_height="match_parent"android:paddingLeft="10dp"android:paddingTop="10dip"android:textColor="#000000"android:textSize="16sp" /></LinearLayout>

group_head.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal" android:background="#B8E6FA"android:gravity="center_vertical"><ImageView android:id="@+id/groupIcon"android:layout_width="50dp"android:layout_height="50dp"android:contentDescription="@null"android:layout_marginLeft="10dp"android:src="@drawable/btn_browser2"/><TextViewandroid:id="@+id/groupto"android:layout_width="match_parent"android:layout_height="50dp"android:textColor="#000000"android:textSize="18sp" android:gravity="center_vertical|left"/></LinearLayout>

group.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal" android:background="#B8E6FA"android:gravity="center_vertical"><ImageView android:id="@+id/groupIcon"android:layout_width="50dp"android:layout_height="50dp"android:contentDescription="@null"android:layout_marginLeft="10dp"android:src="@drawable/btn_browser"/><TextViewandroid:id="@+id/groupto"android:layout_width="match_parent"android:layout_height="50dp"android:textColor="#000000"android:textSize="18sp" android:gravity="center_vertical|left"/>
</LinearLayout>

layout_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#F0F0F0"android:orientation="vertical" ><com.xiaos.view.PinnedHeaderExpandableListViewandroid:id="@+id/explistview"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginLeft="0.0dip"android:cacheColorHint="#00000000"android:choiceMode="singleChoice"android:drawSelectorOnTop="false"android:fastScrollEnabled="false"android:footerDividersEnabled="true"android:groupIndicator="@null"android:scrollbars="vertical"android:scrollingCache="true" /></LinearLayout>

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.xiaos.pinnedheaderexpandable"android:versionCode="1"android:versionName="1.0" ><uses-sdkandroid:minSdkVersion="8"android:targetSdkVersion="19" /><applicationandroid:allowBackup="true"android:icon="@drawable/ic_launcher"android:label="@string/app_name"android:theme="@style/AppTheme" ><activity android:name=".MainActivity"android:icon="@drawable/ic_launcher"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity></application></manifest>

两张图片:

     

下载地址: http://download.csdn.net/detail/h7870181/8073677

Android UI视图效果篇之仿QQ好友列表分组悬浮PinnedHeaderExpandableListView相关推荐

  1. android 仿qq好友动态,Android UI仿QQ好友列表分组悬浮效果

    本文实例为大家分享了Android UI仿QQ好友列表分组悬浮效果的具体代码,供大家参考,具体内容如下 楼主是在平板上測试的.图片略微有点大,大家看看效果就好 接下来贴源代码: PinnedHeade ...

  2. android 仿qq好友列表分组效果及联系人分组效果

     历史记录仿QQ好友列表的动态效果 以及联系人的分组效果 QQ朋友分组的功能做的不错,大家都很认可,那么到底他的分组并且滑动的时候,标题能停留在顶部是如何实现的呢?今天从网上搜索了一下资料,自己运行了 ...

  3. 仿QQ好友列表分组折叠效果

    最近要一个类似QQ好友列表分组折叠效果,经过网友提醒应该使用ExpandableListView,因为其就集成了这个功能,我到网上随便找了文章一看,果然如此,因为工作需要和兴趣的推动,下班做完事后决定 ...

  4. Android仿QQ好友列表分组实现增删改及持久化

    Android自带的控件ExpandableListView实现了分组列表功能,本案例在此基础上进行优化,为此控件添加增删改分组及子项的功能,以及列表数据的持久化. Demo实现效果:     Dem ...

  5. java如何实现qq分组_Android仿QQ好友列表分组实现增删改及持久化

    Android自带的控件ExpandableListView实现了分组列表功能,本案例在此基础上进行优化,为此控件添加增删改分组及子项的功能,以及列表数据的持久化. Demo实现效果: GroupLi ...

  6. iOS之仿QQ好友列表展开收缩效果的实现

    使用UICollectionView实现 思路 很明显整体它是一个列表,它的分组是一个列表,它里面的好友列表也是一个列表,所以就可以使用组头来设置分组列表,使用cell设置好友列表: 当点击组头的时候 ...

  7. html仿qq最小化怎么实现,JS仿QQ好友列表展开、收缩功能(第一篇)

    JS仿QQ好友列表展开.收缩功能(第一篇) 发布时间:2020-10-17 14:20:03 来源:脚本之家 阅读:96 作者:erdouzhang 效果图如下所示: html: 我的好友 张三 李四 ...

  8. qq列表展开多个html代码,JS仿QQ好友列表展开、收缩功能(第二篇)

    在上篇的基础上继续完善,点击一个li元素,其他li元素上的类名清除掉. 效果图如下所示: js: window.onload = function(){ var list = document.get ...

  9. 仿QQ好友列表,QListWidget!

    仿QQ好友列表, 设计逻辑: 设计qqItem类,再添加到widget中: 设计时布局等可以直接在ui中设计:内容设计通过代码实现: qqItem.cpp #include "qqitem. ...

最新文章

  1. java8中的时间处理6 - 格式化
  2. 2018-3-10论文(网络评论中非结构化信息表示与研究)笔记-----网评评定等级,网评分类,网评信度函数,Dempster法则
  3. python中fib什么意思_Python斐波那契数列是什么?怎么用?
  4. SQL盲注之时间注入
  5. C#代码与javaScript函数的相互调用
  6. C++中的指针特征操作符重载
  7. join和group by能一块用吗_冷冻一下地球能缓解温室效应吗?| No.171
  8. Spring中的ModelAndView
  9. 搜索框中“请输入搜索keyword”
  10. C++游戏服务器框架笔记(二)_封装Socket类
  11. 内卷到底是什么意思?
  12. [转帖] 一个老乞丐的一句话,震惊全中国人!
  13. 【DDNS更新】--公云的DDNS自动更新
  14. windows使用scp远程传输文件的方法
  15. c语言程序执行不了,为什么C语言程序中if和else后的语句执行不了?
  16. IE低版本提示下载新的浏览器js--IEOutTips.zip
  17. 隔直电容大小如何选择
  18. python sendkeys用法_selenium sendkeys方法总结
  19. 用 Python 分析微信群聊记录,是怎样一种体验?
  20. 虚拟主机火云服务器区别,太一云大日如来陆压是什么来历在佛门担任什么职务...

热门文章

  1. 自动拖取win10聚焦壁纸到桌面
  2. Proteus8.9 VSM Studio Keil编译器仿真AT89C51RD2系列018_lcd12864竖屏
  3. Excel技巧:怎么比较两列文本
  4. 使用原生js实现歌曲上一曲/下一曲
  5. 洛谷——P2067 Cytus-Holyknight
  6. FBX文件导入Unity导致贴图丢失问题解决,以3ds max,Blender导入导出为例
  7. python 多进程 提高运行效率
  8. sqlserver2008r2提示—应用程序的组件中发生了无法处理的异常。如果单击继续,应用程序将忽略次错误并尝试继续
  9. UE-Vs转Rider for UE
  10. 锐捷网络 Java开发工程师校招 一面面经