Android开源项目-XListView

来源:互联网 发布:python量化投资之路 编辑:程序博客网 时间:2024/05/29 17:42
        XListView是一个含有下拉刷新、上拉加载功能的Android开源项目。特点是代码量极少(只有3个java文件和2个xml文件即可实现),虽然功能较为单一,但是在项目开发中很实用。并且由于代码量小,可以很轻松的理解下拉刷新和下拉加载的源码机制,并加以自定制。
话不多说
直接开干
需要的三个类直接复制就可以用了
第一个类


/** * @file XListView.java * @package me.maxwin.view * @create Mar 18, 2012 6:28:41 PM * @author Maxwin * @description An ListView support (a) Pull down to refresh, (b) Pull up to load more. *        Implement IXListViewListener, and see stopRefresh() / stopLoadMore(). */package com.example.learnxlistview.view.list;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewTreeObserver.OnGlobalLayoutListener;import android.view.animation.DecelerateInterpolator;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ListAdapter;import android.widget.ListView;import android.widget.RelativeLayout;import android.widget.Scroller;import android.widget.TextView;import com.example.learnxlistview.R;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;   // -- 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);      // init header height      mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(            new OnGlobalLayoutListener() {               @Override               public void onGlobalLayout() {                  mHeaderViewHeight = mHeaderViewContent.getHeight();                  getViewTreeObserver()                        .removeGlobalOnLayoutListener(this);               }            });   }   @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) {         mFooterView.hide();         mFooterView.setOnClickListener(null);         //make sure "pull up" don't show a line in bottom when listview with one page          setFooterDividersEnabled(false);      } else {         mPullLoading = false;         mFooterView.show();         mFooterView.setState(XListViewFooter.STATE_NORMAL);         //make sure "pull up" don't show a line in bottom when listview with one page           setFooterDividersEnabled(true);         // 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();      }   }   /**    * stop load more, reset footer view.    */   public void stopLoadMore() {      if (mPullLoading == true) {         mPullLoading = false;         mFooterView.setState(XListViewFooter.STATE_NORMAL);      }   }   /**    * 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) {      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   }   /**    * 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;      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() {      mPullLoading = true;      mFooterView.setState(XListViewFooter.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 (getFirstVisiblePosition() == 0               && (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {            // the first item is showing, header has shown or pull down.            updateHeaderHeight(deltaY / OFFSET_RADIO);            invokeOnScrolling();         } else if (getLastVisiblePosition() == mTotalItemCount - 1               && (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {            // last item, already pulled up or want to pull up.            updateFooterHeight(-deltaY / OFFSET_RADIO);         }         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) {                  mListViewListener.onRefresh();               }            }            resetHeaderHeight();         } else if (getLastVisiblePosition() == mTotalItemCount - 1) {            // invoke load more.            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) {            mHeaderView.setVisiableHeight(mScroller.getCurrY());         } 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) {      // send to user's listener      mTotalItemCount = totalItemCount;      if (mScrollListener != null) {         mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,               totalItemCount);      }   }   public void setXListViewListener(IXListViewListener l) {      mListViewListener = 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();   }}

第二个类 这里主要是下拉头的使用

/** * @file XFooterView.java * @create Mar 31, 2012 9:33:43 PM * @author Maxwin * @description XListView's footer */package com.example.learnxlistview.view.list;import android.content.Context;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.TextView;import com.example.learnxlistview.R;public class XListViewFooter 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 mHintView;      public XListViewFooter(Context context) {      super(context);      initView(context);   }      public XListViewFooter(Context context, AttributeSet attrs) {      super(context, attrs);      initView(context);   }      public void setState(int state) {      mHintView.setVisibility(View.INVISIBLE);      mProgressBar.setVisibility(View.INVISIBLE);      mHintView.setVisibility(View.INVISIBLE);      if (state == STATE_READY) {         mHintView.setVisibility(View.VISIBLE);         mHintView.setText(R.string.xlistview_footer_hint_ready);      } else if (state == STATE_LOADING) {         mProgressBar.setVisibility(View.VISIBLE);      } else {         mHintView.setVisibility(View.VISIBLE);         mHintView.setText(R.string.xlistview_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;   }         /**    * normal status    */   public void normal() {      mHintView.setVisibility(View.VISIBLE);      mProgressBar.setVisibility(View.GONE);   }         /**    * loading status     */   public void loading() {      mHintView.setVisibility(View.GONE);      mProgressBar.setVisibility(View.VISIBLE);   }      /**    * hide footer when disable pull load more    */   public void hide() {      LayoutParams lp = (LayoutParams)mContentView.getLayoutParams();      lp.height = 0;      mContentView.setLayoutParams(lp);   }      /**    * show footer    */   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.xlistview_footer, null);      addView(moreView);      moreView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));            mContentView = moreView.findViewById(R.id.xlistview_footer_content);      mProgressBar = moreView.findViewById(R.id.xlistview_footer_progressbar);      mHintView = (TextView)moreView.findViewById(R.id.xlistview_footer_hint_textview);   }      }


第三个类 对脚布局使用下拉加载更多的

/** * @file XListViewHeader.java * @create Apr 18, 2012 5:22:27 PM * @author Maxwin * @description XListView's header */package com.example.learnxlistview.view.list;import android.content.Context;import android.util.AttributeSet;import android.view.Gravity;import android.view.LayoutInflater;import android.view.View;import android.view.animation.Animation;import android.view.animation.RotateAnimation;import android.widget.ImageView;import android.widget.LinearLayout;import android.widget.ProgressBar;import android.widget.TextView;import com.example.learnxlistview.R;public class XListViewHeader extends LinearLayout {   private LinearLayout mContainer;   private ImageView mArrowImageView;   private ProgressBar mProgressBar;   private TextView mHintTextView;   private int mState = STATE_NORMAL;   private Animation mRotateUpAnim;   private Animation mRotateDownAnim;      private final int ROTATE_ANIM_DURATION = 180;      public final static int STATE_NORMAL = 0;   public final static int STATE_READY = 1;   public final static int STATE_REFRESHING = 2;   public XListViewHeader(Context context) {      super(context);      initView(context);   }   /**    * @param context    * @param attrs    */   public XListViewHeader(Context context, AttributeSet attrs) {      super(context, attrs);      initView(context);   }   private void initView(Context context) {      // 初始情况,设置下拉刷新view高度为0      LayoutParams lp = new LayoutParams(            LayoutParams.FILL_PARENT, 0);      mContainer = (LinearLayout) LayoutInflater.from(context).inflate(            R.layout.xlistview_header, null);      addView(mContainer, lp);      setGravity(Gravity.BOTTOM);      mArrowImageView = (ImageView)findViewById(R.id.xlistview_header_arrow);      mHintTextView = (TextView)findViewById(R.id.xlistview_header_hint_textview);      mProgressBar = (ProgressBar)findViewById(R.id.xlistview_header_progressbar);            mRotateUpAnim = new RotateAnimation(0.0f, -180.0f,            Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,            0.5f);      mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION);      mRotateUpAnim.setFillAfter(true);      mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f,            Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,            0.5f);      mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION);      mRotateDownAnim.setFillAfter(true);   }   public void setState(int state) {      if (state == mState) return ;            if (state == STATE_REFRESHING) {   // 显示进度         mArrowImageView.clearAnimation();         mArrowImageView.setVisibility(View.INVISIBLE);         mProgressBar.setVisibility(View.VISIBLE);      } else {   // 显示箭头图片         mArrowImageView.setVisibility(View.VISIBLE);         mProgressBar.setVisibility(View.INVISIBLE);      }            switch(state){      case STATE_NORMAL:         if (mState == STATE_READY) {            mArrowImageView.startAnimation(mRotateDownAnim);         }         if (mState == STATE_REFRESHING) {            mArrowImageView.clearAnimation();         }         mHintTextView.setText(R.string.xlistview_header_hint_normal);         break;      case STATE_READY:         if (mState != STATE_READY) {            mArrowImageView.clearAnimation();            mArrowImageView.startAnimation(mRotateUpAnim);            mHintTextView.setText(R.string.xlistview_header_hint_ready);         }         break;      case STATE_REFRESHING:         mHintTextView.setText(R.string.xlistview_header_hint_loading);         break;         default:      }            mState = state;   }      public void setVisiableHeight(int height) {      if (height < 0)         height = 0;      LayoutParams lp = (LayoutParams) mContainer            .getLayoutParams();      lp.height = height;      mContainer.setLayoutParams(lp);   }   public int getVisiableHeight() {      return mContainer.getLayoutParams().height;   }}


主activity的布局

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.example.learnxlistview.MainActivity">    <com.example.learnxlistview.view.list.XListView        android:layout_width="match_parent"        android:id="@+id/xlv"        android:layout_height="match_parent"></com.example.learnxlistview.view.list.XListView></RelativeLayout>

需要用到的头布局

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="wrap_content"    android:gravity="bottom" >    <RelativeLayout        android:id="@+id/xlistview_header_content"        android:layout_width="fill_parent"        android:layout_height="60dp" >        <LinearLayout            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:gravity="center"            android:orientation="vertical" android:id="@+id/xlistview_header_text">            <TextView                android:id="@+id/xlistview_header_hint_textview"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="@string/xlistview_header_hint_normal" />            <LinearLayout                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_marginTop="3dp" >                <TextView                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:text="@string/xlistview_header_last_time"                    android:textSize="12sp" />                <TextView                    android:id="@+id/xlistview_header_time"                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:textSize="12sp" />            </LinearLayout>        </LinearLayout>        <ImageView            android:id="@+id/xlistview_header_arrow"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_alignLeft="@id/xlistview_header_text"            android:layout_centerVertical="true"            android:layout_marginLeft="-35dp"            android:src="@mipmap/xlistview_arrow" />        <ProgressBar            android:id="@+id/xlistview_header_progressbar"            android:layout_width="30dp"            android:layout_height="30dp"            android:layout_alignLeft="@id/xlistview_header_text"            android:layout_centerVertical="true"            android:layout_marginLeft="-40dp"            android:visibility="invisible" />    </RelativeLayout></LinearLayout>


下拉脚的布局


<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="wrap_content" >    <RelativeLayout        android:id="@+id/xlistview_footer_content"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:padding="10dp" >        <ProgressBar            android:id="@+id/xlistview_footer_progressbar"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:visibility="invisible" />        <TextView            android:id="@+id/xlistview_footer_hint_textview"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:text="@string/xlistview_footer_hint_normal" />    </RelativeLayout></LinearLayout>


下边这个就是重点了


package com.example.learnxlistview;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.widget.ArrayAdapter;import com.example.learnxlistview.view.list.XListView;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity {    XListView xlv;    List<String> list;    ArrayAdapter<String> adapter;    Handler handler=new Handler(){        @Override        public void handleMessage(Message msg) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        xlv= (XListView) findViewById(R.id.xlv);        initData();        adapter=new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_list_item_1,list);        xlv.setAdapter(adapter);        xlv.setPullRefreshEnable(true);        xlv.setPullLoadEnable(true);        xlv.setRefreshTime("2017-8-11");        xlv.setXListViewListener(new XListView.IXListViewListener() {            // 刷新方法            @Override            public void onRefresh() {                // TODO Auto-generated method stub                // 发起网络请求 刷新数据                handler.postDelayed(new Runnable() {                    @Override                    public void run() {                        // TODO Auto-generated method stub                        // 把数据加载在list结合的哪个位置上                        list.add(0, "我是刷新来的数据");                        adapter.notifyDataSetChanged();                        xlv.stopRefresh();                        //例如 list1 = {{12345},{12345},{12345}}                        //    list2 = {345678}                        //list1.addAll(list2)                        //list1 = {12345345678}                        //list1.add(3)                        //list1.add(list2) = {{12345},{12345},{12345}list2}                        //list2 = {{12345},{12345},{12345}}                        //list2.addAll(list2) = {{12345},{12345},{12345},{12345},{12345},{12345} }                        //list.addAll("拼接一个list结合");                    }                }, 2000);            }            // 加载更多方法            @Override            public void onLoadMore() {                // TODO Auto-generated method stub                // 发起网络请求 把请求来的数据 拼接在后面                handler.postDelayed(new Runnable() {                    @Override                    public void run() {                        // TODO Auto-generated method stub                        list.add("我是加载来的数据");                        adapter.notifyDataSetChanged();                        xlv.stopLoadMore();                    }                }, 2000);            }        });    }    private void initData() {        list = new ArrayList<String>();        for (int i = 0; i < 100; i++) {            list.add("内容" + i);        }    }}

具体效果怎么样呢?
来看一下


可以看到效果一般 但是是一套很好使用的模版
最后附上可以下载源码的链接

https://github.com/Maxwin-z/XListView-Android

其中有一个fork库完善下拉刷新上拉加载更多的功能并实现自动刷新以及自动加载等功能, 并增加对ScrollView的支持。建议直接使用这个即可。本文即使用该库。

https://github.com/MarkMjw/PullToRefresh



作者:Tsy远
链接:http://www.jianshu.com/p/9c40c0968f4b
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


这是需要的一行图片

原创粉丝点击