Android實現(xiàn)簡單的下拉刷新pulltorefresh
網(wǎng)上下拉刷新的DEMO很多,但是總有各種不滿意的地方,有些會下拉卡住,有些回彈不流暢,有些性能太低會各種卡頓,有些emptyView無法下拉......
自己寫的才是最合適自己的,代碼很簡單,也很容易修改,稍微閱讀下代碼就能改出自己需要的各種效果。
首先,重寫ListView,自定義Touch事件,為了使emptyView也可下拉,emptyView也加上Touch事件。 如果要實現(xiàn)GridView,把這里的ListView改成GridView即可。
PullableListView :
public class PullableListView extends ListView {
private boolean inited;
private float density;
private int mDownY, mMoveY;
private int mPullY;
private boolean isPull;
private PullListener mPullListener;
private VelocityTracker mVelocityTracker;
public interface PullListener {
public boolean onPullDownStart();
public void onPullDown(int moveY);
public void onPullDownDrop();
}
public PullableListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public PullableListView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PullableListView(Context context) {
super(context);
init();
}
private void init() {
if (!inited) {
density = getResources().getDisplayMetrics().density;
}
}
public void setPullListener(PullListener mPullListener) {
this.mPullListener = mPullListener;
}
public boolean isPulling() {
return isPull;
}
@Override
public void setEmptyView(View emptyView) {
super.setEmptyView(emptyView);
// 重寫emptyView的Touch事件,使顯示emptyView時也可以下拉刷新
emptyView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent ev) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
mMoveY = (int) ev.getY();
if (!isPull) {
mVelocityTracker.computeCurrentVelocity(1000, 8000f);
if (mVelocityTracker.getYVelocity() > 500 // 下拉速度大于500
&& Math.abs(mMoveY - mDownY) > 20 * density) { // 下拉距離超過20dp
mPullY = mMoveY;
if (mPullListener.onPullDownStart()) {
isPull = true;
}
}
} else {
// 阻尼下拉(隨著下拉距離增加,阻力增加)
mPullListener.onPullDown(mMoveY - mPullY + v.getScrollY());
// 等阻力下拉(阻力恒定,不隨下拉距離增加而增加)
// mPullListener.onPullDown(mMoveY - mPullY);
if (mMoveY < mPullY) {
isPull = false;
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mVelocityTracker != null) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
if (isPull) {
mPullY = 0;
isPull = false;
mPullListener.onPullDownDrop();
return true;
}
break;
}
return true;
}
});
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (isPull) {
// 正在下拉時,阻住Touch事件向下傳遞,同時會向各個ChildView發(fā)送ACTION_CANLE事件,
// 使之前捕捉到了ACTION_DOWN事件的ChildView回復(fù)到正常狀態(tài)
return true;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownY = (int) ev.getY();
break;
case MotionEvent.ACTION_MOVE:
mMoveY = (int) ev.getY();
if (!isPull) {
if (getFirstVisiblePosition() == 0) {
View view = getChildAt(0);
mVelocityTracker.computeCurrentVelocity(1000, 8000f);
if (mVelocityTracker.getYVelocity() > 500// 下拉速度大于500
&& (view == null || view.getTop() == getPaddingTop()) // 已拉動到頂部
&& Math.abs(mMoveY - mDownY) > 15 * density) { // 下拉距離超過20dp
mPullY = mMoveY;
if (mPullListener.onPullDownStart()) {
// 根據(jù)返回值確認(rèn)是否進(jìn)入下拉狀態(tài)
isPull = true;
}
}
}
} else {
// 阻尼下拉(隨著下拉距離增加,阻力增加)
mPullListener.onPullDown(mMoveY - mPullY);
// 等阻力下拉(阻力恒定,不隨下拉距離增加而增加)
// mPullListener.onPullDown(mMoveY - mPullY - getScrollY());
if (mMoveY < mPullY) {
isPull = false;
}
return true;
}
break;
case MotionEvent.ACTION_UP:
if (mVelocityTracker != null) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
if (isPull) {
mPullY = 0;
isPull = false;
mPullListener.onPullDownDrop();
return true;
}
break;
case MotionEvent.ACTION_CANCEL:
break;
}
return super.onTouchEvent(ev);
}
}
然后是外層的LinearyLayer,監(jiān)聽PullableListView的下拉回調(diào),實現(xiàn)下拉效果。同時提供ListView(GridView)的外部接口,如 setEmptyView(View view),setAdapter(ListAdapter adapter)...等等,這里只提供部分我需要使用的,可以根據(jù)自身需求去提供外部接口。
代碼中R.drawable.pulltorefresh 和 R.drawable.loading 分別是下拉箭頭 和 刷新滾動條 的圖片,這里不提供了,自己隨意找兩張圖片貼上就行了。
PullToRefreshView:
public class PullToRefreshView extends LinearLayout {
protected static final String TAG = "PullToRefreshView";
/**
* 下拉阻力系數(shù)
*/
private static final float SCALL_PULL_DOWW = 2.0f;
private View mView;
private PullableListView mListView;
private TextView mPullTv;
private ImageView mProgressBar;
private View mPullV;
private View mEmptyView;
private boolean isInited;
private boolean canRefresh;
private boolean isRefreshing;
private boolean isPullable = true;
private int mOrMargin;
private ObjectAnimator mArrowRotateAnimator;
private Animation mProAnimation;
private PullToRefreshListener mPullToRefreshListener;
public PullToRefreshView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
public PullToRefreshView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public PullToRefreshView(Context context) {
super(context);
initView(context);
}
public interface PullToRefreshListener {
/**
* do data refresh here
*/
public void onRefreshStart();
/**
* do view update here
*/
public void onRefreshFinished();
}
private void initView(Context context) {
if (!isInited) {
isInited = true;
mView = LayoutInflater.from(context).inflate(R.layout.view_pulltorefresh, null);
mProgressBar = (ImageView) mView.findViewById(R.id.iv_pulltorefresh_arrow);
mProgressBar.setImageResource(R.drawable.pulltorefresh);
mPullTv = (TextView) mView.findViewById(R.id.tv_pulltorefresh);
mPullV = mView.findViewById(R.id.ly_pulltorefresh_pull);
mListView = (PullableListView) mView.findViewById(R.id.gv_smarturc_urcs);
mListView.setPullListener(mPullListener);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
addView(mView, lp);
LayoutParams lParams = (LayoutParams) mPullV.getLayoutParams();
mOrMargin = lParams.topMargin;
mProAnimation = AnimationUtils.loadAnimation(getContext(),
R.anim.anim_progressbar);
}
}
private PullListener mPullListener = new PullListener() {
@Override
public boolean onPullDownStart() {
if (isRefreshing || !isPullable) {
return false;
}
mPullTv.setText("下拉刷新");
mProgressBar.setRotation(0f);
mProgressBar.setImageResource(R.drawable.pulltorefresh);
if (mProgressBar.getAnimation() != null) {
mProgressBar.clearAnimation();
}
return true;
}
@Override
public void onPullDown(int moveY) {
if (isRefreshing || !isPullable) {
return;
}
moveY = (int) Math.max(0, moveY / SCALL_PULL_DOWW);
mView.scrollTo(0, -moveY);
mEmptyView.scrollTo(0, -moveY);
if (!canRefresh
&& Math.abs(mView.getScrollY()) > Math.abs(mOrMargin)) {
mPullTv.setText("松開刷新");
canRefresh = true;
if (mArrowRotateAnimator != null) {
mArrowRotateAnimator.cancel();
}
float rotation = mProgressBar.getRotation();
mArrowRotateAnimator = ObjectAnimator.ofFloat(mProgressBar, "rotation",
rotation, 180f);
mArrowRotateAnimator.setDuration(100).start();
} else if (canRefresh
&& Math.abs(mView.getScrollY()) <= Math.abs(mOrMargin)) {
mPullTv.setText("下拉刷新");
canRefresh = false;
if (mArrowRotateAnimator != null) {
mArrowRotateAnimator.cancel();
}
float rotation = mProgressBar.getRotation();
mArrowRotateAnimator = ObjectAnimator.ofFloat(mProgressBar, "rotation",
rotation, 0f);
mArrowRotateAnimator.setDuration(100).start();
}
}
@Override
public void onPullDownDrop() {
if (canRefresh) {
setRefreshing();
} else {
isRefreshing = false;
backTo(mView.getScrollY(), 0);
}
}
};
private void backTo(final int from, final int to) {
ObjectAnimator.ofInt(mView, "scrollY", from, to).setDuration(300)
.start();
ObjectAnimator.ofInt(mEmptyView, "scrollY", from, to).setDuration(300)
.start();
}
/**
* 設(shè)置為正在刷新狀態(tài)
*/
public void setRefreshing() {
isRefreshing = true;
mProgressBar.setImageResource(R.drawable.loading);
mProgressBar.startAnimation(mProAnimation);
mPullTv.setText("正在刷新");
backTo(mView.getScrollY(), mOrMargin);
if (mPullToRefreshListener != null) {
mPullToRefreshListener.onRefreshStart();
}
}
/**
* 刷新完成
*/
public void setRrefreshFinish() {
if (isRefreshing) {
isRefreshing = false;
backTo(mView.getScrollY(), 0);
}
if (mPullToRefreshListener != null) {
mPullToRefreshListener.onRefreshFinished();
}
}
public void setPullable(boolean pullable) {
isPullable = pullable;
}
public void setPullToRefreshListener(
PullToRefreshListener mPullToRefreshListener) {
this.mPullToRefreshListener = mPullToRefreshListener;
}
public void setAdapter(ListAdapter adapter) {
mListView.setAdapter(adapter);
}
public void setEmptyView(View emptyView) {
mListView.setEmptyView(emptyView);
this.mEmptyView = emptyView;
}
public void setOnItemClickListener(OnItemClickListener itemClickListener) {
mListView.setOnItemClickListener(itemClickListener);
}
public void setOnItemLongClickListener(OnItemLongClickListener itemLongClickListener) {
mListView.setOnItemLongClickListener(itemLongClickListener);
}
}
layout-view_pulltorefresh:
<?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:background="#cccccc"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/ly_pulltorefresh_pull"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="-48dp" >
<ImageView
android:id="@+id/iv_pulltorefresh_arrow"
android:layout_width="20dp"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:src="@drawable/pulltorefresh" />
<TextView
android:id="@+id/tv_pulltorefresh"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="4dp"
android:layout_marginLeft="8dp"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="16sp" />
</LinearLayout>
<com.example.pulltorefresh.PullableListView
android:id="@+id/gv_smarturc_urcs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
android:overScrollMode="never"
android:scrollingCache="false" >
</com.example.pulltorefresh.PullableListView>
</LinearLayout>
anim-anim_progressbar:
<?xml version="1.0" encoding="utf-8"?> <rotate xmlns:android="http://schemas.android.com/apk/res/android" android:fromDegrees="0" android:toDegrees="360" android:pivotX="50%" android:pivotY="50%" android:repeatCount="infinite" android:repeatMode="restart" android:duration="800" android:interpolator="@android:anim/linear_interpolator"/>
最后是DEMO ACTIVITY:
public class PullToRefreshActivity extends Activity {
private PullToRefreshView mPullToRefreshView;
private List<String> data = new ArrayList<String>();
private MyAdapter mAdapter;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pulltorefresh);
mHandler = new Handler();
mPullToRefreshView = (PullToRefreshView) findViewById(R.id.pullToRefreshView1);
mAdapter = new MyAdapter();
mPullToRefreshView.setAdapter(mAdapter);
mPullToRefreshView.setEmptyView(findViewById(R.id.empty));
mPullToRefreshView.setOnItemLongClickListener(new OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(getApplicationContext(), "Long click : " + data.get(position),
Toast.LENGTH_SHORT).show();
return true;
}
});
mPullToRefreshView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(getApplicationContext(), data.get(position), Toast.LENGTH_SHORT)
.show();
}
});
mPullToRefreshView.setPullToRefreshListener(new PullToRefreshListener() {
@Override
public void onRefreshStart() {
// 模擬刷新數(shù)據(jù)
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
data.add(String.valueOf((int) (Math.random() * 1000)));
mPullToRefreshView.setRrefreshFinish();
}
}, 2000);
}
@Override
public void onRefreshFinished() {
// 更新視圖
mAdapter.notifyDataSetChanged();
}
});
// mHandler.postDelayed(new Runnable() {
// @Override
// public void run() {
// // TODO Auto-generated method stub
// mPullToRefreshView.setRefreshing();
// }
// }, 500);
}
public class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
// TODO Auto-generated method stub
return data.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return data.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
if (convertView == null) {
convertView = new TextView(PullToRefreshActivity.this);
}
TextView textView = (TextView) convertView;
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40f);
textView.setPadding(30, 30, 30, 30);
textView.setText(data.get(position));
return convertView;
}
}
}
layout-activity_pulltorefresh:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.example.pulltorefresh.PullToRefreshView
android:id="@+id/pullToRefreshView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</com.example.pulltorefresh.PullToRefreshView>
<LinearLayout
android:id="@+id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="60dp" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="NO DATA" />
</LinearLayout>
</RelativeLayout>
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- android使用PullToRefresh框架實現(xiàn)ListView下拉刷新上拉加載更多
- android使用Ultra-PullToRefresh實現(xiàn)下拉刷新自定義代碼
- android使用PullToRefresh實現(xiàn)下拉刷新和上拉加載
- Android使用PullToRefresh完成ListView下拉刷新和左滑刪除功能
- Android開源項目PullToRefresh下拉刷新功能詳解2
- Android開源項目PullToRefresh下拉刷新功能詳解
- Android下拉刷新控件PullToRefresh實例解析
- Android使用PullToRefresh實現(xiàn)上拉加載和下拉刷新效果的代碼
- Android程序開發(fā)之使用PullToRefresh實現(xiàn)下拉刷新和上拉加載
- Android PullToRefreshLayout下拉刷新控件的終結(jié)者
- Android帶刷新時間顯示的PullToRefresh上下拉刷新
相關(guān)文章
Android應(yīng)用開發(fā)中觸摸屏手勢識別的實現(xiàn)方法解析
這篇文章主要介紹了Android應(yīng)用開發(fā)中觸摸屏手勢識別的實現(xiàn)方法解析,深入的部分則是對左右手勢的識別給出了相關(guān)編寫思路,需要的朋友可以參考下2016-02-02
Android開發(fā)之a(chǎn)ctiviti節(jié)點跳轉(zhuǎn)
這篇文章主要介紹了Android開發(fā)之a(chǎn)ctiviti節(jié)點跳轉(zhuǎn)的相關(guān)資料,需要的朋友可以參考下2016-04-04
Android學(xué)習(xí)筆記——Menu介紹(三)
今天繼續(xù)昨天沒有講完的Menu的學(xué)習(xí),主要是Popup Menu的學(xué)習(xí),需要的朋友可以參考下2014-10-10
Android解決getExternalStorageDirectory在29后廢棄問題(推薦)
這篇文章主要介紹了Android解決getExternalStorageDirectory在29后廢棄問題(推薦),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02
簡單學(xué)習(xí)Android Socket的使用方法
這篇文章主要幫助大家簡單學(xué)習(xí)Android Socket的使用方法,感興趣的小伙伴們可以參考一下2016-03-03
手把手教學(xué)Android用jsoup解析html實例
本篇文章主要介紹了手把手教學(xué)Android用jsoup解析html實例,jsoup 是一款Java 的HTML解析器。具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06

