Anroid ListView分组和悬浮Header实现方法

Anroid ListView分组和悬浮Header实现方法,第1张

概述之前在使用iOS时,看到过一种分组的View,每一组都有一个Header,在上下滑动的时候,会有一个悬浮的Header,这种体验觉得很不错,请看下图:

之前在使用iOS时,看到过一种分组的VIEw,每一组都有一个header,在上下滑动的时候,会有一个悬浮的header,这种体验觉得很不错,请看下图:


上图中标红的1,2,3,4四张图中,当向上滑动时,仔细观察灰色条的header变化,当第二组向上滑动时,会把第一组的悬浮header挤上去。

这种效果在AndroID是没有的,iOS的SDK就自带这种效果。这篇文章就介绍如何在AndroID实现这种效果。

1、悬浮header的实现

其实AndroID自带的联系人的App中就有这样的效果,我也是把他的类直接拿过来的,实现了PinnedheaderListVIEw这么一个类,扩展于ListVIEw,核心原理就是在ListVIEw的最顶部绘制一个调用者设置的header VIEw,在滑动的时候,根据一些状态来决定是否向上或向下移动header VIEw(其实就是调用其layout方法,理论上在绘制那里作一些平移也是可以的)。下面说一下具体的实现:

1.1、PinnedheaderAdapter接口

这个接口需要ListVIEw的Adapter来实现,它定义了两个方法,一个是让Adapter告诉ListVIEw当前指定的position的数据的状态,比如指定position的数据可能是组的header;另一个方法就是设置header VIEw,比如设置header VIEw的文本,图片等,这个方法是由调用者去实现的。

/**  * 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

这是在dispatchDraw方法中绘制的:

@OverrIDe protected voID dispatchDraw(Canvas canvas) {   super.dispatchDraw(canvas);   if (mheaderVIEwVisible) {     drawChild(canvas,mheaderVIEw,getDrawingTime());   } } 

1.3、配置header VIEw

核心就是根据不同的状态值来控制header VIEw的状态,比如PINNED_header_GONE(隐藏)的情况,可能需要设置一个flag标记,不绘制header VIEw,那么就达到隐藏的效果。当PINNED_header_PUSHED_UP状态时,可能需要根据不同的位移来计算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,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,Alpha);       if (mheaderVIEw.gettop() != y) {         mheaderVIEw.layout(0,y,mheaderVIEwHeight + y);       }       mheaderVIEwVisible = true;       break;     }   } } 

1.4、onLayout和onMeasure

在这两个方法中,控制header VIEw的位置及大小

@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,mheaderVIEwHeight);     configureheaderVIEw(getFirstVisibleposition());   } } 

好了,到这里,悬浮header VIEw就完了,各位可能看不到完整的代码,只要明白这几个核心的方法,自己写出来,也差不多了。

2、ListVIEw Section实现

有两种方法实现ListVIEw Section效果:

方法一:

每一个ItemVIEw中包含header,通过数据来控制其显示或隐藏,实现原理如下图:


优点:

1,实现简单,在Adapter.getVIEw的实现中,只需要根据数据来判断是否是header,不是的话,隐藏Item vIEw中的header部分,否则显示。

2,Adapter.getItem(int n)始终返回的数据是在数据列表中对应的第n个数据,这样容易理解。

3,控制header的点击事件更加容易

缺点:
1、使用更多的内存,第一个Item vIEw中都包含一个header vIEw,这样会费更多的内存,多数时候都可能header都是隐藏的。

方法二:

使用不同类型的VIEw:重写getItemVIEwType(int)和getVIEwTypeCount()方法。

优点:

1,允许多个不同类型的item

2,理解更加简单

缺点:

1,实现比较复杂

2,得到指定位置的数据变得复杂一些

到这里,我的实现方式是选择第二种方案,尽管它的实现方式要复杂一些,但优点比较明显。

3、Adapter的实现

这里主要就是说一下getPinnedheaderState和configurePinnedheader这两个方法的实现

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);         itemVIEw.setGravity(Gravity.CENTER_VERTICAL);         //itemVIEw.setBackgroundcolor(color.argb(255,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 Alpha) {     Contact item = (Contact) getItem(position);     if (null != item) {       if (header instanceof TextVIEw) {         ((TextVIEw) header).setText(item.sectionStr);       }     }   } } 

在getPinnedheaderState方法中,如果第一个item不是section,第二个item是section的话,就返回状态PINNED_header_PUSHED_UP,否则返回PINNED_header_VISIBLE。
在configurePinnedheader方法中,就是将item的section字符串设置到header vIEw上面去。

【重要说明】

Adapter中的数据里面已经包含了section(header)的数据,数据结构中有一个方法来标识它是否是section。那么,在点击事件就要注意了,通过position可能返回的是section数据结构。

数据结构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));     mListVIEw.setonItemClickListener(new OnItemClickListener() {       @OverrIDe       public voID onItemClick(AdapterVIEw<?> parent,VIEw vIEw,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,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,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);           itemVIEw.setGravity(Gravity.CENTER_VERTICAL);           //itemVIEw.setBackgroundcolor(color.argb(255,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 Alpha) {       Contact item = (Contact) getItem(position);       if (null != item) {         if (header instanceof TextVIEw) {           ((TextVIEw) header).setText(item.sectionStr);         }       }     }   } } 

最后来一张截图:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。

总结

以上是内存溢出为你收集整理的Anroid ListView分组和悬浮Header实现方法全部内容,希望文章能够帮你解决Anroid ListView分组和悬浮Header实现方法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1147816.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存