【安卓筆記】下拉重新整理元件的使用及實現
專案中如果需要實現下拉重新整理一般有以下幾個選擇:
1.使用開源庫Android-pullToRefresh。
2.使用support.v4包提供的SwipeRefreshLayout。
3.自己實現一個。
下面分別簡單介紹:
注:以listView下拉重新整理為例.
方案1:使用開源庫Android-pullToRefresh
1.下載Android-PullToRefresh開源庫(https://github.com/chrisbanes/Android-PullToRefresh)
2.將library工程匯入到eclipse中
3.建立一個新工程,並在properties->android選項中引用library庫
4.編寫程式碼
該庫提供了PullToRefreshListView,我們用它替換ListView,它在使用上跟ListView一致。
但是其提供了一個監聽下拉更新的介面,我們通過setOnRefreshListener註冊該監聽器,然後在onRefresh回撥方法中非同步更新資料即可,更新完成後需呼叫onRefreshComplete方法完成更新。
示例:
頁面佈局:
listView的item佈局:
<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;
@Override
protected 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>()
{
@Override
public 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>>
{
@Override
protected 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;
}
@Override
protected void onPostExecute(List<String> result)
{
adapter.addAll(result);
//重新整理完成
lv.onRefreshComplete();
}
}
}
效果:方案2:使用support.v4包提供的SwipeRefreshLayout
SwipeRefreshLayout是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;
@Override
protected 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()
{
@Override
public void onRefresh()
{
new UpdateTask().execute();
}
});
}
private class UpdateTask extends AsyncTask<Void, Void, List<String>>
{
@Override
protected 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;
}
@Override
protected 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)
{
//新增headerview
mHeaderView = 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());
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount)
{
if(firstVisibleItem == 0)
flag = true;
else
flag = false;
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState)
{
}
@Override
public 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
{
@Override
public void run()
{
//獲取header的高度
mHeaderHeight = mHeaderView.getMeasuredHeight();//view在被渲染前無法獲得其寬高
Log.d(TAG,"Headerheight:"+mHeaderHeight);
//通過設定header的padding來隱藏header
setHeaderTopPadding(-mHeaderHeight);
}
}
}
使用上跟上面差不多,也是需要新增監聽器,然後處理回撥方法,資料更新完呼叫refreshComplete。
當然,這個只是一個簡單的demo,效果可能不是很好,僅供參考~
相關文章
- React Native 實現自定義下拉重新整理元件React Native元件
- VBA 控制元件學習筆記(下拉選單實現)控制元件筆記
- 實現RecyclerView下拉重新整理View
- Vue下拉重新整理元件Vue元件
- AlloyTouch 實現下拉重新整理
- AlloyTouch實現下拉重新整理
- SmartRefreshLayout+BaseRecyclerviewAdapterHelper使用MVP方式實現下拉重新整理ViewAPTMVP
- Swift - MJRefresh庫的使用詳解1(配置,及庫自帶的下拉重新整理元件)Swift元件
- React.js中實現下拉重新整理ReactJS
- Android實現帶動畫的下拉重新整理RecyclerViewAndroid動畫View
- Flutter 實現下拉重新整理&上拉載入Flutter
- UITableView實現下拉重新整理新增資料功能UIView
- 如何實現上拉載入,下拉重新整理?
- 控制元件:UIRefreshControl下拉重新整理控制元件UI
- Android下拉重新整理完全解析,教你如何一分鐘實現下拉重新整理功能Android
- Android開發之SwipeRefreshLayout實現下拉重新整理Android
- android開發(3):列表listview的實現 | 下拉重新整理AndroidView
- 自定義一個下拉重新整理控制元件控制元件
- Android自定義下拉重新整理控制元件Android控制元件
- Movable-view實現列表的下拉重新整理上拉載入View
- 小程式scroll-view自身下拉重新整理的實現分享View
- 超簡單!原生SwipeRefreshLayout實現首頁下拉重新整理
- uniapp專案實踐總結(十六)自定義下拉重新整理元件APP元件
- Angular實現虛擬滾動多選下拉框筆記Angular筆記
- 【安卓筆記】CardView+RecyclerView使用示例安卓筆記View
- 移動端用下拉重新整理的方式實現上拉載入
- js實現的移動端下拉重新整理功能程式碼例項JS
- Android筆記:invalidate()和postInvalidate() 的區別及使用——重新整理uiAndroid筆記UI
- vue 實現上拉載入下拉重新整理(思路賊清晰)Vue
- 仿健客、京東、天貓下拉重新整理載入動畫實現動畫
- 安卓開發之ViewDragHelper的使用及自定義可下拉展示內容的ViewGroup安卓View
- 淺談對MJRefresh(上)下拉重新整理控制元件的理解控制元件
- 手把手教會自定義下拉重新整理控制元件控制元件
- 60 行 JS 程式碼搞定一個下拉重新整理元件JS元件
- XRecyclerView和萬能baseeAdapter 實現上拉下拉重新整理列表ViewAPT
- Android開發筆記(一百二十三)下拉重新整理佈局SwipeRefreshLayoutAndroid筆記
- 非同步下拉樹使用要求及實際操作非同步
- 遨翔在知識的海洋裡----(apicloud之下拉重新整理元件)APICloud元件