用ExpandableListView实现类似QQ好友列表

简介:
 

ExpandableListView是一个用来显示二级节点的listview。

qq好友列表中子列表上下移动时,父节点在顶端会始终显示,这里我们可以自定义一个view来充当这个父节点。

 

主布局文件qq_listview如下,其中当我们拖动列表时,系统默认拖动过程中列表背景是黑的,我们可以通过android:cacheColorHint="#00000000"将其设置为透明,其中前两位是透明效果参数(00-99),后六位是颜色的设置。

 

Xml代码
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.   xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   android:layout_width="fill_parent"  
  5.   android:layout_height="wrap_content"  
  6.   android:orientation="vertical"  
  7.   android:background="@drawable/default_bg_hdpi">  
  8.   <LinearLayout  
  9.      android:id="@+id/gone_linear"  
  10.      android:layout_width="fill_parent"  
  11.      android:layout_height="wrap_content"  
  12.      android:background="@drawable/expand_column_bg"  
  13.      android:visibility="gone"  
  14.      android:cacheColorHint="#50000000"  
  15.   >  
  16.   <ImageView android:id="@+id/qq_list_imageview"    
  17.     android:layout_width="wrap_content"  android:layout_height="30dip"    
  18.     android:src="@drawable/narrow_select" />  
  19.   <TextView android:id="@+id/qq_list_textview"   android:layout_marginLeft="50dip"  
  20.     android:layout_width="fill_parent" android:layout_height="wrap_content" />  
  21.     </LinearLayout>  
  22.     <FrameLayout android:layout_width="fill_parent"  
  23.         android:layout_height="fill_parent">  
  24.   <ExpandableListView android:id="@+id/qq_listview"  
  25.      android:cacheColorHint="#00000000"  
  26.     android:layout_width="fill_parent" android:layout_height="wrap_content" />  
  27.     </FrameLayout>  
  28. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:orientation="vertical"
  android:background="@drawable/default_bg_hdpi">
  <LinearLayout
  	 android:id="@+id/gone_linear"
 	 android:layout_width="fill_parent"
 	 android:layout_height="wrap_content"
 	 android:background="@drawable/expand_column_bg"
 	 android:visibility="gone"
 	 android:cacheColorHint="#50000000"
  >
  <ImageView android:id="@+id/qq_list_imageview" 
  	android:layout_width="wrap_content"  android:layout_height="30dip" 
  	android:src="@drawable/narrow_select" />
  <TextView android:id="@+id/qq_list_textview"   android:layout_marginLeft="50dip"
  	android:layout_width="fill_parent" android:layout_height="wrap_content" />
  	</LinearLayout>
	<FrameLayout android:layout_width="fill_parent"
		android:layout_height="fill_parent">
  <ExpandableListView android:id="@+id/qq_listview"
 	 android:cacheColorHint="#00000000"
  	android:layout_width="fill_parent" android:layout_height="wrap_content" />
  	</FrameLayout>
</LinearLayout>

 

如果我们想更换父节点打开和关闭时的箭头,可以先设置一个selector.xml

 

Java代码
  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">   
  3.     <item android:state_window_focused="false" android:drawable="@drawable/expand_column_bg_over" />   
  4.     <item android:state_pressed="true" android:drawable="@drawable/expand_column_bg" />   
  5.     <item android:state_pressed="false" android:drawable="@drawable/feedlistdividerbg"></item>   
  6. </selector>  
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
	<item android:state_window_focused="false" android:drawable="@drawable/expand_column_bg_over" />
	<item android:state_pressed="true" android:drawable="@drawable/expand_column_bg" />
	<item android:state_pressed="false" android:drawable="@drawable/feedlistdividerbg"></item>
</selector>

 然后在代码中调用

 

Java代码
  1. elistview = (ExpandableListView)findViewById(R.id.qq_listview);   
  2. //替换ExpandableListView的打开关闭时的箭头图标   
  3. elistview.setGroupIndicator(this.getResources().getDrawable(R.drawable.expand_list_selector));  
		elistview = (ExpandableListView)findViewById(R.id.qq_listview);
		//替换ExpandableListView的打开关闭时的箭头图标
		elistview.setGroupIndicator(this.getResources().getDrawable(R.drawable.expand_list_selector));

 

此外,我们还要设置父节点和子节点item的布局文件

父节点qq_list_parent.xml如下

 

Java代码
  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <LinearLayout   
  3.   xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   android:layout_width="fill_parent"  
  5.   android:layout_height="fill_parent"      
  6.   android:background="@drawable/expand_column_bg">   
  7.   <TextView android:id="@+id/parend"  
  8.     android:layout_width="wrap_content"    
  9.     android:layout_height="30dip"  
  10.     android:layout_marginLeft="50dip"  
  11.      />   
  12. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" 	
  android:background="@drawable/expand_column_bg">
  <TextView android:id="@+id/parend"
  	android:layout_width="wrap_content" 
  	android:layout_height="30dip"
  	android:layout_marginLeft="50dip"
  	 />
</LinearLayout>

 

子节点qq_listview_child.xml

 

Java代码
  1. <?xml version="1.0" encoding="utf-8"?>   
  2. <LinearLayout   
  3.   xmlns:android="http://schemas.android.com/apk/res/android"  
  4.   android:layout_width="fill_parent"  
  5.   android:layout_height="fill_parent"  
  6.   android:cacheColorHint="#00000000"  
  7.  >   
  8.     <TextView android:id="@+id/child"  
  9.         android:layout_width="wrap_content" android:layout_height="40dip"  
  10.         android:layout_marginLeft="80dip"  
  11.         />   
  12. </LinearLayout>  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:cacheColorHint="#00000000"
 >
    <TextView android:id="@+id/child"
  		android:layout_width="wrap_content" android:layout_height="40dip"
  		android:layout_marginLeft="80dip"
  		/>
</LinearLayout>

 

java代码如下

 

Java代码
  1. package com.sy.android.qqlistview;   
  2.   
  3. import java.util.ArrayList;   
  4. import java.util.HashMap;   
  5. import java.util.Map;   
  6.   
  7. import android.app.Activity;   
  8. import android.os.Bundle;   
  9. import android.view.View;   
  10. import android.view.View.OnClickListener;   
  11. import android.widget.AbsListView;   
  12. import android.widget.AbsListView.OnScrollListener;   
  13. import android.widget.ExpandableListView;   
  14. import android.widget.ExpandableListView.OnGroupCollapseListener;   
  15. import android.widget.ExpandableListView.OnGroupExpandListener;   
  16. import android.widget.LinearLayout;   
  17. import android.widget.SimpleExpandableListAdapter;   
  18. import android.widget.TextView;   
  19.   
  20. import com.sy.android.testAndroid.R;   
  21.   
  22. public class QQListView extends Activity   {   
  23.        
  24.     private static ArrayList<Map<String,String>> parentData = new ArrayList<Map<String,String>>();   
  25.     private static ArrayList<ArrayList<Map<String,String>>> childData = new ArrayList<ArrayList<Map<String,String>>>();   
  26.     private ExpandableListView elistview;   
  27.     private TextView tv;   
  28.        
  29.     /**  
  30.      *当前打开的父节点  
  31.      */  
  32.     private int the_group_expand_position=-1;   
  33.     /**  
  34.      * 打开的父节点所与的子节点数  
  35.      */  
  36.     private int position_child_count=0;   
  37.     /**  
  38.      * 是否有打开的父节点  
  39.      */  
  40.     private boolean isExpanding=false;   
  41.        
  42.     public void getData(){   
  43.         for(int i=0; i<20;i++){   
  44.              Map<String,String> map = new HashMap<String,String>();   
  45.              map.put("parend", i+"");   
  46.              parentData.add(map);   
  47.         }   
  48.         for(int i=0;i<20;i++){   
  49.             ArrayList<Map<String,String>> child = new ArrayList<Map<String,String>>();   
  50.             for(int j=0; j<20;j++){   
  51.                 Map<String,String> map = new HashMap<String,String>();   
  52.                 map.put("child", i+""+j);   
  53.                 child.add(map);   
  54.             }   
  55.             childData.add(child);   
  56.         }   
  57.     }   
  58.        
  59.     public void onCreate(Bundle saveBundle){   
  60.         super.onCreate(saveBundle);   
  61.         setContentView(R.layout.qq_listview);   
  62.            
  63.         elistview = (ExpandableListView)findViewById(R.id.qq_listview);   
  64.         //替换ExpandableListView的打开关闭时的箭头图标   
  65.         elistview.setGroupIndicator(this.getResources().getDrawable(R.drawable.expand_list_selector));   
  66.         tv = (TextView)findViewById(R.id.qq_list_textview);   
  67.            
  68.         /**  
  69.          * 滑动子列表时在上方显示父节点的view  
  70.          */  
  71.         final LinearLayout linear = (LinearLayout)findViewById(R.id.gone_linear);   
  72.            
  73.         /**  
  74.          * 监听父节点打开的事件  
  75.          */  
  76.         elistview.setOnGroupExpandListener(new OnGroupExpandListener(){   
  77.   
  78.             @Override  
  79.             public void onGroupExpand(int groupPosition) {   
  80.                 the_group_expand_position=groupPosition;   
  81.                 position_child_count=childData.get(groupPosition).size();   
  82.                 isExpanding=true;   
  83.             }   
  84.                
  85.         });   
  86.            
  87.         /**  
  88.          * 监听父节点关闭的事件  
  89.          */  
  90.         elistview.setOnGroupCollapseListener(new OnGroupCollapseListener(){   
  91.   
  92.             @Override  
  93.             public void onGroupCollapse(int groupPosition) {   
  94.                 if(linear.getVisibility()==View.VISIBLE){   
  95.                     linear.setVisibility(View.GONE);   
  96.                 }   
  97.                 isExpanding=false;   
  98.             }   
  99.                
  100.         });   
  101.            
  102.   
  103.         linear.setOnClickListener(new OnClickListener(){   
  104.   
  105.             @Override  
  106.             public void onClick(View v) {   
  107.                 linear.setVisibility(View.GONE);   
  108.                 elistview.collapseGroup(the_group_expand_position);   
  109.             }   
  110.                
  111.         });   
  112.            
  113.         /**  
  114.          * 通过setOnScrollListener来监听列表上下滑动时item显示和消失的事件  
  115.          */  
  116.            
  117.         elistview.setOnScrollListener(new OnScrollListener(){   
  118.   
  119.             @Override  
  120.             public void onScrollStateChanged(AbsListView view, int scrollState) {   
  121.                    
  122.             }   
  123.   
  124.             @Override  
  125.             public void onScroll(AbsListView view, int firstVisibleItem,   
  126.                     int visibleItemCount, int totalItemCount) {   
  127.                 if(isExpanding){   
  128.                     // 当当前第一个item id小于打开的父节点id 或大于打开的父节点id和它的子节点总数之和时   
  129.                     if(firstVisibleItem<the_group_expand_position ||   
  130.                             firstVisibleItem>(the_group_expand_position+position_child_count)){   
  131.                         linear.setVisibility(View.GONE);   
  132.                     }else{   
  133.                         linear.setVisibility(View.VISIBLE);   
  134.                         tv.setText(((Map)parentData.get(the_group_expand_position)).get("parend").toString());   
  135.                     }   
  136.                 }   
  137.             }   
  138.                
  139.         });   
  140.            
  141.         getData();   
  142.            
  143.         SimpleExpandableListAdapter selAdapter = new SimpleExpandableListAdapter(this,   
  144.                 parentData,   
  145.                 R.layout.qq_listview_parend_item,   
  146.                 new String[]{"parend"},   
  147.                 new int[]{R.id.parend},   
  148.                 childData,   
  149.                 R.layout.qq_liatview_child_item,   
  150.                 new String[]{"child"},   
  151.                 new int[]{R.id.child}   
  152.                 );   
  153.         elistview.setAdapter(selAdapter);   
  154.   
  155.     }   
  156.   
  157. }  
package com.sy.android.qqlistview;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnGroupCollapseListener;
import android.widget.ExpandableListView.OnGroupExpandListener;
import android.widget.LinearLayout;
import android.widget.SimpleExpandableListAdapter;
import android.widget.TextView;

import com.sy.android.testAndroid.R;

public class QQListView extends Activity   {
	
	private static ArrayList<Map<String,String>> parentData = new ArrayList<Map<String,String>>();
	private static ArrayList<ArrayList<Map<String,String>>> childData = new ArrayList<ArrayList<Map<String,String>>>();
	private ExpandableListView elistview;
	private TextView tv;
	
	/**
	 *当前打开的父节点
	 */
	private int the_group_expand_position=-1;
	/**
	 * 打开的父节点所与的子节点数
	 */
	private int position_child_count=0;
	/**
	 * 是否有打开的父节点
	 */
	private boolean isExpanding=false;
	
	public void getData(){
		for(int i=0; i<20;i++){
			 Map<String,String> map = new HashMap<String,String>();
			 map.put("parend", i+"");
			 parentData.add(map);
		}
		for(int i=0;i<20;i++){
			ArrayList<Map<String,String>> child = new ArrayList<Map<String,String>>();
			for(int j=0; j<20;j++){
				Map<String,String> map = new HashMap<String,String>();
				map.put("child", i+""+j);
				child.add(map);
			}
			childData.add(child);
		}
	}
	
	public void onCreate(Bundle saveBundle){
		super.onCreate(saveBundle);
		setContentView(R.layout.qq_listview);
		
		elistview = (ExpandableListView)findViewById(R.id.qq_listview);
		//替换ExpandableListView的打开关闭时的箭头图标
		elistview.setGroupIndicator(this.getResources().getDrawable(R.drawable.expand_list_selector));
		tv = (TextView)findViewById(R.id.qq_list_textview);
		
		/**
		 * 滑动子列表时在上方显示父节点的view
		 */
		final LinearLayout linear = (LinearLayout)findViewById(R.id.gone_linear);
		
		/**
		 * 监听父节点打开的事件
		 */
		elistview.setOnGroupExpandListener(new OnGroupExpandListener(){

			@Override
			public void onGroupExpand(int groupPosition) {
				the_group_expand_position=groupPosition;
				position_child_count=childData.get(groupPosition).size();
				isExpanding=true;
			}
			
		});
		
		/**
		 * 监听父节点关闭的事件
		 */
		elistview.setOnGroupCollapseListener(new OnGroupCollapseListener(){

			@Override
			public void onGroupCollapse(int groupPosition) {
				if(linear.getVisibility()==View.VISIBLE){
					linear.setVisibility(View.GONE);
				}
				isExpanding=false;
			}
			
		});
		

		linear.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				linear.setVisibility(View.GONE);
				elistview.collapseGroup(the_group_expand_position);
			}
			
		});
		
		/**
		 * 通过setOnScrollListener来监听列表上下滑动时item显示和消失的事件
		 */
		
		elistview.setOnScrollListener(new OnScrollListener(){

			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				
			}

			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				if(isExpanding){
					// 当当前第一个item id小于打开的父节点id 或大于打开的父节点id和它的子节点总数之和时
					if(firstVisibleItem<the_group_expand_position ||
							firstVisibleItem>(the_group_expand_position+position_child_count)){
						linear.setVisibility(View.GONE);
					}else{
						linear.setVisibility(View.VISIBLE);
						tv.setText(((Map)parentData.get(the_group_expand_position)).get("parend").toString());
					}
				}
			}
			
		});
		
		getData();
		
		SimpleExpandableListAdapter selAdapter = new SimpleExpandableListAdapter(this,
				parentData,
				R.layout.qq_listview_parend_item,
				new String[]{"parend"},
				new int[]{R.id.parend},
				childData,
				R.layout.qq_liatview_child_item,
				new String[]{"child"},
				new int[]{R.id.child}
				);
		elistview.setAdapter(selAdapter);

	}

}

 

 

实现的思路是通过setOnScrollListener来监听listview,从而获得显示在视图中的item的id,通过id的判断来决定显示在顶端的自定义的view是否显示

相关文章
|
10月前
|
数据可视化 数据挖掘 BI
团队管理者必读:高效看板类协同软件的功能解析
在现代职场中,团队协作的效率直接影响项目成败。看板类协同软件通过可视化界面,帮助团队清晰规划任务、追踪进度,提高协作效率。本文介绍看板类软件的优势,并推荐五款优质工具:板栗看板、Trello、Monday.com、ClickUp 和 Asana,助力团队实现高效管理。
195 2
|
8月前
|
监控 前端开发 数据可视化
React音频进度条组件开发全攻略
本文介绍了音频播放器的实现与优化,涵盖基础功能、进阶交互、问题诊断及企业级增强方案。首先通过绑定音频元素和进度条展示核心逻辑,解决状态循环更新和除零错误等典型问题。接着实现拖拽定位、缓冲加载等功能,处理移动端兼容性和性能优化。针对时间不同步、内存泄漏等问题提出解决方案,并介绍异步状态管理和内存防护策略。最后探讨键盘导航、可视化扩展等企业级特性,总结最佳实践,包括状态隔离、性能监控、无障碍支持及测试方案,建议使用TypeScript和Storybook提升开发效率和类型安全性。
177 30
React音频进度条组件开发全攻略
|
测试技术 API 持续交付
微服务的版本控制
微服务的版本控制
147 6
|
9月前
|
编解码 安全 网络安全
指纹浏览器中HTTP代理IP的重要性及使用原因
随着数字化发展,网络安全和隐私保护成为核心需求,指纹浏览器应运而生。它通过客户端信息唯一标识用户身份。搭配HTTP代理IP可增强安全性,具体表现为:1. 保护用户地址;2. 提高信息安全;3. 防止指纹检测;4. 增强网络安全。HTTP代理以其稳定性、安全性,在网络营销等领域发挥重要作用,二者结合为用户提供更强的隐私和安全保护。
289 34
|
8月前
|
人工智能 小程序 机器人
GDC2025 | DeepSeek-Qwen 模型蒸馏极限挑战赛,来了!(预赛报名)
欢迎您关注由魔搭社区 x SwanLab平台联合举办的 DeepSeek-Qwen 模型蒸馏极限挑战赛!本赛事将作为2025全球开发者先锋大会(GDC)的活动之一,欢迎具备大模型训练/微调实战经验的独立开发者前来挑战!详细报名规则见后文。
283 3
|
12月前
|
机器学习/深度学习 算法 固态存储
深度学习在图像识别中的应用与发展
本文将探讨深度学习在图像识别领域的应用与发展。通过分析深度学习的基本概念、常用模型以及具体案例,揭示其在图像识别中的重要性和未来趋势。我们将详细介绍卷积神经网络(CNN)的原理与结构,并展示如何利用深度学习进行图像分类、目标检测和图像分割等任务。同时,我们还将讨论当前面临的挑战及可能的解决途径。希望通过这篇文章,读者能够对深度学习在图像识别中的应用有一个全面而深入的了解。
|
自然语言处理 API 数据安全/隐私保护
InstaGraph:将文本和URL转化为知识图谱的强大工具
InstaGraph 可以将文本或 URL 转化为美观的知识图谱,帮助你更清晰地理解复杂主题中不同实体之间的关系,InstaGraph 的自然语言处理能力是由 OpenAI 的 GPT-3.5 技术提供支持的。
|
前端开发 API
【threejs教程】让你的场景动起来!深入了解threejs场景及坐标轴
【8月更文挑战第5天】让你的场景动起来!深入了解threejs场景及坐标轴
198 0
【threejs教程】让你的场景动起来!深入了解threejs场景及坐标轴
|
网络协议 网络架构
IP网络协议的深度讲解
IP协议作为互联网的基础,其设计的简洁性和强大功能使得全球范围内的网络通信成为可能。通过不断的发展和改进,尤其是IPv6的推广使用,IP协议将继续支持着日益增长的网络通信需求。了解IP协议的工作原理,对于任何网络技术的学习和应用都是基础且必要的。
327 0
|
前端开发 JavaScript 开发者
雪碧图:图片合并的艺术!
雪碧图:图片合并的艺术!