Android UI视图效果篇之仿QQ好友列表分组悬浮PinnedHeaderExpandableListView
楼主是在平板上測试的。图片略微有点大,大家看看效果就好
接下来贴源代码:
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 是画上去的 , 所以设置事件监听是无效的 , 仅仅有自行控制 .
*/
@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 = (HeaderAdapter) 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);
//Header自己主动置顶
//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.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());
}
} @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) {
}
}
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; @Override
protected 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;
}
}
//设置悬浮头部VIEW
explistview.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{
@Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
if (expandFlag == -1) {
// 展开被选的group
explistview.expandGroup(groupPosition);
// 设置被选中的group置于顶端
explistview.setSelectedGroup(groupPosition);
expandFlag = groupPosition;
} else if (expandFlag == groupPosition) {
explistview.collapseGroup(expandFlag);
expandFlag = -1;
} else {
explistview.collapseGroup(expandFlag);
// 展开被选的group
explistview.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" > <ImageView
android:id="@+id/groupIcon"
android:layout_width="40dp"
android:layout_height="40dp"
android:paddingLeft="10dp"
android:src="@null" /> <TextView
android: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"/>
<TextView
android: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"/>
<TextView
android: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.PinnedHeaderExpandableListView
android: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-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" /> <application
android: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>
两张图片:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvaDc4NzAxODE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
Android UI视图效果篇之仿QQ好友列表分组悬浮PinnedHeaderExpandableListView的更多相关文章
- 仿QQ好友列表界面的实现
TableView有2种style:UITableViewStylePlain 和 UITableViewStyleGrouped. 但是QQ好友列表的tableView给人的感觉似乎是2个style ...
- ExpandableListView仿QQ好友列表
本例中,对ExpandableListView中的数据进行了封装,分为两个JavaBean,一个为Group类表示组信息,一个Child类表示该组下子列表信息: Group: public class ...
- Objective-c——UI基础开发第九天(QQ好友列表)
一.知识点: 1.双模型的嵌套使用 2.Button的对齐方式 3.优化UITableView的加载 4.layoutSubview的使用 5.cell的折叠代理 二.双模型的嵌套定义: 注意是将se ...
- android 实现QQ好友列表
在某些Android开发群里,看到有些新手问怎么实现QQ好友列表,其实网上一搜挺多的.接触Android,也才一年的时间,大部分时间花在工作上(解bug...),界面上开发很少参与.自己维护的系统应用 ...
- iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一)
iOS开发UI篇—使用UItableview完成一个简单的QQ好友列表(一) 一.项目结构和plist文件 二.实现代码 1.说明: 主控制器直接继承UITableViewController // ...
- Android特效专辑(六)——仿QQ聊天撒花特效,无形装逼,最为致命
Android特效专辑(六)--仿QQ聊天撒花特效,无形装逼,最为致命 我的关于特效的专辑已经在CSDN上申请了一个专栏--http://blog.csdn.net/column/details/li ...
- 动手分析安卓仿QQ联系人列表TreeView控件
因项目需要需要用到仿QQ联系人列表的控件样式,于是网上找到一个轮子(https://github.com/TealerProg/TreeView),工作完成现在简单分析一下这个源码. 一. 需要用 ...
- WPF 自定义TreeView控件样式,仿QQ联系人列表
一.前言 TreeView控件在项目中使用比较频繁,普通的TreeView并不能满足我们的需求.因此我们需要滴对TreeView进行改造.下面的内容将介绍仿QQ联系人TreeView样式及TreeVi ...
- 基于Qt的相似QQ好友列表抽屉效果的实现
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/shuideyidi/article/details/30619167 前段时间在忙毕业设计, ...
随机推荐
- Mac和PC在工作中管理的对比(5)
ACLs, 企业应用必备 Windows: ACLs是Access Control Lists的缩写, 存取控制列表/访问控制列表. 它已经在大多数的现代操作系统中得到支持. 系统安全就是通过ACLs ...
- Oracle 删除表中记录 如何释放表及表空间大小
1.查看一个表所占的空间大小:SELECT bytes/1024/1024 ||'MB' TABLE_SIZE ,u.* FROM USER_SEGMENTS U WHERE U.SEGMENT_NA ...
- 11个强大的 Visual Studio 调试技能
简介 调试是软件开辟周期中很首要的一项目组.它具有挑衅性,同时也很让人困惑和懊恼.总的来说,对于稍大一点的法度,调试是不成避免的.比来几年,调试对象的成长让很多调试任务变的越来越简单和省时. 这篇文章 ...
- fread与read的差别(文件io补充)
这里有一个我们常常提出的问题就是fread,read的差别.(当然这两个分别代表了操作文件系统的两套不同的函数,包含open,read, write, seek 等). 一.他们的差别就是一个(rea ...
- php 5.3 垃圾回收
1.引用计数器 php中的每个变量都存在一个zval的变量容器中, zval容易包括变量类型.值.is_ref(是否是引用).refercount(引用次数,也成为符号), 所有的符号存在一个符号表中 ...
- 启动ip wizard时报the ip wizard does not support dhcp
启动ip wizard时报the ip wizard does not support dhcp 阅读:5502012-05-11 11:15 标签:loadrunner 打开ip wizard:开始 ...
- 金山PDF
金山是个很不错的软件公司,金山出PDF,纯粹是完善生态圈!毕竟没FoxitReader专业对PDF的处理上! 官网:芝麻开门 下载:http://wdl1.cache.wps.cn/wps/downl ...
- 域名无法解析 Linux临时或永久修改DNS
最近给VPS重装了系统,因为服务商不提供DHCP,所以只好手动设置IP和DNS Server.悲催的是系统重装的时候忘记了输入DNS Server,最后导致进去系统后,各种域名无法解析. Linux中 ...
- 微信小程序-自定义底部导航
代码地址如下:http://www.demodashi.com/demo/14258.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...
- Spark GraphX 的数据可视化
概述 Spark GraphX 本身并不提供可视化的支持, 我们通过第三方库 GraphStream 和 Breeze 来实现这一目标 详细 代码下载:http://www.demodashi.com ...