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)是否進入下拉狀態(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-02Android開發(fā)之a(chǎn)ctiviti節(jié)點跳轉(zhuǎn)
這篇文章主要介紹了Android開發(fā)之a(chǎn)ctiviti節(jié)點跳轉(zhuǎn)的相關(guān)資料,需要的朋友可以參考下2016-04-04Android學(xué)習(xí)筆記——Menu介紹(三)
今天繼續(xù)昨天沒有講完的Menu的學(xué)習(xí),主要是Popup Menu的學(xué)習(xí),需要的朋友可以參考下2014-10-10Android解決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