Anroid ListView分组和悬浮Header实现
之前在使用iOS时,看到过一种分组的View,每一组都有一个Header,在上下滑动的时候,会有一个悬浮的Header,这种体验觉得很不错,请看下图:
上图中标红的1,2,3,4四张图中,当向上滑动时,仔细观察灰色条的Header变化,当第二组向上滑动时,会把第一组的悬浮Header挤上去。
这种效果在Android是没有的,iOS的SDK就自带这种效果。这篇文章就介绍如何在Android实现这种效果。
1、悬浮Header的实现
- /**
- * Adapter interface. The list adapter must implement this interface.
- */
- public interface PinnedHeaderAdapter {
- /**
- * Pinned header state: don't show the header.
- */
- public static final int PINNED_HEADER_GONE = 0;
- /**
- * Pinned header state: show the header at the top of the list.
- */
- public static final int PINNED_HEADER_VISIBLE = 1;
- /**
- * Pinned header state: show the header. If the header extends beyond
- * the bottom of the first shown element, push it up and clip.
- */
- public static final int PINNED_HEADER_PUSHED_UP = 2;
- /**
- * Computes the desired state of the pinned header for the given
- * position of the first visible list item. Allowed return values are
- * {@link #PINNED_HEADER_GONE}, {@link #PINNED_HEADER_VISIBLE} or
- * {@link #PINNED_HEADER_PUSHED_UP}.
- */
- int getPinnedHeaderState(int position);
- /**
- * Configures the pinned header view to match the first visible list item.
- *
- * @param header pinned header view.
- * @param position position of the first visible list item.
- * @param alpha fading of the header view, between 0 and 255.
- */
- void configurePinnedHeader(View header, int position, int alpha);
- }
1.2、如何绘制Header View
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- if (mHeaderViewVisible) {
- drawChild(canvas, mHeaderView, getDrawingTime());
- }
- }
1.3、配置Header View
- public void configureHeaderView(int position) {
- if (mHeaderView == null || null == mAdapter) {
- return;
- }
- int state = mAdapter.getPinnedHeaderState(position);
- switch (state) {
- case PinnedHeaderAdapter.PINNED_HEADER_GONE: {
- mHeaderViewVisible = false;
- break;
- }
- case PinnedHeaderAdapter.PINNED_HEADER_VISIBLE: {
- mAdapter.configurePinnedHeader(mHeaderView, position, MAX_ALPHA);
- if (mHeaderView.getTop() != 0) {
- mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
- }
- mHeaderViewVisible = true;
- break;
- }
- case PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP: {
- View firstView = getChildAt(0);
- int bottom = firstView.getBottom();
- int itemHeight = 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.configurePinnedHeader(mHeaderView, position, alpha);
- if (mHeaderView.getTop() != y) {
- mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y);
- }
- mHeaderViewVisible = true;
- break;
- }
- }
- }
1.4、onLayout和onMeasure
- @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();
- }
- }
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (mHeaderView != null) {
- mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
- configureHeaderView(getFirstVisiblePosition());
- }
- }
好了,到这里,悬浮Header View就完了,各位可能看不到完整的代码,只要明白这几个核心的方法,自己写出来,也差不多了。
2、ListView Section实现
3、Adapter的实现
- private class ListViewAdapter extends BaseAdapter implements PinnedHeaderAdapter {
- private ArrayList<Contact> mDatas;
- private static final int TYPE_CATEGORY_ITEM = 0;
- private static final int TYPE_ITEM = 1;
- public ListViewAdapter(ArrayList<Contact> datas) {
- mDatas = datas;
- }
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
- @Override
- public boolean isEnabled(int position) {
- // 异常情况处理
- if (null == mDatas || position < 0|| position > getCount()) {
- return true;
- }
- Contact item = mDatas.get(position);
- if (item.isSection) {
- return false;
- }
- return true;
- }
- @Override
- public int getCount() {
- return mDatas.size();
- }
- @Override
- public int getItemViewType(int position) {
- // 异常情况处理
- if (null == mDatas || position < 0|| position > getCount()) {
- return TYPE_ITEM;
- }
- Contact item = mDatas.get(position);
- if (item.isSection) {
- return TYPE_CATEGORY_ITEM;
- }
- return TYPE_ITEM;
- }
- @Override
- public int getViewTypeCount() {
- return 2;
- }
- @Override
- public Object getItem(int position) {
- return (position >= 0 && position < mDatas.size()) ? mDatas.get(position) : 0;
- }
- @Override
- public long getItemId(int position) {
- return 0;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- int itemViewType = getItemViewType(position);
- Contact data = (Contact) getItem(position);
- TextView itemView;
- switch (itemViewType) {
- case TYPE_ITEM:
- if (null == convertView) {
- itemView = new TextView(SectionListView.this);
- itemView.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- mItemHeight));
- itemView.setTextSize(16);
- itemView.setPadding(10, 0, 0, 0);
- itemView.setGravity(Gravity.CENTER_VERTICAL);
- //itemView.setBackgroundColor(Color.argb(255, 20, 20, 20));
- convertView = itemView;
- }
- itemView = (TextView) convertView;
- itemView.setText(data.toString());
- break;
- case TYPE_CATEGORY_ITEM:
- if (null == convertView) {
- convertView = getHeaderView();
- }
- itemView = (TextView) convertView;
- itemView.setText(data.toString());
- break;
- }
- return convertView;
- }
- @Override
- public int getPinnedHeaderState(int position) {
- if (position < 0) {
- return PINNED_HEADER_GONE;
- }
- Contact item = (Contact) getItem(position);
- Contact itemNext = (Contact) getItem(position + 1);
- boolean isSection = item.isSection;
- boolean isNextSection = (null != itemNext) ? itemNext.isSection : false;
- if (!isSection && isNextSection) {
- return PINNED_HEADER_PUSHED_UP;
- }
- return PINNED_HEADER_VISIBLE;
- }
- @Override
- public void configurePinnedHeader(View header, int position, int alpha) {
- Contact item = (Contact) getItem(position);
- if (null != item) {
- if (header instanceof TextView) {
- ((TextView) header).setText(item.sectionStr);
- }
- }
- }
- }
数据结构Contact的定义如下:
- public class Contact {
- int id;
- String name;
- String pinyin;
- String sortLetter = "#";
- String sectionStr;
- String phoneNumber;
- boolean isSection;
- static CharacterParser sParser = CharacterParser.getInstance();
- Contact() {
- }
- Contact(int id, String name) {
- this.id = id;
- this.name = name;
- this.pinyin = sParser.getSpelling(name);
- if (!TextUtils.isEmpty(pinyin)) {
- String sortString = this.pinyin.substring(0, 1).toUpperCase();
- if (sortString.matches("[A-Z]")) {
- this.sortLetter = sortString.toUpperCase();
- } else {
- this.sortLetter = "#";
- }
- }
- }
- @Override
- public String toString() {
- if (isSection) {
- return name;
- } else {
- //return name + " (" + sortLetter + ", " + pinyin + ")";
- return name + " (" + phoneNumber + ")";
- }
- }
- }
完整的代码
- package com.lee.sdk.test.section;
- import java.util.ArrayList;
- import android.graphics.Color;
- import android.os.Bundle;
- import android.view.Gravity;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.AbsListView;
- import android.widget.AdapterView;
- import android.widget.AdapterView.OnItemClickListener;
- import android.widget.BaseAdapter;
- import android.widget.TextView;
- import android.widget.Toast;
- import com.lee.sdk.test.GABaseActivity;
- import com.lee.sdk.test.R;
- import com.lee.sdk.widget.PinnedHeaderListView;
- import com.lee.sdk.widget.PinnedHeaderListView.PinnedHeaderAdapter;
- public class SectionListView extends GABaseActivity {
- private int mItemHeight = 55;
- private int mSecHeight = 25;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- float density = getResources().getDisplayMetrics().density;
- mItemHeight = (int) (density * mItemHeight);
- mSecHeight = (int) (density * mSecHeight);
- PinnedHeaderListView mListView = new PinnedHeaderListView(this);
- mListView.setAdapter(new ListViewAdapter(ContactLoader.getInstance().getContacts(this)));
- mListView.setPinnedHeaderView(getHeaderView());
- mListView.setBackgroundColor(Color.argb(255, 20, 20, 20));
- mListView.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- ListViewAdapter adapter = ((ListViewAdapter) parent.getAdapter());
- Contact data = (Contact) adapter.getItem(position);
- Toast.makeText(SectionListView.this, data.toString(), Toast.LENGTH_SHORT).show();
- }
- });
- setContentView(mListView);
- }
- private View getHeaderView() {
- TextView itemView = new TextView(SectionListView.this);
- itemView.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- mSecHeight));
- itemView.setGravity(Gravity.CENTER_VERTICAL);
- itemView.setBackgroundColor(Color.WHITE);
- itemView.setTextSize(20);
- itemView.setTextColor(Color.GRAY);
- itemView.setBackgroundResource(R.drawable.section_listview_header_bg);
- itemView.setPadding(10, 0, 0, itemView.getPaddingBottom());
- return itemView;
- }
- private class ListViewAdapter extends BaseAdapter implements PinnedHeaderAdapter {
- private ArrayList<Contact> mDatas;
- private static final int TYPE_CATEGORY_ITEM = 0;
- private static final int TYPE_ITEM = 1;
- public ListViewAdapter(ArrayList<Contact> datas) {
- mDatas = datas;
- }
- @Override
- public boolean areAllItemsEnabled() {
- return false;
- }
- @Override
- public boolean isEnabled(int position) {
- // 异常情况处理
- if (null == mDatas || position < 0|| position > getCount()) {
- return true;
- }
- Contact item = mDatas.get(position);
- if (item.isSection) {
- return false;
- }
- return true;
- }
- @Override
- public int getCount() {
- return mDatas.size();
- }
- @Override
- public int getItemViewType(int position) {
- // 异常情况处理
- if (null == mDatas || position < 0|| position > getCount()) {
- return TYPE_ITEM;
- }
- Contact item = mDatas.get(position);
- if (item.isSection) {
- return TYPE_CATEGORY_ITEM;
- }
- return TYPE_ITEM;
- }
- @Override
- public int getViewTypeCount() {
- return 2;
- }
- @Override
- public Object getItem(int position) {
- return (position >= 0 && position < mDatas.size()) ? mDatas.get(position) : 0;
- }
- @Override
- public long getItemId(int position) {
- return 0;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- int itemViewType = getItemViewType(position);
- Contact data = (Contact) getItem(position);
- TextView itemView;
- switch (itemViewType) {
- case TYPE_ITEM:
- if (null == convertView) {
- itemView = new TextView(SectionListView.this);
- itemView.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- mItemHeight));
- itemView.setTextSize(16);
- itemView.setPadding(10, 0, 0, 0);
- itemView.setGravity(Gravity.CENTER_VERTICAL);
- //itemView.setBackgroundColor(Color.argb(255, 20, 20, 20));
- convertView = itemView;
- }
- itemView = (TextView) convertView;
- itemView.setText(data.toString());
- break;
- case TYPE_CATEGORY_ITEM:
- if (null == convertView) {
- convertView = getHeaderView();
- }
- itemView = (TextView) convertView;
- itemView.setText(data.toString());
- break;
- }
- return convertView;
- }
- @Override
- public int getPinnedHeaderState(int position) {
- if (position < 0) {
- return PINNED_HEADER_GONE;
- }
- Contact item = (Contact) getItem(position);
- Contact itemNext = (Contact) getItem(position + 1);
- boolean isSection = item.isSection;
- boolean isNextSection = (null != itemNext) ? itemNext.isSection : false;
- if (!isSection && isNextSection) {
- return PINNED_HEADER_PUSHED_UP;
- }
- return PINNED_HEADER_VISIBLE;
- }
- @Override
- public void configurePinnedHeader(View header, int position, int alpha) {
- Contact item = (Contact) getItem(position);
- if (null != item) {
- if (header instanceof TextView) {
- ((TextView) header).setText(item.sectionStr);
- }
- }
- }
- }
- }
关于数据加载,分组的逻辑这里就不列出了,数据分组请参考:
Anroid ListView分组和悬浮Header实现的更多相关文章
- Android 自定义组件之 带有悬浮header的listview
最近做项目遇到一个需求,要做一个带有悬浮header的listview,即,当listview滑动时,header消失,静止时header浮现. 这个需求看似简单,实际做起来还是会遇到不少的困难,特此 ...
- WPF:ListView 分组合并
CollectionViewSource 绑定的是从数据库取出的数据ListBind 以DeptName为分组依据 <Window.Resources> <CollectionVie ...
- WPF ListView 使用GridView 带有Header 以及点击header排序 sort
ListView: <ListView x:Name="lvFiles" VerticalAlignment="Stretch" Background=& ...
- WPF ListView 分组 Grouping
在Resource里定义数据源和分组字段: <CollectionViewSource x:Key="listData" Source="{Binding Cate ...
- Android ListView分组显示
ListView的实现方法也是普通的实现方法.只不过在list列表中加入groupkey信息.在渲染的时候要判断是否是分组的标题. 就是在使用不同的两个View的时候存在这种情况,convertVie ...
- 移动端解决悬浮层(悬浮header、footer)会遮挡住内容的方法
固定Footer Bootstrap框架提供了两种固定导航条的方式: ☑ .navbar-fixed-top:导航条固定在浏览器窗口顶部 ☑ .navbar-fixed-bottom:导航条固定在 ...
- 仿照支付宝账单界面--listview分组显示 用来做!发!财树充值交易明细
QQ图片20150430155638.png (151.65 KB, 下载次数: 32) 下载链接: http://pan.baidu.com/s/1kVMY1SV 密码: i8ta
- 收藏的技术文章链接(ubuntu,python,android等)
我的收藏 他山之石,可以攻玉 转载请注明出处:https://ahangchen.gitbooks.io/windy-afternoon/content/ 开发过程中收藏在Chrome书签栏里的技术文 ...
- React-Native 常用组件学习资料链接
以下链接是自己开发RN工程时参考的一些不错的资料,给喜欢学习的朋友分享以下. React-Native组件用法详解之ListViewhttp://www.jianshu.com/p/1293bb8ac ...
随机推荐
- ZUFE OJ 2289 God Wang II
Description 这个世界太无聊了,于是God Wang想出了新的运算符号$,对于两个数x,y来说x$y的值等于x和y各个位置上的数字乘积之和,没有的位按0来算 比如说123$321=1*3+2 ...
- ios给textView提价提示性文字
不推荐使用的方式 但是在用的时候才发现原来textView没有类似于textField的那种placeholder功能.所谓placeholder就比如用户看到一个输入框,然后输入框里面一般会有几个浅 ...
- 查看apk包及Activity名方法
查看apk包名方法activity名: 方法一: aapt dump badging +客户端包所在路径+客户端包名称 如: aapt.exe dump badging K:\Apk\fanxing. ...
- hdu_4529_郑厂长系列故事——N骑士问题(状压DP)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4529 题意:中文,不解释 题解:状压DP,dp[i][j][k][s]表示第i行当前用了j个骑士,i- ...
- 依赖注入容器Autofac与MVC集成
Autofac是应用于.Net平台的依赖注入(DI,Dependency Injection)容器,具有贴近.契合C#语言的特点.随着应用系统的日益庞大与复杂,使用Autofac容器来管理组件之间的关 ...
- Apache 与tomcat区别
Apache与Tomcat有什么关系和区别 经常在用apache和tomcat等这些服务器,可是总感觉还是不清楚他们之间有什么关系,在用tomcat的时候总出现apache,总感到迷惑,到底谁是主谁是 ...
- MySQL慢日志查询全解析:从参数、配置到分析工具【转】
转自: MySQL慢日志查询全解析:从参数.配置到分析工具 - MySQL - DBAplus社群——围绕数据库.大数据.PaaS云,运维圈最专注围绕“数据”的学习交流和专业社群http://dbap ...
- php五种常用的设计模式
php 设计模式 1.单例模式 单例模式顾名思义,就是只有一个实例.作为对象的创建模式, 单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的要点有三个: 一是某个类 ...
- Food on the Plane
Food on the Plane time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...
- dfs手写栈模板
在竞赛中如果系统栈很小的话,过深的递归会让栈溢出,这个时候我们就要自己手写栈,将递归转化成手工栈. 方法其实也很简单. 基本思路上,我们就是用栈不断的pop,push.但是何时push,何时pop呢? ...