Android实现仿支付宝流水

来源:互联网 发布:java web是什么 编辑:程序博客网 时间:2024/05/16 17:31

今天给大家讲的是如何自定义下拉的ListView实现支付宝账单的效果,月份是需要悬浮的,然后没一个月归为一类,先看一个效果图吧。

这里写图片描述

场景:后台下发的数据就是一个List<对象>,考虑到实际情况,还需要做下拉的分页操作,所以,基于上面的情况,我们需要自定义一个可以拦截月份的view。

先定义一个FooterView类。

public class ListViewFooter extends LinearLayout {    public final static int STATE_NORMAL = 0;    public final static int STATE_READY = 1;    public final static int STATE_LOADING = 2;    private Context mContext;    private View mContentView;    private View mProgressBar;    private TextView mFootTextView;    public ListViewFooter(Context context) {        super(context);        initView(context);    }    public ListViewFooter(Context context, AttributeSet attrs) {        super(context, attrs);        initView(context);    }    //下拉状态处理    public void setState(int state) {        mFootTextView.setVisibility(View.INVISIBLE);        mProgressBar.setVisibility(View.GONE);        if (state == STATE_READY) {            mFootTextView.setVisibility(View.VISIBLE);            mProgressBar.setVisibility(View.GONE);            mFootTextView.setText(R.string.pull_footer_hint_ready);        } else if (state == STATE_LOADING) {            mProgressBar.setVisibility(View.VISIBLE);            mFootTextView.setVisibility(View.VISIBLE);        } else {            mFootTextView.setVisibility(View.VISIBLE);            mProgressBar.setVisibility(View.GONE);            mFootTextView.setText(R.string.pull_footer_hint_normal);        }    }    public void setBottomMargin(int height) {        if (height < 0) return ;        LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();        lp.bottomMargin = height;        mContentView.setLayoutParams(lp);    }    public int getBottomMargin() {        LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();        return lp.bottomMargin;    }    public void normal() {        mFootTextView.setVisibility(View.VISIBLE);        mProgressBar.setVisibility(View.GONE);    }    public void loading() {        mFootTextView.setVisibility(View.GONE);        mProgressBar.setVisibility(View.VISIBLE);    }    public void hide() {        LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();        lp.height = 0;        mContentView.setLayoutParams(lp);    }    public void show() {        LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();        lp.height = LayoutParams.WRAP_CONTENT;        mContentView.setLayoutParams(lp);    }    private void initView(Context context) {        mContext = context;        LinearLayout moreView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_listview_footer, null);        addView(moreView);        moreView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));        mContentView = moreView.findViewById(R.id.footer_view);        mProgressBar = moreView.findViewById(R.id.footer_progressbar);        mFootTextView = (TextView)moreView.findViewById(R.id.footer_textview);    }    public TextView getmFootTextView() {        return mFootTextView;    }}

然后我们通过继承自ListViwe实现一个下拉的PullListView类。

public class PullFreshListView extends ListView implements OnScrollListener {    private float mLastY = -1;    private Scroller mScroller;    private OnScrollListener mScrollListener;    private ListViewPlusListener mListViewListener;    private boolean mPullRefreshing = false;    private ListViewFooter mFooterView;    private boolean mEnablePullLoad;    private boolean mPullLoading;    private boolean mIsFooterReady = false;    private int mTotalItemCount;    private int mScrollBack;    private int minItemCount=3;    private final static int SCROLLBACK_HEADER = 0;    private final static int SCROLLBACK_FOOTER = 1;    private final static int SCROLL_DURATION = 400;    private final static int PULL_LOAD_MORE_DELTA = 50;    private final static float OFFSET_RADIO = 1.8f;    public PullFreshListView(Context context) {        super(context);        initWithContext(context);    }    public PullFreshListView(Context context, AttributeSet attrs) {        super(context, attrs);        initWithContext(context);    }    public PullFreshListView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initWithContext(context);    }    private void initWithContext(Context context) {        mScroller = new Scroller(context, new DecelerateInterpolator());        super.setOnScrollListener(this);        mFooterView = new ListViewFooter(context);    }    @Override    public void setAdapter(ListAdapter adapter) {        if (mIsFooterReady == false) {            mIsFooterReady = true;            addFooterView(mFooterView);        }        super.setAdapter(adapter);    }    public void setLoadEnable(boolean enable) {        mEnablePullLoad = enable;        if (!mEnablePullLoad) {            mFooterView.hide();            mFooterView.setOnClickListener(null);            setFooterDividersEnabled(false);        } else {            mPullLoading = false;            mFooterView.show();            mFooterView.setState(ListViewFooter.STATE_NORMAL);            setFooterDividersEnabled(true);            mFooterView.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    startLoadMore();                }            });        }    }    public void stopLoadMore() {        if (mPullLoading == true) {            mPullLoading = false;            mFooterView.setState(ListViewFooter.STATE_NORMAL);        }    }    private void invokeOnScrolling() {        if (mScrollListener instanceof OnXScrollListener) {            OnXScrollListener l = (OnXScrollListener) mScrollListener;            l.onXScrolling(this);        }    }    private void resetHeaderHeight() {        invalidate();    }    private void updateFooterHeight(float delta) {        int height = mFooterView.getBottomMargin() + (int) delta;        if (mEnablePullLoad && !mPullLoading) {            if (height > PULL_LOAD_MORE_DELTA) {                mFooterView.setState(ListViewFooter.STATE_READY);            } else {                mFooterView.setState(ListViewFooter.STATE_NORMAL);            }        }        mFooterView.setBottomMargin(height);    }    private void resetFooterHeight() {        int bottomMargin = mFooterView.getBottomMargin();        if (bottomMargin > 0) {            mScrollBack = SCROLLBACK_FOOTER;            mScroller.startScroll(0, bottomMargin, 0, -bottomMargin,                    SCROLL_DURATION);            invalidate();        }    }    private void startLoadMore() {        mPullLoading = true;        mFooterView.setState(ListViewFooter.STATE_LOADING);        if (mListViewListener != null) {            mListViewListener.onLoadMore();        }    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        if (mLastY == -1) {            mLastY = ev.getRawY();        }        switch (ev.getAction()) {        case MotionEvent.ACTION_DOWN:            mLastY = ev.getRawY();            break;        case MotionEvent.ACTION_MOVE:            final float deltaY = ev.getRawY() - mLastY;            mLastY = ev.getRawY();             if (getLastVisiblePosition() == mTotalItemCount - 1                    && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {                updateFooterHeight(-deltaY / OFFSET_RADIO);            }            break;        default:            mLastY = -1;            if (getFirstVisiblePosition() == 0) {                resetHeaderHeight();            } else if (getLastVisiblePosition() == mTotalItemCount - 1) {                if (mEnablePullLoad                    && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA                    && !mPullLoading) {                    startLoadMore();                }                resetFooterHeight();            }            break;        }        return super.onTouchEvent(ev);    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            if (mScrollBack == SCROLLBACK_HEADER) {            } else {                mFooterView.setBottomMargin(mScroller.getCurrY());            }            postInvalidate();            invokeOnScrolling();        }        super.computeScroll();    }    @Override    public void setOnScrollListener(OnScrollListener l) {        mScrollListener = l;    }    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        if (mScrollListener != null) {            mScrollListener.onScrollStateChanged(view, scrollState);        }    }    @Override    public void onScroll(AbsListView view, int firstVisibleItem,            int visibleItemCount, int totalItemCount) {        if(mFooterView!=null){            if (totalItemCount<minItemCount+2) {                mFooterView.getmFootTextView().setText("");            }else {                if(mFooterView.getmFootTextView().getText().toString().equals(""))mFooterView.getmFootTextView().                    setText(getResources().getString(R.string.pull_footer_hint_normal));            }        }        mTotalItemCount = totalItemCount;        if (mScrollListener != null) {            mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,                    totalItemCount);        }    }    public void setListViewPlusListener(ListViewPlusListener l) {        mListViewListener = l;    }    public interface OnXScrollListener extends OnScrollListener {         void onXScrolling(View view);    }    public interface ListViewPlusListener {         void onLoadMore();    }}

最后要实现可以悬浮的效果,我们需要对布局的滑动的数据进行监听,具体请看代码:

public class PullStickyListView extends PullFreshListView {    public  interface PinnedSectionListAdapter extends ListAdapter {        boolean isItemViewTypePinned(int viewType);    }    static class PinnedSection {        public View view;        public int position;        public long id;    }    private final Rect mTouchRect = new Rect();    private final PointF mTouchPoint = new PointF();    private int mTouchSlop;    private View mTouchTarget;    private MotionEvent mDownEvent;    private GradientDrawable mShadowDrawable;    private int mSectionsDistanceY;    private int mShadowHeight;    private OnScrollListener mDelegateOnScrollListener;    private PinnedSection mRecycleSection;    private PinnedSection mPinnedSection;    private int mTranslateY;    private final OnScrollListener mOnScrollListener = new OnScrollListener() {        @Override        public void onScrollStateChanged(AbsListView view, int scrollState) {            if (mDelegateOnScrollListener != null) {                mDelegateOnScrollListener.onScrollStateChanged(view, scrollState);            }        }        @Override        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {            if (mDelegateOnScrollListener != null) {                mDelegateOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);            }            ListAdapter adapter = getAdapter();            if (adapter == null || visibleItemCount == 0) return;            final boolean isFirstVisibleItemSection =                    isItemViewTypePinned(adapter, adapter.getItemViewType(firstVisibleItem));            if (isFirstVisibleItemSection) {                View sectionView = getChildAt(0);                if (sectionView.getTop() == getPaddingTop()) {                    destroyPinnedShadow();                } else {                    ensureShadowForPosition(firstVisibleItem, firstVisibleItem, visibleItemCount);                }            } else {                int sectionPosition = findCurrentSectionPosition(firstVisibleItem);                if (sectionPosition > -1) {                    ensureShadowForPosition(sectionPosition, firstVisibleItem, visibleItemCount);                } else {                    destroyPinnedShadow();                }            }        }    };    private final DataSetObserver mDataSetObserver = new DataSetObserver() {        @Override        public void onChanged() {            recreatePinnedShadow();        }        @Override        public void onInvalidated() {            recreatePinnedShadow();        }    };    public PullStickyListView(Context context, AttributeSet attrs) {        super(context, attrs);        initView();    }    public PullStickyListView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initView();    }    private void initView() {        setOnScrollListener(mOnScrollListener);        mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();//        initShadow(true);    }    public void setShadowVisible(boolean visible) {        initShadow(visible);        if (mPinnedSection != null) {            View v = mPinnedSection.view;            invalidate(v.getLeft(), v.getTop(), v.getRight(), v.getBottom() + mShadowHeight);        }    }    public void initShadow(boolean visible) {        if (visible) {            if (mShadowDrawable == null) {                mShadowDrawable = new GradientDrawable(Orientation.TOP_BOTTOM,                        new int[]{Color.parseColor("#ffa0a0a0"), Color.parseColor("#50a0a0a0"), Color.parseColor("#00a0a0a0")});                mShadowHeight = (int) (8 * getResources().getDisplayMetrics().density);            }        } else {            if (mShadowDrawable != null) {                mShadowDrawable = null;                mShadowHeight = 0;            }        }    }    void createPinnedShadow(int position) {        PinnedSection pinnedShadow = mRecycleSection;        mRecycleSection = null;        if (pinnedShadow == null) pinnedShadow = new PinnedSection();        View pinnedView = getAdapter().getView(position, pinnedShadow.view, PullStickyListView.this);        LayoutParams layoutParams = (LayoutParams) pinnedView.getLayoutParams();        if (layoutParams == null) {            layoutParams = (LayoutParams) generateDefaultLayoutParams();            pinnedView.setLayoutParams(layoutParams);        }        int heightMode = MeasureSpec.getMode(layoutParams.height);        int heightSize = MeasureSpec.getSize(layoutParams.height);        if (heightMode == MeasureSpec.UNSPECIFIED) heightMode = MeasureSpec.EXACTLY;        int maxHeight = getHeight() - getListPaddingTop() - getListPaddingBottom();        if (heightSize > maxHeight) heightSize = maxHeight;        // measure & layout        int ws = MeasureSpec.makeMeasureSpec(getWidth() - getListPaddingLeft() - getListPaddingRight(), MeasureSpec.EXACTLY);        int hs = MeasureSpec.makeMeasureSpec(heightSize, heightMode);        pinnedView.measure(ws, hs);        pinnedView.layout(0, 0, pinnedView.getMeasuredWidth(), pinnedView.getMeasuredHeight());        mTranslateY = 0;        // initialize pinned shadow        pinnedShadow.view = pinnedView;        pinnedShadow.position = position;        pinnedShadow.id = getAdapter().getItemId(position);        // store pinned shadow        mPinnedSection = pinnedShadow;    }    void destroyPinnedShadow() {        if (mPinnedSection != null) {            mRecycleSection = mPinnedSection;            mPinnedSection = null;        }    }    void ensureShadowForPosition(int sectionPosition, int firstVisibleItem, int visibleItemCount) {        if (visibleItemCount < 2) {            destroyPinnedShadow();            return;        }        if (mPinnedSection != null                && mPinnedSection.position != sectionPosition) {            destroyPinnedShadow();        }        if (mPinnedSection == null) {            createPinnedShadow(sectionPosition);        }        int nextPosition = sectionPosition + 1;        if (nextPosition < getCount()) {            int nextSectionPosition = findFirstVisibleSectionPosition(nextPosition,                    visibleItemCount - (nextPosition - firstVisibleItem));            if (nextSectionPosition > -1) {                View nextSectionView = getChildAt(nextSectionPosition - firstVisibleItem);                final int bottom = mPinnedSection.view.getBottom() + getPaddingTop();                mSectionsDistanceY = nextSectionView.getTop() - bottom;                if (mSectionsDistanceY < 0) {                    mTranslateY = mSectionsDistanceY;                } else {                    mTranslateY = 0;                }            } else {                mTranslateY = 0;                mSectionsDistanceY = Integer.MAX_VALUE;            }        }    }    int findFirstVisibleSectionPosition(int firstVisibleItem, int visibleItemCount) {        ListAdapter adapter = getAdapter();        int adapterDataCount = adapter.getCount();        if (getLastVisiblePosition() >= adapterDataCount)            return -1;        if (firstVisibleItem + visibleItemCount >= adapterDataCount) {//added to prevent index Outofbound (in case)            visibleItemCount = adapterDataCount - firstVisibleItem;        }        for (int childIndex = 0; childIndex < visibleItemCount; childIndex++) {            int position = firstVisibleItem + childIndex;            int viewType = adapter.getItemViewType(position);            if (isItemViewTypePinned(adapter, viewType)) return position;        }        return -1;    }    int findCurrentSectionPosition(int fromPosition) {        ListAdapter adapter = getAdapter();        if (fromPosition >= adapter.getCount()) return -1;        if (adapter instanceof SectionIndexer) {            SectionIndexer indexer = (SectionIndexer) adapter;            int sectionPosition = indexer.getSectionForPosition(fromPosition);            int itemPosition = indexer.getPositionForSection(sectionPosition);            int typeView = adapter.getItemViewType(itemPosition);            if (isItemViewTypePinned(adapter, typeView)) {                return itemPosition;            }        }        for (int position = fromPosition; position >= 0; position--) {            int viewType = adapter.getItemViewType(position);            if (isItemViewTypePinned(adapter, viewType)) return position;        }        return -1;    }    void recreatePinnedShadow() {        destroyPinnedShadow();        ListAdapter adapter = getAdapter();        if (adapter != null && adapter.getCount() > 0) {            int firstVisiblePosition = getFirstVisiblePosition();            int sectionPosition = findCurrentSectionPosition(firstVisiblePosition);            if (sectionPosition == -1) return;            ensureShadowForPosition(sectionPosition,                    firstVisiblePosition, getLastVisiblePosition() - firstVisiblePosition);        }    }    @Override    public void setOnScrollListener(OnScrollListener listener) {        if (listener == mOnScrollListener) {            super.setOnScrollListener(listener);        } else {            mDelegateOnScrollListener = listener;        }    }    @Override    public void onRestoreInstanceState(Parcelable state) {        super.onRestoreInstanceState(state);        post(new Runnable() {            @Override            public void run() {                recreatePinnedShadow();            }        });    }    @Override    public void setAdapter(ListAdapter adapter) {        if (adapter != null) {            if (!(adapter instanceof PinnedSectionListAdapter))                throw new IllegalArgumentException("Does your adapter implement PinnedSectionListAdapter?");            if (adapter.getViewTypeCount() < 2)                throw new IllegalArgumentException("Does your adapter handle at least two types" +                        " of views in getViewTypeCount() method: items and sections?");        }        ListAdapter oldAdapter = getAdapter();        if (oldAdapter != null) oldAdapter.unregisterDataSetObserver(mDataSetObserver);        if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver);        if (oldAdapter != adapter) destroyPinnedShadow();        super.setAdapter(adapter);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);        if (mPinnedSection != null) {            int parentWidth = r - l - getPaddingLeft() - getPaddingRight();            int shadowWidth = mPinnedSection.view.getWidth();            if (parentWidth != shadowWidth) {                recreatePinnedShadow();            }        }    }    @Override    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);        if (mPinnedSection != null) {            int pLeft = getListPaddingLeft();            int pTop = getListPaddingTop();            View view = mPinnedSection.view;            canvas.save();            int clipHeight = view.getHeight() +                    (mShadowDrawable == null ? 0 : Math.min(mShadowHeight, mSectionsDistanceY));            canvas.clipRect(pLeft, pTop, pLeft + view.getWidth(), pTop + clipHeight);            canvas.translate(pLeft, pTop + mTranslateY);            drawChild(canvas, mPinnedSection.view, getDrawingTime());            if (mShadowDrawable != null && mSectionsDistanceY > 0) {                mShadowDrawable.setBounds(mPinnedSection.view.getLeft(),                        mPinnedSection.view.getBottom(),                        mPinnedSection.view.getRight(),                        mPinnedSection.view.getBottom() + mShadowHeight);                mShadowDrawable.draw(canvas);            }            canvas.restore();        }    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        final float x = ev.getX();        final float y = ev.getY();        final int action = ev.getAction();        if (action == MotionEvent.ACTION_DOWN                && mTouchTarget == null                && mPinnedSection != null                && isPinnedViewTouched(mPinnedSection.view, x, y)) {            mTouchTarget = mPinnedSection.view;            mTouchPoint.x = x;            mTouchPoint.y = y;            mDownEvent = MotionEvent.obtain(ev);        }        if (mTouchTarget != null) {            if (isPinnedViewTouched(mTouchTarget, x, y)) {                mTouchTarget.dispatchTouchEvent(ev);            }            if (action == MotionEvent.ACTION_UP) {                super.dispatchTouchEvent(ev);                performPinnedItemClick();                clearTouchTarget();            } else if (action == MotionEvent.ACTION_CANCEL) {                clearTouchTarget();            } else if (action == MotionEvent.ACTION_MOVE) {                if (Math.abs(y - mTouchPoint.y) > mTouchSlop) {                    MotionEvent event = MotionEvent.obtain(ev);                    event.setAction(MotionEvent.ACTION_CANCEL);                    mTouchTarget.dispatchTouchEvent(event);                    event.recycle();                    super.dispatchTouchEvent(mDownEvent);                    super.dispatchTouchEvent(ev);                    clearTouchTarget();                }            }            return true;        }        return super.dispatchTouchEvent(ev);    }    private boolean isPinnedViewTouched(View view, float x, float y) {        view.getHitRect(mTouchRect);        mTouchRect.top += mTranslateY;        mTouchRect.bottom += mTranslateY + getPaddingTop();        mTouchRect.left += getPaddingLeft();        mTouchRect.right -= getPaddingRight();        return mTouchRect.contains((int) x, (int) y);    }    private void clearTouchTarget() {        mTouchTarget = null;        if (mDownEvent != null) {            mDownEvent.recycle();            mDownEvent = null;        }    }    private boolean performPinnedItemClick() {        if (mPinnedSection == null) return false;        OnItemClickListener listener = getOnItemClickListener();        if (listener != null && getAdapter().isEnabled(mPinnedSection.position)) {            View view = mPinnedSection.view;            playSoundEffect(SoundEffectConstants.CLICK);            if (view != null) {                view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);            }            listener.onItemClick(this, view, mPinnedSection.position, mPinnedSection.id);            return true;        }        return false;    }    public static boolean isItemViewTypePinned(ListAdapter adapter, int viewType) {        if (adapter instanceof HeaderViewListAdapter) {            adapter = ((HeaderViewListAdapter) adapter).getWrappedAdapter();        }        return ((PinnedSectionListAdapter) adapter).isItemViewTypePinned(viewType);    }}

附:仿支付宝账单流水
饼状图:http://blog.csdn.net/kuangxiaoguo0123/article/details/53282809

0 0
原创粉丝点击