国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Anroid ListView分組和懸浮Header實現方法

2019-12-12 04:48:03
字體:
來源:轉載
供稿:網友

之前在使用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, 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

在這兩個方法中,控制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, 0, mHeaderViewWidth, 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, 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);       }     }   } } 

在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, 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);         }       }     }   } } 

最后來一張截圖:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 鄂温| 巴林左旗| 全椒县| 南雄市| 仁布县| 万年县| 滦平县| 香港| 石景山区| 郓城县| 青川县| 于田县| 清涧县| 瑞丽市| 安顺市| 柳江县| 上高县| 剑川县| 泗阳县| 华池县| 翁源县| 江城| 涟源市| 塘沽区| 舒兰市| 新乡市| 岑巩县| 雅江县| 嘉义市| 辽源市| 历史| 扎赉特旗| 长宁区| 漾濞| 饶平县| 娄底市| 凤冈县| 峨眉山市| 南靖县| 恩施市| 兴山县|