android自定义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);
- }
- }
- }
- }
- }
关于数据加载,分组的逻辑这里就不列出了,数据分组请参考:
android自定义listview实现header悬浮框效果的更多相关文章
- Android 自定义 ListView 上下拉动“刷新最新”和“加载更多”歌曲列表
本文内容 环境 测试数据 项目结构 演示 参考资料 本文演示,上拉刷新最新的歌曲列表,和下拉加载更多的歌曲列表.所谓"刷新最新"和"加载更多"是指日期.演示代码 ...
- Android 自定义ListView
本文讲实现一个自定义列表的Android程序,程序将实现一个使用自定义的适配器(Adapter)绑定 数据,通过contextView.setTag绑定数据有按钮的ListView. 系统显示列表(L ...
- C# 鼠标悬停在datagridview的某单元格,显示悬浮框效果
今天在做项目时,看到一软件做的悬浮框效果不错,从网上搜罗了一些资料,未见到有十分好的解决办法,只能自已动手,利用datagridview 的ToolTipText 来达到此效果. 以下是我简单实现的代 ...
- Android -- 使用WindowManager实现悬浮框效果
1,原文在这里http://blog.csdn.net/qq_17250009/article/details/52908791,我只是把里面的关键步骤给注释了一下,首先来看一下我们的效果,如图(电脑 ...
- Android 自定义ListView实现底部分页刷新与顶部下拉刷新,androidlistview
在项目开发中,由于数据过大时,需要进行分页加载或下拉刷新,来缓解一次性加载的过长等待.本篇博文实例讲解通过自定义的ListView实现底部分页加载和顶部下拉刷新的效果. 其效果图: 一.ListVie ...
- android中listview的item滑动删除效果(已解决listview点击问题)
领导看到iphone上tableview有个滑动删除的效果,要求在android上也实现,搜了下资料,实现起来比较简单,可弄到后面,居然不能点击了,把一篇文章中的代码修改了一下,捣鼓了一番,搞定,下面 ...
- [置顶] android 自定义ListView实现动画特效
通过自定义ListView实现动画特效,被点击元素A向前移,A之前元素往后移动. 重点在于动画的实现: 具体代码如下: package com.open.widget; import java.uti ...
- android自定义listview实现圆角
在项目中我们会经常遇到这种圆角效果,因为直角的看起来确实不那么雅观,可能大家会想到用图片实现,试想上中下要分别做三张图片,这样既会是自己的项目增大也会增加内存使用量,所以使用shape来实现不失为一种 ...
- Android 自定义 ListView 上下拉动刷新最新和加载更多
本文内容 开发环境 演示上下拉动刷新最新和加载更多 ListView 参考资料 本文演示上下拉动,刷新最新和加载更多,这个效果很常见,比如,新闻资讯类 APP,当向下拉动时,加载最新的资讯:向上拉动时 ...
随机推荐
- [转载]致创业者:APP已死 服务永生
前几日,有位创业者和我讲他在带领团队做一个将爱踢球的人集中在一起的App,我告诉他你的创业方向错了.原因在于你的目的是要为爱踢球的人提供服务,而你现在却在竭尽全力的做App,你应该做的是设计你为爱踢球 ...
- xshell连接centos与ubuntu
操作系统:Windows 7 应用软件:Ware Workstation &Xshell 5 Linux:CentOS 7 Minimal &Ubuntu Server 16 ==== ...
- PHP MySQL Delete
DELETE 语句用于从数据库表中删除行. 删除数据库中的数据 DELETE FROM 语句用于从数据库表中删除记录. 语法 DELETE FROM table_name WHERE some_col ...
- 在Spring Boot中使用数据缓存
春节就要到了,在回家之前要赶快把今年欠下的技术债还清.so,今天继续.Spring Boot前面已经预热了n篇博客了,今天我们来继续看如何在Spring Boot中解决数据缓存问题.本篇博客是以初识在 ...
- java.io.FileNotFoundException: D:\Program%20Files\Apache%20Software%20Foundation\Tomcat%205.0\webapp
慢慢把以前遇到过的问题一点点发出来,以前做的笔记比较杂: java.io.FileNotFoundException: D:\Program%20Files\Apache%20Software%20F ...
- 六星经典CSAPP-笔记(10)系统IO
六星经典CSAPP-笔记(10)系统I/O 1.Unix I/O 所有语言的运行时系统都提供了高抽象层次的I/O操作函数.例如,ANSI C在标准I/O库中提供了诸如printf和scanf等I/O缓 ...
- python模块:时间处理模块
http://blog.csdn.net/pipisorry/article/details/53067168 常用python自带时间处理模块 python自带的时间处理模块参考[操作系统服务:ti ...
- 【BAT经典算法面试题系列】求和为n的连续正整数
马上就要到9月份了,意味着一年一度的秋招就要开始了,相信不论是正在实习的童鞋还是马上就要找工作的童鞋,BAT无疑是国内的"明星企业",是每个学计算机的小伙伴们心之向往的企业,但是呢 ...
- Github上的Android项目介绍之ListViewAnimation(针对listView item的侧滑菜单)(1)
demo源码,需要可以下载 1.这是一个github开源项目,先去github上面下载,github下载地址. 2.将SwipeMenuListView项目,导入,然后新建项目如果要引用,要设置为相应 ...
- XMPP系列(七)---获取群组列表
上一篇介绍了如何创建群组,这一篇就介绍一下,如何获取自己的群组列表. 在上一篇有提到,如果我们创建的群组是公共的群组,那么获取自己的群组列表时,会获取到自己的群组列表和那些公共的群组.而实际做社交的应 ...
