編輯:關於Android編程
最近做的類似於微博的項目中,有個Android功能要使用到listview的向下拉刷新來刷新最新消息,向上拉刷新(滑動分頁)來加載更多。
新浪微博就是使用這種方式的典型。
當用戶從網絡上讀取微博的時候,如果一下子全部加載用戶未讀的微博這將耗費比較長的時間,造成不好的用戶體驗,同時一屏的內容也不足以顯示如此多的內容。這時候,我們就需要用到另一個功能,那就是listview的分頁了,其實這個分頁可以做成客戶端的分頁,也可以做成服務器端的分頁(點擊加載時,從服務器對應的加載第N頁就好了!!!)。通過分頁分次加載數據,用戶看多少就去加載多少。
通常這也分為兩種方式,一種是設置一個按鈕,用戶點擊即加載。另一種是當用戶滑動到底部時自動加載。今天我就和大家分享一下滑動到底端時自動加載這個功能的實現。
效果圖如下所示:
下拉刷新最主要的流程是:
(1). 下拉,顯示提示頭部界面(HeaderView),這個過程提示用戶”下拉刷新”
(2). 下拉到一定程度,超出了刷新最基本的下拉界限,我們認為達到了刷新的條件,提示用戶可以”松手刷新”了,效果上允許用戶繼續下拉
(3). 用戶松手,可能用戶下拉遠遠不止提示頭部界面,所以這一步,先反彈回僅顯示提示頭部界面,然後提示用戶”正在加載”。
(4). 加載完成後,隱藏提示頭部界面。
那麼讓我們看看怎麼才能實現呢???
第一步:既然是要顯示listview ,那麼就應該有個listview 的容器pulldown.xml
<?xml version="1.0" encoding="utf-8"?> <com.solo.pulldown.PullDownView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pull_down_view" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@android:color/white"> </com.solo.pulldown.PullDownView>
第二步:自定義一個listview中顯示的item對象pulldown_item.xml
<?xml version="1.0" encoding="utf-8"?> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@android:id/text1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:gravity="center_vertical" android:paddingLeft="6dip" android:minHeight="?android:attr/listPreferredItemHeight" android:textColor="@android:color/black" />
第三步:定義一個header的xml布局文件pulldown_header.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="10dp" android:paddingTop="10dp" > <ImageView android:id="@+id/pulldown_header_arrow" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_marginLeft="20dp" android:scaleType="centerCrop" android:src="@drawable/z_arrow_down" android:visibility="invisible" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@+id/pulldown_header_arrow" android:layout_alignTop="@+id/pulldown_header_arrow" android:layout_centerHorizontal="true" android:gravity="center_vertical" android:orientation="vertical" > <TextView android:id="@+id/pulldown_header_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="加載中..." /> <TextView android:id="@+id/pulldown_header_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:text="更新於:" android:visibility="gone" /> </LinearLayout> <ProgressBar android:id="@+id/pulldown_header_loading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" /> </RelativeLayout>
第四步:如果需要向上拉更新更多的話,那就定義一個底部的footer的布局文件,在此為方便起見,只定義一個progressbar跟textview,更加復雜的顯示,就交給你們了~~~~~pulldown_footer.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="10dp" android:paddingTop="10dp" > <TextView android:id="@+id/pulldown_footer_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="更多" android:textSize="15dp" /> <ProgressBar android:id="@+id/pulldown_footer_loading" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="20dp" android:visibility="gone" /> </RelativeLayout>
第五步:那麼主要的文件這才登場:::::::重寫listview這個文件主要任務是提供觸摸的事件的處理方法。
/** * <p>一個可以監聽ListView是否滾動到最頂部或最底部的自定義控件</p> * 只能監聽由觸摸產生的,如果是ListView本身Flying導致的,則不能監聽</br> * 如果加以改進,可以實現監聽scroll滾動的具體位置等 */ public class ScrollOverListView extends ListView { private int mLastY; private int mTopPosition; private int mBottomPosition; public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public ScrollOverListView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScrollOverListView(Context context) { super(context); init(); } private void init(){ mTopPosition = 0; mBottomPosition = 0; } @Override public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); final int y = (int) ev.getRawY(); switch(action){ case MotionEvent.ACTION_DOWN:{ mLastY = y; final boolean isHandled = mOnScrollOverListener.onMotionDown(ev); if (isHandled) { mLastY = y; return isHandled; } break; } case MotionEvent.ACTION_MOVE:{ final int childCount = getChildCount(); if(childCount == 0) return super.onTouchEvent(ev); final int itemCount = getAdapter().getCount() - mBottomPosition; final int deltaY = y - mLastY; //DLog.d("lastY=%d y=%d", mLastY, y); final int firstTop = getChildAt(0).getTop(); final int listPadding = getListPaddingTop(); final int lastBottom = getChildAt(childCount - 1).getBottom(); final int end = getHeight() - getPaddingBottom(); final int firstVisiblePosition = getFirstVisiblePosition(); final boolean isHandleMotionMove = mOnScrollOverListener.onMotionMove(ev, deltaY); if(isHandleMotionMove){ mLastY = y; return true; } //DLog.d("firstVisiblePosition=%d firstTop=%d listPaddingTop=%d deltaY=%d", firstVisiblePosition, firstTop, listPadding, deltaY); if (firstVisiblePosition <= mTopPosition && firstTop >= listPadding && deltaY > 0) { final boolean isHandleOnListViewTopAndPullDown; isHandleOnListViewTopAndPullDown = mOnScrollOverListener.onListViewTopAndPullDown(deltaY); if(isHandleOnListViewTopAndPullDown){ mLastY = y; return true; } } // DLog.d("lastBottom=%d end=%d deltaY=%d", lastBottom, end, deltaY); if (firstVisiblePosition + childCount >= itemCount && lastBottom <= end && deltaY < 0) { final boolean isHandleOnListViewBottomAndPullDown; isHandleOnListViewBottomAndPullDown = mOnScrollOverListener.onListViewBottomAndPullUp(deltaY); if(isHandleOnListViewBottomAndPullDown){ mLastY = y; return true; } } break; } case MotionEvent.ACTION_UP:{ final boolean isHandlerMotionUp = mOnScrollOverListener.onMotionUp(ev); if (isHandlerMotionUp) { mLastY = y; return true; } break; } } mLastY = y; return super.onTouchEvent(ev); } /**空的*/ private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener(){ @Override public boolean onListViewTopAndPullDown(int delta) { return false; } @Override public boolean onListViewBottomAndPullUp(int delta) { return false; } @Override public boolean onMotionDown(MotionEvent ev) { return false; } @Override public boolean onMotionMove(MotionEvent ev, int delta) { return false; } @Override public boolean onMotionUp(MotionEvent ev) { return false; } }; // =============================== public method =============================== /** * 可以自定義其中一個條目為頭部,頭部觸發的事件將以這個為准,默認為第一個 * * @param index 正數第幾個,必須在條目數范圍之內 */ public void setTopPosition(int index){ if(getAdapter() == null) throw new NullPointerException("You must set adapter before setTopPosition!"); if(index < 0) throw new IllegalArgumentException("Top position must > 0"); mTopPosition = index; } /** * 可以自定義其中一個條目為尾部,尾部觸發的事件將以這個為准,默認為最後一個 * * @param index 倒數第幾個,必須在條目數范圍之內 */ public void setBottomPosition(int index){ if(getAdapter() == null) throw new NullPointerException("You must set adapter before setBottonPosition!"); if(index < 0) throw new IllegalArgumentException("Bottom position must > 0"); mBottomPosition = index; } /** * 設置這個Listener可以監聽是否到達頂端,或者是否到達低端等事件</br> * * @see OnScrollOverListener */ public void setOnScrollOverListener(OnScrollOverListener onScrollOverListener){ mOnScrollOverListener = onScrollOverListener; } /** * 滾動監聽接口</br> * @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener) * */ public interface OnScrollOverListener { /** * 到達最頂部觸發 * * @param delta 手指點擊移動產生的偏移量 * @return */ boolean onListViewTopAndPullDown(int delta); /** * 到達最底部觸發 * * @param delta 手指點擊移動產生的偏移量 * @return */ boolean onListViewBottomAndPullUp(int delta); /** * 手指觸摸按下觸發,相當於{@link MotionEvent#ACTION_DOWN} * * @return 返回true表示自己處理 * @see View#onTouchEvent(MotionEvent) */ boolean onMotionDown(MotionEvent ev); /** * 手指觸摸移動觸發,相當於{@link MotionEvent#ACTION_MOVE} * * @return 返回true表示自己處理 * @see View#onTouchEvent(MotionEvent) */ boolean onMotionMove(MotionEvent ev, int delta); /** * 手指觸摸後提起觸發,相當於{@link MotionEvent#ACTION_UP} * * @return 返回true表示自己處理 * @see View#onTouchEvent(MotionEvent) */ boolean onMotionUp(MotionEvent ev); } }
第六步:下拉刷新控件,真正實現下拉刷新的是這個控件,而上面的那個ScrollOverListView只是提供觸摸的事件等
/** * 下拉刷新控件</br> * 真正實現下拉刷新的是這個控件, * ScrollOverListView只是提供觸摸的事件等 */ public class PullDownView extends LinearLayout implements OnScrollOverListener{ private static final String TAG = "PullDownView"; private static final int START_PULL_DEVIATION = 50; // 移動誤差 private static final int AUTO_INCREMENTAL = 10; // 自增量,用於回彈 private static final int WHAT_DID_LOAD_DATA = 1; // Handler what 數據加載完畢 private static final int WHAT_ON_REFRESH = 2; // Handler what 刷新中 private static final int WHAT_DID_REFRESH = 3; // Handler what 已經刷新完 private static final int WHAT_SET_HEADER_HEIGHT = 4;// Handler what 設置高度 private static final int WHAT_DID_MORE = 5; // Handler what 已經獲取完更多 private static final int DEFAULT_HEADER_VIEW_HEIGHT = 105; // 頭部文件原本的高度 private static SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd HH:mm"); private View mHeaderView; private LayoutParams mHeaderViewParams; private TextView mHeaderViewDateView; private TextView mHeaderTextView; private ImageView mHeaderArrowView; private View mHeaderLoadingView; private View mFooterView; private TextView mFooterTextView; private View mFooterLoadingView; private ScrollOverListView mListView; private OnPullDownListener mOnPullDownListener; private RotateAnimation mRotateOTo180Animation; private RotateAnimation mRotate180To0Animation; private int mHeaderIncremental; // 增量 private float mMotionDownLastY; // 按下時候的Y軸坐標 private boolean mIsDown; // 是否按下 private boolean mIsRefreshing; // 是否下拉刷新中 private boolean mIsFetchMoreing; // 是否獲取更多中 private boolean mIsPullUpDone; // 是否回推完成 private boolean mEnableAutoFetchMore; // 是否允許自動獲取更多 // 頭部文件的狀態 private static final int HEADER_VIEW_STATE_IDLE = 0; // 空閒 private static final int HEADER_VIEW_STATE_NOT_OVER_HEIGHT = 1; // 沒有超過默認高度 private static final int HEADER_VIEW_STATE_OVER_HEIGHT = 2; // 超過默認高度 private int mHeaderViewState = HEADER_VIEW_STATE_IDLE; public PullDownView(Context context, AttributeSet attrs) { super(context, attrs); initHeaderViewAndFooterViewAndListView(context); } public PullDownView(Context context) { super(context); initHeaderViewAndFooterViewAndListView(context); } /* * ================================== * Public method * 外部使用,具體就是用這幾個就可以了 * * ================================== */ /** * 刷新事件接口 */ public interface OnPullDownListener { void onRefresh(); void onMore(); } /** * 通知加載完了數據,要放在Adapter.notifyDataSetChanged後面 * 當你加載完數據的時候,調用這個notifyDidLoad() * 才會隱藏頭部,並初始化數據等 */ public void notifyDidLoad() { mUIHandler.sendEmptyMessage(WHAT_DID_LOAD_DATA); } /** * 通知已經刷新完了,要放在Adapter.notifyDataSetChanged後面 * 當你執行完刷新任務之後,調用這個notifyDidRefresh() * 才會隱藏掉頭部文件等操作 */ public void notifyDidRefresh() { mUIHandler.sendEmptyMessage(WHAT_DID_REFRESH); } /** * 通知已經獲取完更多了,要放在Adapter.notifyDataSetChanged後面 * 當你執行完更多任務之後,調用這個notyfyDidMore() * 才會隱藏加載圈等操作 */ public void notifyDidMore() { mUIHandler.sendEmptyMessage(WHAT_DID_MORE); } /** * 設置監聽器 * @param listener */ public void setOnPullDownListener(OnPullDownListener listener){ mOnPullDownListener = listener; } /** * 獲取內嵌的listview * @return ScrollOverListView */ public ListView getListView(){ return mListView; } /** * 是否開啟自動獲取更多 * 自動獲取更多,將會隱藏footer,並在到達底部的時候自動刷新 * @param index 倒數第幾個觸發 */ public void enableAutoFetchMore(boolean enable, int index){ if(enable){ mListView.setBottomPosition(index); mFooterLoadingView.setVisibility(View.VISIBLE); }else{ mFooterTextView.setText("更多"); mFooterLoadingView.setVisibility(View.GONE); } mEnableAutoFetchMore = enable; } /* * ================================== * Private method * 具體實現下拉刷新等操作 * * ================================== */ /** * 初始化界面 */ private void initHeaderViewAndFooterViewAndListView(Context context){ setOrientation(LinearLayout.VERTICAL); //setDrawingCacheEnabled(false); /* * 自定義頭部文件 * 放在這裡是因為考慮到很多界面都需要使用 * 如果要修改,和它相關的設置都要更改 */ mHeaderView = LayoutInflater.from(context).inflate(R.layout.pulldown_header, null); mHeaderViewParams = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); addView(mHeaderView, 0, mHeaderViewParams); mHeaderTextView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_text); mHeaderArrowView = (ImageView) mHeaderView.findViewById(R.id.pulldown_header_arrow); mHeaderLoadingView = mHeaderView.findViewById(R.id.pulldown_header_loading); // 注意,圖片旋轉之後,再執行旋轉,坐標會重新開始計算 mRotateOTo180Animation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mRotateOTo180Animation.setDuration(250); mRotateOTo180Animation.setFillAfter(true); mRotate180To0Animation = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mRotate180To0Animation.setDuration(250); mRotate180To0Animation.setFillAfter(true); /** * 自定義腳部文件 */ mFooterView = LayoutInflater.from(context).inflate(R.layout.pulldown_footer, null); mFooterTextView = (TextView) mFooterView.findViewById(R.id.pulldown_footer_text); mFooterLoadingView = mFooterView.findViewById(R.id.pulldown_footer_loading); mFooterView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(!mIsFetchMoreing){ mIsFetchMoreing = true; mFooterLoadingView.setVisibility(View.VISIBLE); mOnPullDownListener.onMore(); } } }); /* * ScrollOverListView 同樣是考慮到都是使用,所以放在這裡 * 同時因為,需要它的監聽事件 */ mListView = new ScrollOverListView(context); mListView.setOnScrollOverListener(this); mListView.setCacheColorHint(0); addView(mListView, LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); // 空的listener mOnPullDownListener = new OnPullDownListener() { @Override public void onRefresh() {} @Override public void onMore() {} }; } /** * 在下拉和回推的時候檢查頭部文件的狀態</br> * 如果超過了默認高度,就顯示松開可以刷新, * 否則顯示下拉可以刷新 */ private void checkHeaderViewState(){ if(mHeaderViewParams.height >= DEFAULT_HEADER_VIEW_HEIGHT){ if(mHeaderViewState == HEADER_VIEW_STATE_OVER_HEIGHT) return; mHeaderViewState = HEADER_VIEW_STATE_OVER_HEIGHT; mHeaderTextView.setText("松開可以刷新"); mHeaderArrowView.startAnimation(mRotateOTo180Animation); }else{ if(mHeaderViewState == HEADER_VIEW_STATE_NOT_OVER_HEIGHT || mHeaderViewState == HEADER_VIEW_STATE_IDLE) return; mHeaderViewState = HEADER_VIEW_STATE_NOT_OVER_HEIGHT; mHeaderTextView.setText("下拉可以刷新"); mHeaderArrowView.startAnimation(mRotate180To0Animation); } } private void setHeaderHeight(final int height){ mHeaderIncremental = height; mHeaderViewParams.height = height; mHeaderView.setLayoutParams(mHeaderViewParams); } /** * 自動隱藏動畫 */ class HideHeaderViewTask extends TimerTask{ @Override public void run() { if(mIsDown) { cancel(); return; } mHeaderIncremental -= AUTO_INCREMENTAL; if(mHeaderIncremental > 0){ mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT); }else{ mHeaderIncremental = 0; mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT); cancel(); } } } /** * 自動顯示動畫 */ class ShowHeaderViewTask extends TimerTask{ @Override public void run() { if(mIsDown) { cancel(); return; } mHeaderIncremental -= AUTO_INCREMENTAL; if(mHeaderIncremental > DEFAULT_HEADER_VIEW_HEIGHT){ mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT); }else{ mHeaderIncremental = DEFAULT_HEADER_VIEW_HEIGHT; mUIHandler.sendEmptyMessage(WHAT_SET_HEADER_HEIGHT); if(!mIsRefreshing){ mIsRefreshing = true; mUIHandler.sendEmptyMessage(WHAT_ON_REFRESH); } cancel(); } } } private Handler mUIHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case WHAT_DID_LOAD_DATA:{ mHeaderViewParams.height = 0; mHeaderLoadingView.setVisibility(View.GONE); mHeaderTextView.setText("下拉可以刷新"); mHeaderViewDateView = (TextView) mHeaderView.findViewById(R.id.pulldown_header_date); mHeaderViewDateView.setVisibility(View.VISIBLE); mHeaderViewDateView.setText("更新於:" + dateFormat.format(new Date(System.currentTimeMillis()))); mHeaderArrowView.setVisibility(View.VISIBLE); showFooterView(); return; } case WHAT_ON_REFRESH:{ // 要清除掉動畫,否則無法隱藏 mHeaderArrowView.clearAnimation(); mHeaderArrowView.setVisibility(View.INVISIBLE); mHeaderLoadingView.setVisibility(View.VISIBLE); mOnPullDownListener.onRefresh(); return; } case WHAT_DID_REFRESH :{ mIsRefreshing = false; mHeaderViewState = HEADER_VIEW_STATE_IDLE; mHeaderArrowView.setVisibility(View.VISIBLE); mHeaderLoadingView.setVisibility(View.GONE); mHeaderViewDateView.setText("更新於:" + dateFormat.format(new Date(System.currentTimeMillis()))); setHeaderHeight(0); showFooterView(); return; } case WHAT_SET_HEADER_HEIGHT :{ setHeaderHeight(mHeaderIncremental); return; } case WHAT_DID_MORE :{ mIsFetchMoreing = false; mFooterTextView.setText("更多"); mFooterLoadingView.setVisibility(View.GONE); } } } }; /** * 顯示腳步腳部文件 */ private void showFooterView(){ if(mListView.getFooterViewsCount() == 0 && isFillScreenItem()){ mListView.addFooterView(mFooterView); mListView.setAdapter(mListView.getAdapter()); } } /** * 條目是否填滿整個屏幕 */ private boolean isFillScreenItem(){ final int firstVisiblePosition = mListView.getFirstVisiblePosition(); final int lastVisiblePostion = mListView.getLastVisiblePosition() - mListView.getFooterViewsCount(); final int visibleItemCount = lastVisiblePostion - firstVisiblePosition + 1; final int totalItemCount = mListView.getCount() - mListView.getFooterViewsCount(); if(visibleItemCount < totalItemCount) return true; return false; } /* * ================================== * 實現 OnScrollOverListener接口 * * * ================================== */ @Override public boolean onListViewTopAndPullDown(int delta) { if(mIsRefreshing || mListView.getCount() - mListView.getFooterViewsCount() == 0) return false; int absDelta = Math.abs(delta); final int i = (int) Math.ceil((double)absDelta / 2); mHeaderIncremental += i; if(mHeaderIncremental >= 0){ // && mIncremental <= mMaxHeight setHeaderHeight(mHeaderIncremental); checkHeaderViewState(); } return true; } @Override public boolean onListViewBottomAndPullUp(int delta) { if(!mEnableAutoFetchMore || mIsFetchMoreing) return false; // 數量充滿屏幕才觸發 if(isFillScreenItem()){ mIsFetchMoreing = true; mFooterTextView.setText("加載更多中..."); mFooterLoadingView.setVisibility(View.VISIBLE); mOnPullDownListener.onMore(); return true; } return false; } @Override public boolean onMotionDown(MotionEvent ev) { mIsDown = true; mIsPullUpDone = false; mMotionDownLastY = ev.getRawY(); return false; } @Override public boolean onMotionMove(MotionEvent ev, int delta) { //當頭部文件回推消失的時候,不允許滾動 if(mIsPullUpDone) return true; // 如果開始按下到滑動距離不超過誤差值,則不滑動 final int absMotionY = (int) Math.abs(ev.getRawY() - mMotionDownLastY); if(absMotionY < START_PULL_DEVIATION) return true; final int absDelta = Math.abs(delta); final int i = (int) Math.ceil((double)absDelta / 2); // onTopDown在頂部,並上回推和onTopUp相對 if(mHeaderViewParams.height > 0 && delta < 0){ mHeaderIncremental -= i; if(mHeaderIncremental > 0){ setHeaderHeight(mHeaderIncremental); checkHeaderViewState(); }else{ mHeaderViewState = HEADER_VIEW_STATE_IDLE; mHeaderIncremental = 0; setHeaderHeight(mHeaderIncremental); mIsPullUpDone = true; } return true; } return false; } @Override public boolean onMotionUp(MotionEvent ev) { mIsDown = false; // 避免和點擊事件沖突 if(mHeaderViewParams.height > 0){ // 判斷頭文件拉動的距離與設定的高度,小了就隱藏,多了就固定高度 int x = mHeaderIncremental - DEFAULT_HEADER_VIEW_HEIGHT; Timer timer = new Timer(true); if(x < 0){ timer.scheduleAtFixedRate(new HideHeaderViewTask(), 0, 10); }else{ timer.scheduleAtFixedRate(new ShowHeaderViewTask(), 0, 10); } return true; } return false; } }
第七步:這個java文件就是封裝數據,然後調用上兩個文件就好了。廢話不多說,上代碼:::::
public class PullDownActivity extends Activity implements OnPullDownListener, OnItemClickListener{ private static final int WHAT_DID_LOAD_DATA = 0; private static final int WHAT_DID_REFRESH = 1; private static final int WHAT_DID_MORE = 2; private ListView mListView; private ArrayAdapter<String> mAdapter; private PullDownView mPullDownView; private List<String> mStrings = new ArrayList<String>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pulldown); /* * 1.使用PullDownView * 2.設置OnPullDownListener * 3.從mPullDownView裡面獲取ListView */ mPullDownView = (PullDownView) findViewById(R.id.pull_down_view); mPullDownView.setOnPullDownListener(this); mListView = mPullDownView.getListView(); mListView.setOnItemClickListener(this); mAdapter = new ArrayAdapter<String>(this, R.layout.pulldown_item, mStrings); mListView.setAdapter(mAdapter); mPullDownView.enableAutoFetchMore(true, 1); loadData(); } private void loadData(){ new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } List<String> strings = new ArrayList<String>(); for (String body : mStringArray) { strings.add(body); } Message msg = mUIHandler.obtainMessage(WHAT_DID_LOAD_DATA); msg.obj = strings; msg.sendToTarget(); } }).start(); } @Override public void onRefresh() { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = mUIHandler.obtainMessage(WHAT_DID_REFRESH); msg.obj = "After refresh " + System.currentTimeMillis(); msg.sendToTarget(); } }).start(); } @Override public void onMore() { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = mUIHandler.obtainMessage(WHAT_DID_MORE); msg.obj = "After more " + System.currentTimeMillis(); msg.sendToTarget(); } }).start(); } private Handler mUIHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case WHAT_DID_LOAD_DATA:{ if(msg.obj != null){ List<String> strings = (List<String>) msg.obj; if(!strings.isEmpty()){ mStrings.addAll(strings); mAdapter.notifyDataSetChanged(); } } // 訴它數據加載完畢; mPullDownView.notifyDidLoad(); break; } case WHAT_DID_REFRESH :{ String body = (String) msg.obj; mStrings.add(0, body); mAdapter.notifyDataSetChanged(); // 告訴它更新完畢 mPullDownView.notifyDidRefresh(); break; } case WHAT_DID_MORE:{ String body = (String) msg.obj; mStrings.add(body); mAdapter.notifyDataSetChanged(); // 告訴它獲取更多完畢 mPullDownView.notifyDidMore(); break; } } } }; @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(this, "啊,你點中我了 " + position, Toast.LENGTH_SHORT).show(); } // 模擬數據 private String[] mStringArray = { "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", "Alverca", "Ambert", "American Cheese" }; }
整個程序就這麼完成了,總之,可以使用前兩個封裝好的文件,然後,調用就好了~~~~沒什麼太難的地方。
以上所述是小編給大家介紹的Android程序開發之Listview下拉刷新上拉(滑動分頁)加載更多,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對本站網站的支持!
JsonOnlineViewer可實現直接在android studio中調試接口數據,可以選擇請求類型,自定義請求頭及請求體,json數據格式化後展示 下載完
關於MIME TYPE描述 多用途互聯網郵件擴展(MIME,Multipurpose Internet Mail Extensions)是一個互聯網標准,它擴展了電子
**對於Volley的工作原理,恐怕有很多朋友還不是很清楚。因此,本篇文章中我們就來一起閱讀一下Volley的源碼,將它的工作流程整體地梳理一遍。 其實,Volley的官
之前寫過一篇有關IPC之AIDL淺談的文章。今天再來介紹另一種IPC-Messenger。一、概述。首先看Messenger介紹, Reference to