PullToRefresh和SwipeRefreshLayout的使用

爆米花9958 2016-09-12

android 布局 PTR ADD string void list

 

PullToRefresh是一套实现非常好的下拉刷新库(下载地址:https://github.com/chrisbanes/Android-PullToRefresh),它支持:

1.ListView

2.ExpandableListView

3.GridView

4.WebView

5.ScrollView

6.Horizontal ScrollView

7.ViewPager

等多种常用的需要刷新的View类型,而且使用起来也十分方便。下载下来解压里面有extras,library,sample这三个文件夹。sample是demo,libarary是类库,我们主要用到的是library这个文件夹。下面就用官网给的demo来具体说明如何使用吧,下面介绍下ListView的使用

使用时直接在布局中引用即可

<com.handmark.pulltorefresh.library.PullToRefreshListView
    android:id="@+id/pull_refresh_list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:cacheColorHint="#00000000"
    android:divider="#19000000"
    android:dividerHeight="4dp"
    android:fadingEdge="none"
    android:fastScrollEnabled="false"
    android:footerDividersEnabled="false"
    android:headerDividersEnabled="false"
    android:smoothScrollbar="true" />

在其他布局文件中,我们也许会看到xmlns:ptr="http://schemas.android.com/apk/res-auto"这个自定义的命名空间,看一下这个控件里面的一些基本属性:

          1.ptr:ptrDrawable="";//这里可以设置自己的上拉下拉图标。

          2.ptr:ptrHeaderBackground="";//上拉时底部的背景色,下拉时头部的背景色

          3.ptr:ptrHeaderTextColor="";//上拉,下拉时Header,Footer显示的字体颜色

          4.ptr:ptrHeaderSubTextColor="";//上拉,下拉Header,Footer中上次刷新时间的颜色

          5.ptr:ptrShowIndicator="";//true时会在控件的右上角和右下角出现设置的icon

          6.ptr:ptrAnimationStyle="";//显示时候图标的取值 ;flip:翻转;rotate:旋转

          7.ptr:ptrRotateDrawableWhilePulling="";//当动画为rotate时,下拉是否旋转

          8.ptr:ptrRefreshableViewBackground="";//设置整个控件布局的背景颜色

          9.ptr:ptrScrollingWhileRefreshingEnabled="";//刷新的时候是否允许ListView或者GridView滚动。推荐使用true

          10.ptr:prtListViewExtrasEnabled="";//决定Header,Footer以何种方式加入PullToRefreshListView.其中为true时,就是以Header的方式加入,在滚动刷新时头部会跟着一起滚动;为false时,就是以Footer的方式加入,在滚动的时候底部会跟着一起滚动。

          11.ptr:ptrMode="";//设置是上拉,下拉还是两者都支持。both:两者都支持;disabled:禁用下拉刷新;pullFromStart:仅支持下拉刷新;pullUpFromBottom:仅支持上拉加载;manualOnly:只允许手动触发。(注意:如果不在代码设置它默认的就只有下拉刷新)

以上只是一些基本属性:一般你用的什么控件它都包含原有控件的所有属性,如你使用的是PullToRefreshListView那么它里面同样包含ListView的一些属性。同样上面的属性你都可以在代码中去set***使用。

Activity代码:

public final class PullToRefreshListActivity extends ListActivity {

    static final int MENU_MANUAL_REFRESH = 0;
    static final int MENU_DISABLE_SCROLL = 1;
    static final int MENU_SET_MODE = 2;
    static final int MENU_DEMO = 3;

    private LinkedList<String> mListItems;
    private PullToRefreshListView mPullRefreshListView;
    private ArrayAdapter<String> mAdapter;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ptr_list);

        mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list);
//    mPullRefreshListView.setMode(Mode.BOTH);
        // Set a listener to be invoked when the list should be refreshed.
        mPullRefreshListView
                .setOnRefreshListener(
                        //双向滑动
//                new OnRefreshListener2<ListView>() {
//
//             @Override
//             public void onPullDownToRefresh(
//                   PullToRefreshBase<ListView> refreshView) {
//                // TODO Auto-generated method stub
//                new GetDataTask().execute();
//             }
//
//             @Override
//             public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) {
//                // TODO Auto-generated method stub
//                new GetDataTask().execute();
//             }
//             
//          }
//       );
                        //单方向滑动
                        new OnRefreshListener<ListView>() {
                            @Override
                            public void onRefresh(
                                    PullToRefreshBase<ListView> refreshView) {
                                String label = DateUtils.formatDateTime(
                                        getApplicationContext(),
                                        System.currentTimeMillis(),
                                        DateUtils.FORMAT_SHOW_TIME
                                                | DateUtils.FORMAT_SHOW_DATE
                                                | DateUtils.FORMAT_ABBREV_ALL);

                                // Update the LastUpdatedLabel
                                refreshView.getLoadingLayoutProxy()
                                        .setLastUpdatedLabel(label);

                                // Do work to refresh the list here.
                                new GetDataTask().execute();
                            }
                        });

        // Add an end-of-list listener
        mPullRefreshListView
                .setOnLastItemVisibleListener(new OnLastItemVisibleListener() {

                    @Override
                    public void onLastItemVisible() {
                        Toast.makeText(PullToRefreshListActivity.this,
                                "End of List!", Toast.LENGTH_SHORT).show();
                    }
                });

        ListView actualListView = mPullRefreshListView.getRefreshableView();

        // Need to use the Actual ListView when registering for Context Menu
        registerForContextMenu(actualListView);

        mListItems = new LinkedList<String>();
        mListItems.addAll(Arrays.asList(mStrings));

        mAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, mListItems);

        /**
         * Add Sound Event Listener
         */
        SoundPullEventListener<ListView> soundListener = new SoundPullEventListener<ListView>(
                this);
        soundListener.addSoundEvent(State.PULL_TO_REFRESH, R.raw.pull_event);
        soundListener.addSoundEvent(State.RESET, R.raw.reset_sound);
        soundListener.addSoundEvent(State.REFRESHING, R.raw.refreshing_sound);
        mPullRefreshListView.setOnPullEventListener(soundListener);

        // You can also just use setListAdapter(mAdapter) or
        // mPullRefreshListView.setAdapter(mAdapter)
        actualListView.setAdapter(mAdapter);
    }

    private class GetDataTask extends AsyncTask<Void, Void, String[]> {

        @Override
        protected String[] doInBackground(Void... params) {
            // Simulates a background job.
            try {
                Thread.sleep(4000);
            } catch (InterruptedException e) {
            }
            return mStrings;
        }

        @Override
        protected void onPostExecute(String[] result) {
            mListItems.addFirst("Added after refresh...");
            mAdapter.notifyDataSetChanged();

            // Call onRefreshComplete when the list has been refreshed.
            mPullRefreshListView.onRefreshComplete();

            super.onPostExecute(result);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, MENU_MANUAL_REFRESH, 0, "Manual Refresh");
        menu.add(
                0,
                MENU_DISABLE_SCROLL,
                1,
                mPullRefreshListView.isScrollingWhileRefreshingEnabled() ? "Disable Scrolling while Refreshing"
                        : "Enable Scrolling while Refreshing");
        menu.add(
                0,
                MENU_SET_MODE,
                0,
                mPullRefreshListView.getMode() == Mode.BOTH ? "Change to MODE_PULL_DOWN"
                        : "Change to MODE_PULL_BOTH");
        menu.add(0, MENU_DEMO, 0, "Demo");
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
                                    ContextMenuInfo menuInfo) {
        AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;

        menu.setHeaderTitle("Item: "
                + getListView().getItemAtPosition(info.position));
        menu.add("Item 1");
        menu.add("Item 2");
        menu.add("Item 3");
        menu.add("Item 4");

        super.onCreateContextMenu(menu, v, menuInfo);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem disableItem = menu.findItem(MENU_DISABLE_SCROLL);
        disableItem
                .setTitle(mPullRefreshListView
                        .isScrollingWhileRefreshingEnabled() ? "Disable Scrolling while Refreshing"
                        : "Enable Scrolling while Refreshing");

        MenuItem setModeItem = menu.findItem(MENU_SET_MODE);
        setModeItem
                .setTitle(mPullRefreshListView.getMode() == Mode.BOTH ? "Change to MODE_FROM_START"
                        : "Change to MODE_PULL_BOTH");

        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case MENU_MANUAL_REFRESH:
                new GetDataTask().execute();
                mPullRefreshListView.setRefreshing(false);
                break;
            case MENU_DISABLE_SCROLL:
                mPullRefreshListView
                        .setScrollingWhileRefreshingEnabled(!mPullRefreshListView
                                .isScrollingWhileRefreshingEnabled());
                break;
            case MENU_SET_MODE:
                mPullRefreshListView
                        .setMode(mPullRefreshListView.getMode() == Mode.BOTH ? Mode.PULL_FROM_START
                                : Mode.BOTH);
                break;
            case MENU_DEMO:
                mPullRefreshListView.demo();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

       
    private String[] mStrings = {"Abbaye de Belloc",
            "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi",
            "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu",
            "Airag", "Airedale", "Aisy Cendre", "Allgauer Emmentaler",
            "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam",
            "Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis",
            "Afuega'l Pitu", "Airag", "Airedale", "Aisy Cendre",
            "Allgauer Emmentaler"
    };
}

具体说下代码,初始化PullToRefreshListView,刷新模式可以在xml文件中进行设置,也可以在java代码中通过setMode()设置,然后设置监听器OnRefreshListener,但这个监听器只能监听一个方向的,如果要实现两个方向那么就使用OnRefreshListener2监听器,同时别忘了了设置模式为Mode.BOTH。

 

但是PullToRefresh作者已经好久不更新了,现在很多人也在此基础上做了很多的扩展,大家可以上网自己搜下。google官方在最新的android.support.v4包中增加了一个新类SwipeRefreshLayout,这个类的作用就是提供官方的下拉刷新,并且效果相当不错,而上拉加载更多则用我们自定义的listview,也是相当简单,我们在下面的例子就用一个最简单的例子去实现一个我们的上拉加载、下拉刷新的功能。它是一个viewgroup,但是它只允许有一个子控件,子控件能是任何view,使用的时候,所在类实现OnRefreshListener接口,在onRefresh()方法中实现所要完成的任务。好了,现在思路明确了,SwipeRefreshLayout已经实现了下拉刷新的功能,我们要做的就是实现一个能够上拉加载的组件,然后放到SwipeRefreshLayout中即可,好,上代码:

自定义一个ListView:

public class LoadMoreListView extends ListView implements OnScrollListener {
   /**
    * 滑动到最下面时的上拉操作
    */
   private int mTouchSlop;
   /**
    * ListView的加载中footer
    */
   private View mListViewFooter;
   /**
    * 上拉监听器, 到了最底部的上拉加载操作
    */
   private OnLoadMoreListener mOnLoadMoreListener;
   /**
    * 按下时的y坐标
    */
   private int mYDown;
   /**
    * 抬起时的y坐标, 与mYDown一起用于滑动到底部时判断是上拉还是下拉
    */
   private int mLastY;
   /**
    * 是否在加载中 ( 上拉加载更多 )
    */
   private boolean isLoading = false;

   /**
    * 加载更多的监听器
    */
   public static interface OnLoadMoreListener {
      public void onLoad();
   }

   public LoadMoreListView(Context context, AttributeSet attrs) {
      super(context, attrs);
      mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
      mListViewFooter = LayoutInflater.from(context).inflate(
            R.layout.listview_footer, null, false);
      this.addFooterView(mListViewFooter);
      mListViewFooter.setVisibility(View.GONE);
      this.setOnScrollListener(this);
   }

   public LoadMoreListView(Context context) {
      super(context);
      // TODO Auto-generated constructor stub
   }

   /**
    * @param loadMoreListener
    */
   public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {
      mOnLoadMoreListener = loadMoreListener;
   }

   @Override
   public boolean dispatchTouchEvent(MotionEvent event) {
      final int action = event.getAction();

      switch (action) {
      case MotionEvent.ACTION_DOWN:
         // 按下
         mYDown = (int) event.getRawY();
         break;

      case MotionEvent.ACTION_MOVE:
         // 移动
         mLastY = (int) event.getRawY();
         break;

      case MotionEvent.ACTION_UP:
         // 抬起
         if (canLoad()) {
            loadData();
         }
         break;
      default:
         break;
      }

      return super.dispatchTouchEvent(event);
   }

   /**
    * @param loading
    */
   public void setLoading(boolean loading) {
      isLoading = loading;
      if (isLoading) {
         mListViewFooter.setVisibility(View.VISIBLE);
      } else {
         mListViewFooter.setVisibility(View.GONE);
         mYDown = 0;
         mLastY = 0;
      }
   }

   @Override
   public void onScrollStateChanged(AbsListView view, int scrollState) {
      // 滚动时到了最底部也可以加载更多
      if (canLoad()) {
         loadData();
      }
   }

   @Override
   public void onScroll(AbsListView view, int firstVisibleItem,
         int visibleItemCount, int totalItemCount) {

   }

   /**
    * 是否可以加载更多, 条件是:到了最底部, listView不在加载中且为上拉操作.
    * 
    * @return
    */
   private boolean canLoad() {
      return isBottom() && !isLoading && isPullUp();
   }

   /**
    * 如果到了最底部,而且是上拉操作.那么执行onLoad方法
    */
   private void loadData() {
      if (mOnLoadMoreListener != null) {
         // 设置状态
         setLoading(true);
         //
         mOnLoadMoreListener.onLoad();
      }
   }

   /**
    * 判断是否到了最底部
    */
   private boolean isBottom() {

      if (this.getAdapter() != null) {
         return this.getLastVisiblePosition() == (this.getAdapter()
               .getCount() - 1);
      }
      return false;
   }

   /**
    * 是否是上拉操作
    * 
    * @return
    */
   private boolean isPullUp() {
      return (mYDown - mLastY) >= mTouchSlop;
   }

}

我们实现了OnScrollListener监听器,同时写了一个OnLoadMoreListener 监听器接口,然后在OnScrollListener中回调相关定义的方法,而接口的具体操作就需要我们在ListView的实现类中实现了

 

而主布局就相当简单了,直接在SwipeRefreshLayout布局中加入我们的ListView

<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.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <com.xmr.swiperefreshlayout.LoadMoreListView
            android:id="@+id/list"
            android:layout_width="match_parent"
            android:layout_height="match_parent" >
       </com.xmr.swiperefreshlayout.LoadMoreListView>
    </android.support.v4.widget.SwipeRefreshLayout>

</RelativeLayout>

MainActivity类如下:

public class MainActivity extends Activity implements OnRefreshListener,
      OnLoadMoreListener {
   private SwipeRefreshLayout swipeLayout;
   private LoadMoreListView listView;
   private ArrayAdapter<String> adapter;
   private List<String> list;

   @Override
   public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      initView();
      setData();
      setListener();
   }

   /**
    * 初始化布局
    */
   private void initView() {
      swipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_container);
      swipeLayout.setColorSchemeResources(R.color.color_bule2,
            R.color.color_bule, R.color.color_bule2, R.color.color_bule3);
      listView = (LoadMoreListView) findViewById(R.id.list);
      list = new ArrayList<String>();
      adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, list);
      listView.setAdapter(adapter);
   }

   /**
    * 添加数据
    */
   private void setData() {
      for (int i = 0; i < 10; i++) {
         list.add("itemText" + i);
      }
      adapter.notifyDataSetChanged();
   }

   /**
    * 设置监听
    */
   private void setListener() {
      swipeLayout.setOnRefreshListener(this);
      listView.setLoadMoreListener(this);
   }

   /**
    * 下拉刷新
    */
   @Override
   public void onRefresh() {
      swipeLayout.postDelayed(new Runnable() {

         @Override
         public void run() {
            // 更新数据 更新完后调用该方法结束刷新
            HashMap<String, String> map = new HashMap<String, String>();
            list.add("itemText" + "刷新");

            adapter.notifyDataSetChanged();
            swipeLayout.setRefreshing(false);
         }
      }, 2000);

   }

   /**
    * 加载更多
    */
   @Override
   public void onLoad() {
      swipeLayout.postDelayed(new Runnable() {
         @Override
         public void run() {
            // 更新数据 更新完后调用该方法结束刷新
            listView.setLoading(false);
            HashMap<String, String> map = new HashMap<String, String>();
            list.add("itemText"+ "更多");
            adapter.notifyDataSetChanged();
         }
      }, 2000);
   }

}

最终效果如下:



当然这只是一个最简单的实现,我们完全可以发挥我们的想象力去创造我们想要的效果,我这算是个抛砖引玉的效果吧,技术有限,多有不足之处,请大家多多指教。
参考:

https://github.com/chrisbanes/Android-PullToRefresh

http://blog.csdn.net/ueryueryuery/article/details/17440465/

http://www.itnose.net/detail/6341754.html

http://blog.csdn.net/hhhccckkk/article/details/46971835

http://www.2cto.com/kf/201505/399695.html

http://blog.csdn.net/hongshengpeng/article/details/50187259

http://www.cnblogs.com/JinyaoLi/p/3852424.html

http://blog.csdn.net/zhongkejingwang/article/details/38868463

https://github.com/bingoogolapple/BGARefreshLayout-Android

https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh

 

 

源代码

登录 后评论
下一篇
corcosa
16416人浏览
2019-10-08
相关推荐
Android UI库书签
432人浏览
2016-05-20 18:28:46
Android编程规范
577人浏览
2016-02-26 16:05:00
复杂recyclerView封装库
7604人浏览
2018-03-20 13:45:22
0
0
0
413