Android实现简单的下拉刷新pulltorefresh
内容摘要
网上下拉刷新的DEMO很多,但是总有各种不满意的地方,有些会下拉卡住,有些回弹不流畅,有些性能太低会各种卡顿,有些emptyView无法下拉......
自己写的才是最合适自己的,代码很简单,
自己写的才是最合适自己的,代码很简单,
文章正文
网上下拉刷新的DEMO很多,但是总有各种不满意的地方,有些会下拉卡住,有些回弹不流畅,有些性能太低会各种卡顿,有些emptyView无法下拉......
自己写的才是最合适自己的,代码很简单,也很容易修改,稍微阅读下代码就能改出自己需要的各种效果。
首先,重写ListView,自定义Touch事件,为了使emptyView也可下拉,emptyView也加上Touch事件。 如果要实现GridView,把这里的ListView改成GridView即可。
PullableListView :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | 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发送ACTION_CANLE事件, // 使之前捕捉到了ACTION_DOWN事件的ChildView回复到正常状态 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()) { // 根据返回值确认是否进入下拉状态 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,监听PullableListView的下拉回调,实现下拉效果。同时提供ListView(GridView)的外部接口,如 setEmptyView(View view),setAdapter(ListAdapter adapter)...等等,这里只提供部分我需要使用的,可以根据自身需求去提供外部接口。
代码中R.drawable.pulltorefresh 和 R.drawable.loading 分别是下拉箭头 和 刷新滚动条 的图片,这里不提供了,自己随意找两张图片贴上就行了。
PullToRefreshView:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | public class PullToRefreshView extends LinearLayout { protected static final String TAG = "PullToRefreshView" ; /** * 下拉阻力系数 */ 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(); } /** * 设置为正在刷新状态 */ 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <?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:
1 2 3 4 5 6 7 8 9 10 | <?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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 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() { // 模拟刷新数据 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <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> |
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持phpstudy。
代码注释