仿支付宝账单的效果(listview分组 )

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




望大家 多多指点!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值