【安卓筆記】下拉重新整理組件的使用及實現,安卓筆記下拉組件
項目中如果需要實現下拉重新整理一般有以下幾個選擇:1.使用開源庫Android-pullToRefresh。2.使用support.v4包提供的SwipeRefreshLayout。3.自己實現一個。
下面分別簡單介紹:註:以listView下拉重新整理為例.方案1:使用開源庫Android-pullToRefresh1.下載Android-PullToRefresh開源庫(https://github.com/chrisbanes/Android-PullToRefresh)2.將library工程匯入到eclipse中3.建立一個新工程,並在properties->android選項中引用library庫4.編寫代碼
該庫提供了PullToRefreshListView,我們用它替換ListView,它在使用上跟ListView一致。但是其提供了一個監聽下拉更新的介面,我們通過setOnRefreshListener註冊該監聽器,然後在onRefresh回調方法中非同步更新資料即可,更新完成後需調用onRefreshComplete方法完成更新。樣本:頁面配置:
<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" tools:context="com.example.flushlistview.MainActivity" > <com.handmark.pulltorefresh.library.PullToRefreshListView android:id="@+id/lv" android:layout_width="match_parent" android:layout_height="match_parent" android:divider="#19000000" android:dividerHeight="4dp" > </com.handmark.pulltorefresh.library.PullToRefreshListView></RelativeLayout>
listView的item布局:
<?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" > <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="#000" android:textSize="20sp" /></LinearLayout>
介面邏輯:
package com.example.flushlistview;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.List;import java.util.Locale;import android.app.Activity;import android.os.AsyncTask;import android.os.Bundle;import android.widget.ArrayAdapter;import android.widget.ListView;import com.handmark.pulltorefresh.library.PullToRefreshBase;import com.handmark.pulltorefresh.library.PullToRefreshBase.OnRefreshListener;import com.handmark.pulltorefresh.library.PullToRefreshListView;/** * @author Rowandjj * *使用pull-to-refresh庫實現下拉重新整理操作 */public class MainActivity extends Activity{private PullToRefreshListView lv;private ArrayAdapter<String> adapter;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); lv = (PullToRefreshListView) findViewById(R.id.lv);List<String> list = new ArrayList<String>();list.add("張三");list.add("李四");list.add("王五");list.add("趙六");list.add("啊啊");list.add("呵呵");list.add("嘻嘻");list.add("嘿嘿");adapter = new ArrayAdapter<String>(this,R.layout.item, R.id.tv, list);lv.setAdapter(adapter);//實現重新整理介面lv.setOnRefreshListener(new OnRefreshListener<ListView>(){@Overridepublic void onRefresh(PullToRefreshBase<ListView> refreshView){Date date = new Date(System.currentTimeMillis());SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日hh時mm分ss秒",Locale.CHINA);String updateTime = format.format(date);refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(updateTime);//非同步重新整理new UpdateTask().execute();}});}private class UpdateTask extends AsyncTask<Void, Void,List<String>>{@Overrideprotected List<String> doInBackground(Void... params){try{Thread.sleep(2000);} catch (InterruptedException e){e.printStackTrace();}List<String> newData = new ArrayList<String>();newData.add("新資料1");newData.add("新資料2");newData.add("新資料3");return newData;}@Overrideprotected void onPostExecute(List<String> result){adapter.addAll(result);//重新整理完成lv.onRefreshComplete();}}}效果:
方案2:使用support.v4包提供的SwipeRefreshLayoutSwipeRefreshLayout是support.v4包提供的一個類,可以實現具有Material Design效果的下拉重新整理。使用方式上也很簡單,只要將ListView/RecyclerView或者其他View放置在此layout之中,然後調用setOnRefreshListener註冊資料更新介面,並在onRefresh方法中處理資料更新事件即可。當資料更新完畢,調用setRefreshing並將參數置為false即可。注:1. SwipeRefreshLayout只能包裹一個子View。2.如果你的support.v4包中沒有這個類,那麼需要更新之。
樣本:頁面配置:
<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" tools:context="com.example.swiperefreshlayoutdemo.MainActivity" > <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/activity_main_swipe_refresh_layout" android:layout_width="match_parent" android:layout_height="wrap_content" > <ListView android:id="@+id/activity_main_listview" android:layout_width="match_parent" android:layout_height="match_parent" > </ListView> </android.support.v4.widget.SwipeRefreshLayout></RelativeLayout>
item布局:
<?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="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="40dip" android:textColor="#000" android:gravity="center_vertical" android:textSize="18sp" /></LinearLayout>
頁面邏輯:
package com.example.swiperefreshlayoutdemo;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.os.AsyncTask;import android.os.Bundle;import android.support.v4.widget.SwipeRefreshLayout;import android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener;import android.widget.ArrayAdapter;import android.widget.ListView;public class MainActivity extends Activity{private ListView mListView;private SwipeRefreshLayout mRefreshLayout;private ArrayAdapter<String> mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mListView = (ListView) findViewById(R.id.activity_main_listview);mRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.activity_main_swipe_refresh_layout);List<String> data = new ArrayList<String>();for (int i = 0; i < 15; i++){data.add("這是資料" + i);}mAdapter = new ArrayAdapter<String>(this, R.layout.item, R.id.tv, data);mListView.setAdapter(mAdapter);mRefreshLayout.setOnRefreshListener(new OnRefreshListener(){@Overridepublic void onRefresh(){new UpdateTask().execute();}});}private class UpdateTask extends AsyncTask<Void, Void, List<String>>{@Overrideprotected List<String> doInBackground(Void... params){try{Thread.sleep(2000);} catch (InterruptedException e){e.printStackTrace();}List<String> newData = new ArrayList<String>();newData.add("新資料1");newData.add("新資料2");newData.add("新資料3");return newData;}@Overrideprotected void onPostExecute(List<String> result){mAdapter.addAll(result);//通知數據更新完畢mRefreshLayout.setRefreshing(false);}}}效果:
方案3:自己實現一個要想實現一個下拉重新整理效果需要對android的事件分發、事件回調機制有清晰的認識。這裡我直接繼承自ListView,然後通過實現OnScrollListener監聽螢幕滾動事件,因為只有ListView滑動到首部才能下滑重新整理,另外,需要重寫onTouchEvent方法,根據手指位置動態更新布局。我們通過分析傳統的下拉重新整理,發現有四種狀態,1.正常態、2.下拉重新整理態、3.釋放重新整理態、4.重新整理態
當手指按下時,首先判斷當前listView的第一個item是否可見,如果不可見那麼不做任何處理,否則記下當前位置。手指移動時,計算豎直方向的位移量,然後根據位移量改變目前狀態,並根據目前狀態更新布局。這裡的布局指的是listView的headerView,當然,預設情況下,這個view是隱藏的,我們可以設定其padding為負的headerview的高度。手指鬆開時,判斷目前狀態,如果是重新整理態,那麼調用回調介面處理更新邏輯。
先貼出代碼:
package com.example.mypulltorefreshlistview.ui;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Locale;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.LayoutInflater;import android.view.MotionEvent;import android.view.View;import android.view.animation.Animation;import android.view.animation.AnimationUtils;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ImageView;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;import com.example.mypulltorefreshlistview.R;public class PullToRefreshListView extends ListView implements OnScrollListener{private static final String TAG = "PullToRefreshListView";/** *頂部布局 */private View mHeaderView;/** * header的高度 */private int mHeaderHeight;/** * 當前頁面已經滑到頂部 */private boolean flag;/** * 初始滑動時的y座標 */private int mStartY;/** * 正常狀態 */public static final int STATE_NORMAL = 0;/** * 下拉重新整理狀態 */public static final int STATE_PULL_TO_REFRESH = 1;/** * 釋放重新整理狀態 */public static final int STATE_RELEASE_TO_REFRESH = 2;/** * 正在重新整理狀態 */public static final int STATE_REFRESH = 3;/** * 目前狀態 */private int mCurrentState;private static final int DEFAULT_LENGTH = 70;private OnRefreshListener mRefreshListener;public PullToRefreshListView(Context context, AttributeSet attrs,int defStyle){super(context, attrs, defStyle);init(context);}public PullToRefreshListView(Context context, AttributeSet attrs){super(context, attrs);init(context);}public PullToRefreshListView(Context context){super(context);init(context);}public void setOnRefreshListener(OnRefreshListener listener){this.mRefreshListener = listener;}/** * 初始化操作 * @param context */private void init(Context context){//添加headerviewmHeaderView = LayoutInflater.from(context).inflate(R.layout.header_layout,null);this.addHeaderView(mHeaderView);//設定滾動監聽器this.setOnScrollListener(this);//通過設定padding將hider隱藏//注:因為此時無法獲得header的高度,所以放入MessageQueue中post(new HideHeaderAction());}/** * 設定header的padding * @param topPadding */private void setHeaderTopPadding(int topPadding){if(mHeaderView != null){mHeaderView.setPadding(mHeaderView.getPaddingLeft(),topPadding,mHeaderView.getPaddingRight(),mHeaderView.getPaddingBottom());}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount){if(firstVisibleItem == 0)flag = true;elseflag = false;}@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState){}@Overridepublic boolean onTouchEvent(MotionEvent ev){int action = ev.getAction();switch (action){case MotionEvent.ACTION_DOWN:if(flag){mStartY = (int) ev.getY();}break;case MotionEvent.ACTION_MOVE:performMove(ev);break;case MotionEvent.ACTION_UP:if(mCurrentState == STATE_RELEASE_TO_REFRESH){mCurrentState = STATE_REFRESH;updateUIByState();//TODO 載入新資料if(mRefreshListener == null){throw new RuntimeException("you must call setOnRefreshListener before...");}else{mRefreshListener.onRefresh(this);}}else if(mCurrentState == STATE_PULL_TO_REFRESH){mCurrentState = STATE_NORMAL;flag = false;updateUIByState();}break;}return super.onTouchEvent(ev);}private void updateUIByState(){TextView tip = (TextView) findViewById(R.id.tip);ImageView arrow = (ImageView) findViewById(R.id.arrow);ProgressBar progressBar = (ProgressBar) findViewById(R.id.progress);Animation anim1 = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_1);Animation anim2 = AnimationUtils.loadAnimation(getContext(),R.anim.rotate_2);switch (mCurrentState){case STATE_NORMAL:setHeaderTopPadding(-mHeaderHeight);break;case STATE_PULL_TO_REFRESH:arrow.clearAnimation();arrow.setAnimation(anim1);arrow.setVisibility(View.VISIBLE);progressBar.setVisibility(View.GONE);tip.setText("下拉可以重新整理...");break;case STATE_RELEASE_TO_REFRESH:arrow.clearAnimation();arrow.setAnimation(anim2);arrow.setVisibility(View.VISIBLE);progressBar.setVisibility(View.GONE);tip.setText("鬆開可以重新整理...");break;case STATE_REFRESH:arrow.clearAnimation();setHeaderTopPadding(mHeaderHeight);arrow.setVisibility(View.GONE);progressBar.setVisibility(View.VISIBLE);tip.setText("正在重新整理...");break;default:break;}}private void performMove(MotionEvent ev){if(!flag){return;}int currY = (int) ev.getY();int deltaY = currY-mStartY;if(deltaY > mHeaderHeight+DEFAULT_LENGTH)deltaY = mHeaderHeight+DEFAULT_LENGTH;switch (mCurrentState){case STATE_NORMAL:if(deltaY > 0){mCurrentState = STATE_PULL_TO_REFRESH;}break;case STATE_PULL_TO_REFRESH:setHeaderTopPadding(deltaY-mHeaderHeight);updateUIByState();if(deltaY >= mHeaderHeight+DEFAULT_LENGTH){mCurrentState = STATE_RELEASE_TO_REFRESH;}else if(deltaY <= 0){mCurrentState = STATE_NORMAL;}break;case STATE_RELEASE_TO_REFRESH://setHeaderTopPadding(deltaY-mHeaderHeight);updateUIByState();if(deltaY < mHeaderHeight+DEFAULT_LENGTH){mCurrentState = STATE_PULL_TO_REFRESH;}else if(deltaY <= 0){mCurrentState = STATE_NORMAL;}break;}}/** * 更新完畢時調用 */public void refreshComplete(){TextView lastUpdateTime = (TextView) findViewById(R.id.last_update_time);SimpleDateFormat format = new SimpleDateFormat("MM-dd hh:mm",Locale.CHINA);String updateTime = format.format(new Date(System.currentTimeMillis()));lastUpdateTime.setText("更新於 "+updateTime);mCurrentState = STATE_NORMAL;updateUIByState();}public interface OnRefreshListener{public void onRefresh(PullToRefreshListView listView);}private class HideHeaderAction implements Runnable{@Overridepublic void run(){//擷取header的高度mHeaderHeight = mHeaderView.getMeasuredHeight();//view在被渲染前無法獲得其寬高Log.d(TAG,"Headerheight:"+mHeaderHeight);//通過設定header的padding來隱藏headersetHeaderTopPadding(-mHeaderHeight);}}}使用上跟上面差不多,也是需要添加監聽器,然後處理回調方法,資料更新完調用refreshComplete。當然,這個只是一個簡單的demo,效果可能不是很好,僅供參考~