最近公司要 添加类似支付宝账单 的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 接口 ,从而实现分页加载的效果。
望大家 多多指点!