仿支付寶賬單的效果(listview分組 )
阿新 • • 發佈:2019-01-26
最近公司要 新增類似支付寶賬單 的listview分組頂部懸浮 的效果,其實總的實現思想很簡單。由於 後臺給的資料 的不同 ,可能處理的方式也不一樣。
接下來咱們就一起來探討研究一下。
首先 ,自定義ListView ,建立 UpLoadPinnedHeaderListView 類 實現 繼承listview。
public class UpLoadPinnedHeaderListView extends ListView implements OnScrollListener { private OnScrollListener mOnScrollListener; public static interface PinnedSectionedHeaderAdapter { public boolean isSectionHeader(int position); public int getSectionForPosition(int position); public View getSectionHeaderView(int section, View convertView, ViewGroup parent); public int getSectionHeaderViewType(int section); public int getCount(); } private PinnedSectionedHeaderAdapter mAdapter; private View mCurrentHeader; private int mCurrentHeaderViewType = 0; private float mHeaderOffset; private boolean mShouldPin = true; private int mCurrentSection = 0; private int mWidthMode; private int mHeightMode; public UpLoadPinnedHeaderListView(Context context) { super(context); super.setOnScrollListener(this); } public UpLoadPinnedHeaderListView(Context context, AttributeSet attrs) { super(context, attrs); super.setOnScrollListener(this); } public UpLoadPinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); super.setOnScrollListener(this); } public void setPinHeaders(boolean shouldPin) { mShouldPin = shouldPin; } @Override public void setAdapter(ListAdapter adapter) { mCurrentHeader = null; mAdapter = (PinnedSectionedHeaderAdapter) adapter; super.setAdapter(adapter); } /** * 載入更多資料回撥介面 */ public interface OnLoadingMoreLinstener { /** * 載入更多資料回撥方法,由元件自身觸發 */ void OnLoadingMore(); } public OnLoadingMoreLinstener loadMoreListener; public void setLoadingMoreListener(OnLoadingMoreLinstener listener) { this.loadMoreListener = listener; }
@Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (mOnScrollListener != null) { mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); } if (mAdapter == null || mAdapter.getCount() == 0 || !mShouldPin || (firstVisibleItem < getHeaderViewsCount())) { mCurrentHeader = null; mHeaderOffset = 0.0f; for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) { View header = getChildAt(i); if (header != null) { header.setVisibility(VISIBLE); } } return; } firstVisibleItem -= getHeaderViewsCount(); int section = mAdapter.getSectionForPosition(firstVisibleItem); int viewType = mAdapter.getSectionHeaderViewType(section); mCurrentHeader = getSectionHeaderView(section, mCurrentHeaderViewType != viewType ? null : mCurrentHeader); ensurePinnedHeaderLayout(mCurrentHeader); mCurrentHeaderViewType = viewType; mHeaderOffset = 0.0f; for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) { if (mAdapter.isSectionHeader(i)) { View header = getChildAt(i - firstVisibleItem); float headerTop = header.getTop(); float pinnedHeaderHeight = mCurrentHeader.getMeasuredHeight(); header.setVisibility(VISIBLE); if (pinnedHeaderHeight >= headerTop && headerTop > 0) { mHeaderOffset = headerTop - header.getHeight(); } else if (headerTop <= 0) { header.setVisibility(INVISIBLE); } } } invalidate(); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (mOnScrollListener != null) { mOnScrollListener.onScrollStateChanged(view, scrollState); } // 滑到底部後自動載入,判斷listview已經停止滾動並且最後可視的條目等於adapter的條目 if (scrollState == OnScrollListener.SCROLL_STATE_FLING) { } else if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL || scrollState == OnScrollListener.SCROLL_STATE_IDLE) { if (getLastVisiblePosition() == (getCount() - 1)) { Log.e("Sticky","--拖動到最後--"); if(loadMoreListener != null) { loadMoreListener.OnLoadingMore(); } } } } private View getSectionHeaderView(int section, View oldView) { boolean shouldLayout = section != mCurrentSection || oldView == null; View view = mAdapter.getSectionHeaderView(section, oldView, this); if (shouldLayout) { // a new section, thus a new header. We should lay it out again ensurePinnedHeaderLayout(view); mCurrentSection = section; } return view; } private void ensurePinnedHeaderLayout(View header) { if (header.isLayoutRequested()) { int widthSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), mWidthMode); int heightSpec; ViewGroup.LayoutParams layoutParams = header.getLayoutParams(); if (layoutParams != null && layoutParams.height > 0) { heightSpec = MeasureSpec.makeMeasureSpec(layoutParams.height, MeasureSpec.EXACTLY); } else { heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } header.measure(widthSpec, heightSpec); header.layout(0, 0, header.getMeasuredWidth(), header.getMeasuredHeight()); } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mAdapter == null || !mShouldPin || mCurrentHeader == null) return; int saveCount = canvas.save(); canvas.translate(0, mHeaderOffset); canvas.clipRect(0, 0, getWidth(), mCurrentHeader.getMeasuredHeight()); // needed // for // < // HONEYCOMB mCurrentHeader.draw(canvas); canvas.restoreToCount(saveCount); } @Override public void setOnScrollListener(OnScrollListener l) { mOnScrollListener = l; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidthMode = MeasureSpec.getMode(widthMeasureSpec); mHeightMode = MeasureSpec.getMode(heightMeasureSpec); } public void setOnItemClickListener(OnItemClickListener listener) { super.setOnItemClickListener(listener); } public static abstract class OnItemClickListener implements AdapterView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> adapterView, View view, int rawPosition, long id) { SectionedBaseAdapter adapter; if (adapterView.getAdapter().getClass().equals(HeaderViewListAdapter.class)) { HeaderViewListAdapter wrapperAdapter = (HeaderViewListAdapter) adapterView.getAdapter(); adapter = (SectionedBaseAdapter) wrapperAdapter.getWrappedAdapter(); } else { adapter = (SectionedBaseAdapter) adapterView.getAdapter(); } int section = adapter.getSectionForPosition(rawPosition); int position = adapter.getPositionInSectionForPosition(rawPosition); if (position == -1) { onSectionClick(adapterView, view, section, id); } else { onItemClick(adapterView, view, section, position, id); } } public abstract void onItemClick(AdapterView<?> adapterView, View view, int section, int position, long id); public abstract void onSectionClick(AdapterView<?> adapterView, View view, int section, long id); } private ArrayList<View> mFooterViews; @Override public void addFooterView(View v) { super.addFooterView(v); if (mFooterViews == null) { mFooterViews = new ArrayList<View>(); } mFooterViews.add(v); } @Override public boolean removeFooterView(View v) { if (super.removeFooterView(v)) { mFooterViews.remove(v); return true; } return false; } }
這塊程式碼塊 主要是是實現分頁載入的效果。
其次 ,自定義BaseAdapter 實現 上面 自定義listview中的 介面
public abstract class SectionedBaseAdapter extends BaseAdapter implements UpLoadPinnedHeaderListView.PinnedSectionedHeaderAdapter { private static int HEADER_VIEW_TYPE = 0; private static int ITEM_VIEW_TYPE = 0; /** * Holds the calculated values of @{link getPositionInSectionForPosition} */ private SparseArray<Integer> mSectionPositionCache; /** * Holds the calculated values of @{link getSectionForPosition} */ private SparseArray<Integer> mSectionCache; /** * Holds the calculated values of @{link getCountForSection} */ private SparseArray<Integer> mSectionCountCache; /** * Caches the item count */ private int mCount; /** * Caches the section count */ private int mSectionCount; public SectionedBaseAdapter() { super(); mSectionCache = new SparseArray<Integer>(); mSectionPositionCache = new SparseArray<Integer>(); mSectionCountCache = new SparseArray<Integer>(); mCount = -1; mSectionCount = -1; } @Override public void notifyDataSetChanged() { mSectionCache.clear(); mSectionPositionCache.clear(); mSectionCountCache.clear(); mCount = -1; mSectionCount = -1; super.notifyDataSetChanged(); } @Override public void notifyDataSetInvalidated() { mSectionCache.clear(); mSectionPositionCache.clear(); mSectionCountCache.clear(); mCount = -1; mSectionCount = -1; super.notifyDataSetInvalidated(); } @Override public final int getCount() { if (mCount >= 0) { return mCount; } int count = 0; for (int i = 0; i < internalGetSectionCount(); i++) { count += internalGetCountForSection(i); count++; // for the header view } mCount = count; return count; } @Override public final Object getItem(int position) { return getItem(getSectionForPosition(position), getPositionInSectionForPosition(position)); } @Override public final long getItemId(int position) { return getItemId(getSectionForPosition(position), getPositionInSectionForPosition(position)); } @Override public final View getView(int position, View convertView, ViewGroup parent) { if (isSectionHeader(position)) { return getSectionHeaderView(getSectionForPosition(position), convertView, parent); } return getItemView(getSectionForPosition(position), getPositionInSectionForPosition(position), convertView, parent); } @Override public final int getItemViewType(int position) { if (isSectionHeader(position)) { return getItemViewTypeCount() + getSectionHeaderViewType(getSectionForPosition(position)); } return getItemViewType(getSectionForPosition(position), getPositionInSectionForPosition(position)); } @Override public final int getViewTypeCount() { return getItemViewTypeCount() + getSectionHeaderViewTypeCount(); } public final int getSectionForPosition(int position) { // first try to retrieve values from cache Integer cachedSection = mSectionCache.get(position); if (cachedSection != null) { return cachedSection; } int sectionStart = 0; for (int i = 0; i < internalGetSectionCount(); i++) { int sectionCount = internalGetCountForSection(i); int sectionEnd = sectionStart + sectionCount + 1; if (position >= sectionStart && position < sectionEnd) { mSectionCache.put(position, i); return i; } sectionStart = sectionEnd; } return 0; } public int getPositionInSectionForPosition(int position) { // first try to retrieve values from cache Integer cachedPosition = mSectionPositionCache.get(position); if (cachedPosition != null) { return cachedPosition; } int sectionStart = 0; for (int i = 0; i < internalGetSectionCount(); i++) { int sectionCount = internalGetCountForSection(i); int sectionEnd = sectionStart + sectionCount + 1; if (position >= sectionStart && position < sectionEnd) { int positionInSection = position - sectionStart - 1; mSectionPositionCache.put(position, positionInSection); return positionInSection; } sectionStart = sectionEnd; } return 0; } public final boolean isSectionHeader(int position) { int sectionStart = 0; for (int i = 0; i < internalGetSectionCount(); i++) { if (position == sectionStart) { return true; } else if (position < sectionStart) { return false; } sectionStart += internalGetCountForSection(i) + 1; } return false; } public int getItemViewType(int section, int position) { return ITEM_VIEW_TYPE; } public int getItemViewTypeCount() { return 1; } public int getSectionHeaderViewType(int section) { return HEADER_VIEW_TYPE; } public int getSectionHeaderViewTypeCount() { return 1; } public abstract Object getItem(int section, int position); public abstract long getItemId(int section, int position); public abstract int getSectionCount(); public abstract int getCountForSection(int section); public abstract View getItemView(int section, int position, View convertView, ViewGroup parent); public abstract View getSectionHeaderView(int section, View convertView, ViewGroup parent); private int internalGetCountForSection(int section) { Integer cachedSectionCount = mSectionCountCache.get(section); if (cachedSectionCount != null) { return cachedSectionCount; } int sectionCount = getCountForSection(section); mSectionCountCache.put(section, sectionCount); return sectionCount; } private int internalGetSectionCount() { if (mSectionCount >= 0) { return mSectionCount; } mSectionCount = getSectionCount(); return mSectionCount; } }
最後 ,操作 這個介面卡
public class TestSectionedAdapter extends SectionedBaseAdapter {
/**
* 每月的記錄的item集合
*/
private List<Map<String, ArrayList<ListMapBean>>> list_map;
/**
* 每月 的總的記錄資料
*/
private List<StatisticlistMapBean> list_static;
private Context context;
public TestSectionedAdapter(Context context, List<Map<String, ArrayList<ListMapBean>>> list_map, List<StatisticlistMapBean> list_static) {
this.context = context;
this.list_map = list_map;
this.list_static = list_static;
}
public void setDataList(List<Map<String, ArrayList<ListMapBean>>> list_map) {
this.list_map = list_map;
notifyDataSetChanged();
}
@Override
public Object getItem(int section, int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int section, int position) {
// TODO Auto-generated method stub
return 0;
}
/**
* 分組的組數
* @return
*/
@Override
public int getSectionCount() {
return list_static.size();
}
/**
* 每個分組下對應的 item的個數
* @param section
* @return
*/
@Override
public int getCountForSection(int section) {
int size = 0;
//獲取map集合中的所有key
Map<String, ArrayList<ListMapBean>> stringArrayListMap = list_map.get(section);
Set<String> keys = stringArrayListMap.keySet();
//利用迭代 (Iterator)
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
ArrayList<ListMapBean> objects = stringArrayListMap.get(key);
size = objects.size();
}
return size;
}
/**
* 每個分組下 的listview item 佈局
* @param section
* @param position
* @param convertView
* @param parent
* @return
*/
@Override
public View getItemView(final int section, final int position, View convertView,
ViewGroup parent) {
LayoutInflater layoutInflater = LayoutInflater.from(context);
ViewHolderChild childHoler = null;
if (convertView == null) {
childHoler = new ViewHolderChild();
convertView = layoutInflater.inflate(R.layout.capital_item_value, parent, false);
childHoler.capital_item_value_name = (TextView) convertView.findViewById(R.id.capital_item_value_name);
childHoler.capital_item_value_money = (TextView) convertView.findViewById(R.id.capital_item_value_money);
childHoler.capital_item_value_time = (TextView) convertView.findViewById(R.id.capital_item_value_time);
childHoler.capital_item_value_remain = (TextView) convertView.findViewById(R.id.capital_item_value_remain);
convertView.setTag(childHoler);
} else {
childHoler = (ViewHolderChild) convertView.getTag();
}
//為了條目設定資料
//獲取map集合中的所有key
Map<String, ArrayList<ListMapBean>> stringArrayListMap = list_map.get(section);
Set<String> keys = stringArrayListMap.keySet();
//利用迭代 (Iterator)
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
ArrayList<ListMapBean> objects = stringArrayListMap.get(key);
ListMapBean listmapbean = objects.get(position);
if(listmapbean!=null)
{
childHoler.capital_item_value_name.setText(listmapbean.getTypeStr());
childHoler.capital_item_value_money.setText(listmapbean.getMoneyOperate());
childHoler.capital_item_value_time.setText(listmapbean.getCreatedAt());
childHoler.capital_item_value_remain.setText(listmapbean.getMoneyUsable());
}
}
return convertView;
}
/**
* 分組 頭的佈局
* @param section
* @param convertView
* @param parent
* @return
*/
@Override
public View getSectionHeaderView(int section, View convertView,
ViewGroup parent) {
StatisticlistMapBean object = list_static.get(section);
//填充頭佈局
LinearLayout layout = null;
if (convertView == null) {
//找控制元件
LayoutInflater inflator = (LayoutInflater) parent.getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layout = (LinearLayout) inflator.inflate(
R.layout.capital_item_title, null);
} else {
layout = (LinearLayout) convertView;
}
//找控制元件
TextView capital_item_title_time = (TextView) layout.findViewById(R.id.capital_item_title_time);//消費的時間
TextView capital_item_title_get = (TextView) layout.findViewById(R.id.capital_item_title_get);//月份的總收入
TextView capital_item_title_out = (TextView) layout.findViewById(R.id.capital_item_title_out);//月份的總支出
TextView capital_get_title = (TextView) layout.findViewById(R.id.capital_get_title);//月份的總收入提示
TextView capital_out_title = (TextView) layout.findViewById(R.id.capital_out_title);//月份的總支出提示
//為找到的控制元件進行賦值
if(object!=null)
{
String month = object.getMonth();
String year = month.substring(1,5);
String date = month.substring(5,7);
capital_item_title_time.setText(year+"年"+date+"月");
if(TextUtils.isEmpty(object.getIncome()))
{
capital_get_title.setVisibility(View.GONE);
}else{
capital_get_title.setVisibility(View.VISIBLE);
capital_item_title_get.setText(object.getIncome());
}
if(TextUtils.isEmpty(object.getExpenditure()))
{
capital_out_title.setVisibility(View.GONE);
}else{
capital_out_title.setVisibility(View.VISIBLE);
capital_item_title_out.setText(object.getExpenditure());
}
}
return layout;
}
//二級的 佈局控制元件
public final class ViewHolderChild {
public TextView capital_item_value_name;//型別的名字
public TextView capital_item_value_money;//流動的資金
public TextView capital_item_value_time;//流動的時間
public TextView capital_item_value_remain;//剩餘資金
}
}
在fragment 、 activity 中實現 UpLoadPinnedHeaderListView.OnLoadingMoreLinstener 介面 ,從而實現分頁載入的效果。
望大家 多多指點!