ListView扩展上拉加载更多,下拉刷新

来源:互联网 发布:mac safari 编辑:程序博客网 时间:2024/06/04 20:04

一、加载过程动态展示动画


       在APP的研发中,加载过程用动画更改时间的消耗,增强用户体验。而有个更精细的加载过程动画,会不断从细节优化APP的体验。且随着APP与服务器交互的增多,加载状态的表示占有越来越重要的地位。

       当前使用Android帧动画,应用较多数量图片,形成“沙漏”计时器。

       Android自定义控件主要有以下实现步骤:

1,定义属性;

2,重写构造方法,获取属性定义;

3,重写OnDraw();

4,重写OnMeasure()。

       形成的自定义控件可与之前的Android自带控件一样使用。

       加载状态是一个自定义控件,但是其实现过程并没有那么复杂 @_@

类的实现:

public class ProgressBarLoading extends ProgressBar{private Context mContext;public ProgressBarLoading(Context context) {super(context);this.mContext = context;initViews();}public ProgressBarLoading(Context context, AttributeSet attrs) {super(context, attrs);this.mContext = context;initViews();}private void initViews() {checkProgressbarDrawable(R.anim.loading_anim, this);}/** * 图片帧动画高版本适配方案 * @param @param resId * @param @param p  * @author zhuanggy * @date 2016-1-19 */private void checkProgressbarDrawable(int resId, ProgressBar p) {if (android.os.Build.VERSION.SDK_INT >= 23) {try {Drawable drawable = MainApplication.getInstance().getResources().getDrawable(resId);p.setIndeterminateDrawable(drawable);p.setIndeterminate(true);// p.requestLayout();} catch (Exception e) {e.printStackTrace();}}}}
loading_anim.xml的实现:

<?xml version="1.0" encoding="utf-8"?><animation-list xmlns:android="http://schemas.android.com/apk/res/android">    <item android:drawable="@mipmap/ic_loading_common0" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common1" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common2" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common3" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common4" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common5" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common6" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common7" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common8" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common9" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common10" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common11" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common12" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common13" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common14" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common15" android:duration="200"/>    <item android:drawable="@mipmap/ic_loading_common16" android:duration="200"/></animation-list>
以下是实现效果:


还是老规矩,这里是源码!!


二、XlistView的使用


XlistView的源码实现:

public class XListView extends ListView implements OnScrollListener {    private float mLastY = -1; // save event y    private Scroller mScroller; // used for scroll back    private OnScrollListener mScrollListener; // user's scroll listener    // the interface to trigger refresh and load more.    private IXListViewListener mListViewListener;    private IXListViewOnTouchListener mListviewTouchListener;    // -- header view    private XListViewHeader mHeaderView;    // header view content, use it to calculate the Header's height. And hide it    // when disable pull refresh.    private RelativeLayout mHeaderViewContent;    // private TextView mHeaderTimeView;    private int mHeaderViewHeight; // header view's height    private boolean mEnablePullRefresh = true;    private boolean mPullRefreshing = false; // is refreashing.    // -- footer view    private XListViewFooter mFooterView;    private boolean mEnablePullLoad;    private boolean mPullLoading;    private boolean mIsFooterReady = false;    // total list items, used to detect is at the bottom of listview.    private int mTotalItemCount;    // for mScroller, scroll back from header or footer.    private int mScrollBack;    private final static int SCROLLBACK_HEADER = 0;    private final static int SCROLLBACK_FOOTER = 1;    private final static int SCROLL_DURATION = 400; // scroll back duration    private final static int PULL_LOAD_MORE_DELTA = 50; // when pull up >= 50px    // at bottom, trigger    // load more.    private final static float OFFSET_RADIO = 1.8f; // support iOS like pull    // feature.    /**     * @param context     */    public XListView(Context context) {        super(context);        initWithContext(context);    }    public XListView(Context context, AttributeSet attrs) {        super(context, attrs);        initWithContext(context);    }    public XListView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        initWithContext(context);    }    private void initWithContext(Context context) {        mScroller = new Scroller(context, new DecelerateInterpolator());        // XListView need the scroll event, and it will dispatch the event to        // user's listener (as a proxy).        super.setOnScrollListener(this);        // init header view        mHeaderView = new XListViewHeader(context);        mHeaderViewContent = (RelativeLayout) mHeaderView.findViewById(R.id.xlistview_header_content);        // mHeaderTimeView = (TextView)        // mHeaderView.findViewById(R.id.xlistview_header_time);        addHeaderView(mHeaderView);        // init footer view        mFooterView = new XListViewFooter(context);        setFadingEdgeLength(0);// 去掉边缘阴影        // init header height        mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                mHeaderViewHeight = mHeaderViewContent.getHeight();                getViewTreeObserver().removeGlobalOnLayoutListener(this);            }        });        // setFastScrollEnabled(true);    }    @Override    public void setAdapter(ListAdapter adapter) {        // make sure XListViewFooter is the last footer view, and only add once.        if (mIsFooterReady == false) {            mIsFooterReady = true;            addFooterView(mFooterView);        }        super.setAdapter(adapter);    }    /**     * enable or disable pull down refresh feature.     *     * @param enable     */    public void setPullRefreshEnable(boolean enable) {        mEnablePullRefresh = enable;        if (!mEnablePullRefresh) { // disable, hide the content            mHeaderViewContent.setVisibility(View.INVISIBLE);        } else {            mHeaderViewContent.setVisibility(View.VISIBLE);        }    }    /**     * enable or disable pull up load more feature.     *     * @param enable     */    public void setPullLoadEnable(boolean enable) {        mEnablePullLoad = enable;        if (!mEnablePullLoad) {            mPullLoading = false;            mFooterView.hide();            mFooterView.setOnClickListener(null);        } else {            mPullLoading = false;            mFooterView.show();            mFooterView.setState(XListViewFooter.STATE_NORMAL);            // both "pull up" and "click" will invoke load more.            mFooterView.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    startLoadMore();                }            });        }    }    /**     * stop refresh, reset header view.     */    public void stopRefresh() {        if (mPullRefreshing == true) {            mPullRefreshing = false;            resetHeaderHeight();            mHeaderView.setState(XListViewHeader.STATE_NORMAL);        }    }    /**     * stop load more, reset footer view.     */    public void stopLoadMore() {        Log.e("", "stopLoadMore");        // if (mPullLoading) {// == true        Log.e("", "完成载入.............");        mPullLoading = false;        mFooterView.setState(XListViewFooter.STATE_NORMAL);        // }    }    public boolean isLoadingMore() {        return mPullLoading;    }    public boolean isRefresh() {        return mPullRefreshing;    }    /**     * set last refresh time     *     * @param time     */    public void setRefreshTime(String time) {        // mHeaderTimeView.setText(time);    }    private void invokeOnScrolling() {        if (mScrollListener instanceof OnXScrollListener) {            OnXScrollListener l = (OnXScrollListener) mScrollListener;            l.onXScrolling(this);        }    }    private void updateHeaderHeight(float delta) {        Log.e("", "delta=" + delta);        mHeaderView.setVisiableHeight((int) delta + mHeaderView.getVisiableHeight());        if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态,更新箭头            if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {                mHeaderView.setState(XListViewHeader.STATE_READY);            } else {                mHeaderView.setState(XListViewHeader.STATE_NORMAL);            }        }        setSelection(0); // scroll to top each time    }    public void setHeadRefreshShowing() {        if (mHeaderView.getState() != XListViewHeader.STATE_REFRESHING) {            mEnablePullRefresh = true;            mPullRefreshing = true;            Log.e("", "mHeaderViewHeight=" + mHeaderViewHeight);            // AbsListView.LayoutParams lp = (AbsListView.LayoutParams)            // mHeaderView.getLayoutParams();            // lp.height = 0;            // mHeaderView.setLayoutParams(lp);            mHeaderView.setVisiableHeight(mHeaderViewHeight);            setSelection(0); // scroll to top each time            mHeaderView.setState(XListViewHeader.STATE_REFRESHING);            mScrollBack = SCROLLBACK_HEADER;        }        // mScroller.startScroll(0, mHeaderViewHeight, 0, -mHeaderViewHeight/2,        // SCROLL_DURATION);    }    /**     * reset header view's height.     */    private void resetHeaderHeight() {        int height = mHeaderView.getVisiableHeight();        if (height == 0) // not visible.            return;        // refreshing and header isn't shown fully. do nothing.        if (mPullRefreshing && height <= mHeaderViewHeight) {            return;        }        int finalHeight = 0; // default: scroll back to dismiss header.        // is refreshing, just scroll back to show all the header.        if (mPullRefreshing && height > mHeaderViewHeight) {            finalHeight = mHeaderViewHeight;        }        mScrollBack = SCROLLBACK_HEADER;        mScroller.startScroll(0, height, 0, finalHeight - height, SCROLL_DURATION);        // trigger computeScroll        invalidate();    }    private void updateFooterHeight(float delta) {        int height = mFooterView.getBottomMargin() + (int) delta;        Log.e("", "height=" + height);        if (mEnablePullLoad && !mPullLoading) {            if (height > PULL_LOAD_MORE_DELTA) { // height enough to invoke load                // more.                mFooterView.setState(XListViewFooter.STATE_READY);            } else {                mFooterView.setState(XListViewFooter.STATE_NORMAL);            }        }        mFooterView.setBottomMargin(height);        // setSelection(mTotalItemCount - 1); // scroll to bottom    }    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() {        mFooterView.setState(XListViewFooter.STATE_LOADING);        if (mListViewListener != null) {            mListViewListener.onLoadMore();        }        if (mFooterView.getState() == XListViewFooter.STATE_LOADING) {            mPullLoading = true;        }    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        if (mLastY == -1) {            mLastY = ev.getRawY();        }        switch (ev.getAction()) {            case MotionEvent.ACTION_DOWN:                mLastY = ev.getRawY();                if (mListviewTouchListener != null) {                    mListviewTouchListener.onTouchDown();                }                break;            case MotionEvent.ACTION_MOVE:                final float deltaY = ev.getRawY() - mLastY;                mLastY = ev.getRawY();                if (getFirstVisiblePosition() == 0) {                    // the first item is showing, header has shown or pull down.                    if ((mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {                        updateHeaderHeight(deltaY / OFFSET_RADIO);                        invokeOnScrolling();                    }                } else if (getLastVisiblePosition() == mTotalItemCount - 1) {                    // last item, already pulled up or want to pull up.                    Log.e("", "mFooterView.getBottomMargin()=" + mFooterView.getBottomMargin() + "deltaY=" + deltaY);                    if ((mFooterView.getBottomMargin() > 0 || deltaY < 0)) {                        updateFooterHeight(-deltaY / OFFSET_RADIO);                    } else {                        if (!mPullLoading) {                            mFooterView.setState(XListViewFooter.STATE_NORMAL);                        }                    }                }                break;            default:                mLastY = -1; // reset                if (getFirstVisiblePosition() == 0) {                    // invoke refresh                    if (mEnablePullRefresh && mHeaderView.getVisiableHeight() > mHeaderViewHeight) {                        mPullRefreshing = true;                        mHeaderView.setState(XListViewHeader.STATE_REFRESHING);                        if (mListViewListener != null) {                            new Handler().postDelayed(new Runnable() {                                @Override                                public void run() {                                    mListViewListener.onRefresh();                                }                            }, 1000);                        }                    }                    resetHeaderHeight();                    if (mFooterView.isShowingReady()) {// 上提时,如果列表不满一页,列表没向上滑动,松开载入更多                        startLoadMore();                    }                } else if (getLastVisiblePosition() == mTotalItemCount - 1) {                    // invoke load more.                    if (mEnablePullLoad && mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA && mFooterView.isShowingReady()) {                        startLoadMore();                    }                    resetFooterHeight();                }                break;        }        return super.onTouchEvent(ev);    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {            if (mScrollBack == SCROLLBACK_HEADER) {                mHeaderView.setVisiableHeight(mScroller.getCurrY());            } else {                mFooterView.setBottomMargin(mScroller.getCurrY());            }            postInvalidate();            invokeOnScrolling();        }        super.computeScroll();    }    @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) {        // send to user's listener        mTotalItemCount = totalItemCount;        if (mScrollListener != null) {            mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);        }    }    public void setXListViewListener(IXListViewListener l) {        mListViewListener = l;    }    public void setXListViewOnTouchListener(IXListViewOnTouchListener l) {        mListviewTouchListener = l;    }    public void setXListViewOnScrollListener(OnXScrollListener l) {        mScrollListener = l;    }    /**     * you can listen ListView.OnScrollListener or this one. it will invoke     * onXScrolling when header/footer scroll back.     */    public interface OnXScrollListener extends OnScrollListener {        public void onXScrolling(View view);    }    /**     * implements this interface to get refresh/load more event.     */    public interface IXListViewListener {        public void onRefresh();        public void onLoadMore();    }    public interface IXListViewOnTouchListener {        public void onTouchDown();    }}
       相比于其他控件实现,这里还需要实现头布局、尾布局。黏贴代码过于长,请参考XlistViewDemo
       以下是使用的xml文件:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <com.future.xlistviewdemo.view.XListView        android:id="@+id/xlistview_body"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:cacheColorHint="@color/transparent" /></LinearLayout>
         不错,这里是展示效果:


          这里是源码Demo      ~_~






努力不应该是某种需要被时常觉知的东西,意志力是短期内会用完的精神能量。
真正坚持到最后的人靠的不是激情,而是恰到好处的喜欢和投入!


1 0
原创粉丝点击