PullToRefresh Demo 浅析:ListView
ListView该项目是一个很简单的demo,介绍了一些PullToRefresh的常规设置。
首先关注一下布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <!-- The PullToRefreshListView replaces a standard ListView widget. --> <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" /> </LinearLayout>
从布局文件中可以看出:想要实现该开源项目的一些功能,首先要导入其相应的控件。
mPullRefreshListView = (PullToRefreshListView) findViewById(R.id.pull_refresh_list); // Set a listener to be invoked when the list should be refreshed. mPullRefreshListView.setOnRefreshListener(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(); } });
首先获取PullToRefreshListView控件,之后设置该空间相应的监听器,对于PullToRefresh项目来说,必须要配置相应的一个或多个监听器。而配置的监听器种类主要还是根据刷新的类型来决定的。比如该Demo中包含有三种模式:
- Mode.BOTH:同时支持上拉下拉
- Mode.PULL_FROM_START:只支持下拉Pulling Down
- Mode.PULL_FROM_END:只支持上拉Pulling Up
如果Mode设置成Mode.BOTH,需要设置刷新Listener为OnRefreshListener2,并实现onPullDownToRefresh()、onPullUpToRefresh()两个方法。
如果Mode设置成Mode.PULL_FROM_START或Mode.PULL_FROM_END,需要设置刷新Listener为OnRefreshListener,同时实现onRefresh()方法。当然也可以设置为OnRefreshListener2,但是Mode.PULL_FROM_START的时候只调用onPullDownToRefresh()方法,Mode.PULL_FROM的时候只调用onPullUpToRefresh()方法.
new GetDataTask().execute();
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); } }
它继承了AsyncTask<Void, Void, String[]> 这个类:
参考:http://blog.sina.com.cn/s/blog_6513b5cf0101ceqn.html
AsyncTask可以使得使用UI线程变的更容易更适当,它可以在后台运行一些操作然后在UI上展现,不用操作具体的线程和handlers,一个 asynchronous task包括三种基本类型(调用参数,进度和结果),和四个步骤(调用开始,在后台运行,处理进度,结束),并且大多数时候覆盖onPostExecute(Result)方法。
使用方法描述:
1、Asynchronous Task必须是作为一个子类来使用,
2、task实例必须在UI线程创建
3、execute(Params...)必须在UI线程调用
4、不要手工调用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)。
5、task只可以execute一次,执行多次就报异常
三种基本类型的说明:
Params, 传给task的参数的类型,没有为Void。
Progress, 表示进度单位的类型,同上。
Result, 返回类型,同上。
四个步骤的说明:
onPreExecute():在task被执行之后,立刻调用
doInBackground(Params...):onPreExecute执行完毕后,执行该方法,参数传到了这个方法中,执行完毕后必须返回一个值,还可以使用 publishProgress(Progress...) 发布进度到onProgressUpdate(Progress...),便于更新进度
onProgressUpdate(Progress...):publishProgress(Progress...)被调用后,就执行该方法,显示进度信息。通常是显示一个进度条,或在text域里显示日志信息。
onPostExecute(Result)当doInBackground(Params...)执行完毕后即执行该方法
结合示例
doInBackground方法顾名思义(在后台运行时)产生了延时,并返回了操作之后的数据。
onPostExecute方法用于处理并提交doInBackground方法返回的数据,类似于刷新的功能。
@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; }
在menu中设置了四个选项,这四个选项放在了actionbar中。他们分别代表不同的效果.
demo方法:
@Override public final boolean demo() { if (mMode.showHeaderLoadingLayout() && isReadyForPullStart()) { smoothScrollToAndBack(-getHeaderSize() * 2); return true; } else if (mMode.showFooterLoadingLayout() && isReadyForPullEnd()) { smoothScrollToAndBack(getFooterSize() * 2); return true; } return false; }
smoothScrollToAndBack:输入参数y代表下拉的距离
private final void smoothScrollToAndBack(int y) { smoothScrollTo(y, SMOOTH_SCROLL_DURATION_MS, 0, new OnSmoothScrollFinishedListener() { @Override public void onSmoothScrollFinished() { smoothScrollTo(0, SMOOTH_SCROLL_DURATION_MS, DEMO_SCROLL_INTERVAL, null); } });//模拟人手的滑动,停留的时间可以自己设定 }
smoothScrollTo方法:
private final void smoothScrollTo(int newScrollValue, long duration, long delayMillis, OnSmoothScrollFinishedListener listener) { if (null != mCurrentSmoothScrollRunnable) { mCurrentSmoothScrollRunnable.stop(); } final int oldScrollValue; switch (getPullToRefreshScrollDirection()) { case HORIZONTAL: oldScrollValue = getScrollX(); break; case VERTICAL: default: oldScrollValue = getScrollY(); break; } if (oldScrollValue != newScrollValue) { if (null == mScrollAnimationInterpolator) { // Default interpolator is a Decelerate Interpolator mScrollAnimationInterpolator = new DecelerateInterpolator(); } mCurrentSmoothScrollRunnable = new SmoothScrollRunnable(oldScrollValue, newScrollValue, duration, listener);//这里才是动作真正开始执行的时候,新开线程去执行。 if (delayMillis > 0) { postDelayed(mCurrentSmoothScrollRunnable, delayMillis); } else { post(mCurrentSmoothScrollRunnable); } } }
SmoothScrollRunnable类继承了runnable,所以会新开一条线程来执行。
@Override public void run() { /** * Only set mStartTime if this is the first time we're starting, * else actually calculate the Y delta */ if (mStartTime == -1) { mStartTime = System.currentTimeMillis(); } else { /** * We do do all calculations in long to reduce software float * calculations. We use 1000 as it gives us good accuracy and * small rounding errors */ long normalizedTime = (1000 * (System.currentTimeMillis() - mStartTime)) / mDuration; normalizedTime = Math.max(Math.min(normalizedTime, 1000), 0); final int deltaY = Math.round((mScrollFromY - mScrollToY) * mInterpolator.getInterpolation(normalizedTime / 1000f)); mCurrentY = mScrollFromY - deltaY; setHeaderScroll(mCurrentY); } // If we're not at the target Y, keep going... if (mContinueRunning && mScrollToY != mCurrentY) { ViewCompat.postOnAnimation(PullToRefreshBase.this, this);//执行线程 } else { if (null != mListener) { mListener.onSmoothScrollFinished(); } } } public void stop() { mContinueRunning = false; removeCallbacks(this); } } static interface OnSmoothScrollFinishedListener { void onSmoothScrollFinished(); }