博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ListView下拉刷新实现(类似陌陌的箭头转动)
阅读量:6819 次
发布时间:2019-06-26

本文共 12290 字,大约阅读时间需要 40 分钟。

1 import android.animation.Animator;  2 import android.animation.Animator.AnimatorListener;  3 import android.animation.TypeEvaluator;  4 import android.animation.ValueAnimator;  5 import android.animation.ValueAnimator.AnimatorUpdateListener;  6 import android.content.Context;  7 import android.util.AttributeSet;  8 import android.view.LayoutInflater;  9 import android.view.MotionEvent; 10 import android.view.View; 11 import android.view.ViewGroup; 12 import android.view.animation.DecelerateInterpolator; 13 import android.widget.ImageView; 14 import android.widget.LinearLayout; 15 import android.widget.ListView; 16 import android.widget.ProgressBar; 17  18 import com.customview.R; 19  20 public class DownRefreshListView extends ListView { 21  22     public DownRefreshListView(Context context) { 23         super(context); 24         init(); 25     } 26  27     public DownRefreshListView(Context context, AttributeSet attrs) { 28         super(context, attrs); 29         init(); 30     } 31  32     public DownRefreshListView(Context context, AttributeSet attrs, int defStyle) { 33         super(context, attrs, defStyle); 34         init(); 35     } 36  37     private void init() { 38         // Listview的headerview(注意headerview的Layout布局要为LinearLayout) 39         mHeaderView = (LinearLayout) LayoutInflater.from(getContext()).inflate( 40                 R.layout.header_view, null); 41         // 指示箭头 42         mArrowImage = (ImageView) mHeaderView.findViewById(R.id.headerArrow); 43         // 指示进度 44         mProgressbar = (ProgressBar) mHeaderView 45                 .findViewById(R.id.headerProgressbar); 46         // 测量 47         measureView(mHeaderView); 48         // 获取测量后的headerview高度 49         mHeaderHeight = mHeaderView.getMeasuredHeight(); 50         // 设置headerview的顶部缩进 51         mHeaderView.setPadding(mHeaderView.getPaddingLeft(), -mHeaderHeight, 52                 mHeaderView.getPaddingRight(), mHeaderView.getPaddingBottom()); 53         // 添加headerview 54         this.addHeaderView(mHeaderView, null, false); 55         this.setDivider(null); 56         this.setVerticalScrollBarEnabled(false); 57     } 58  59     private void measureView(View v) { 60         ViewGroup.LayoutParams p = v.getLayoutParams(); 61         if (p == null) { 62             p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 63                     ViewGroup.LayoutParams.WRAP_CONTENT); 64  65         } 66         int viewWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width); 67         int viewHeightSpec = ViewGroup.getChildMeasureSpec(0, 0, p.height); 68         v.measure(viewWidthSpec, viewHeightSpec); 69     } 70  71     @Override 72     public boolean onTouchEvent(MotionEvent ev) { 73         // 正在refresh data时,直接返回 74         if (mState == State.REFRESH_LOADING) { 75             return super.onTouchEvent(ev); 76         } 77         int action = ev.getAction(); 78         switch (action) { 79         case MotionEvent.ACTION_DOWN: 80             mLastY = ev.getY(); 81             // 当前可视位置为顶部 82             if (mState == State.IDLE && getFirstVisiblePosition() == 0 83                     && !hasStarted) { 84                 mState = State.PULL_TO_REFRESH; 85                 hasStarted = true; 86                 return true; 87             } 88             // 允许动画播放时,用户点击,终止动画,同时继续下拉动作 89             if ((mState == State.PULL_TO_REFRESH || mState == State.ENOUGH_TO_REFRESH) 90                     && hasStarted && isAnimated) { 91                 isCancelManually = true; 92                 // 调用cancel方法,亦会调用animator listener的onAnimationEnd方法 93                 mValueAnimator.cancel(); 94                 return true; 95             } 96             break; 97         case MotionEvent.ACTION_MOVE: 98             mCurrentY = ev.getY(); 99             float deltaY = mCurrentY - mLastY;100             mLastY = mCurrentY;101             // 已经开始,且滑动距离大于认定最小滑动距离102             if (hasStarted && Math.abs(deltaY) > mCustomScaledTouchSlop) {103                 mScrollStated = true;104             }105             if (mScrollStated && mState != State.IDLE106                     && mState != State.REFRESH_LOADING) {107                 // 向下滑动108                 if (deltaY > 0) {109                     // 设置headerview的顶部缩进110                     int paddingTop = mHeaderView.getPaddingTop()111                             + (int) (deltaY / 3);112                     mHeaderView.setPadding(0, paddingTop, 0, 0);113                     // headerview完全可见时,当前状态更动为释放刷新114                     if (paddingTop >= 0) {115                         mState = State.ENOUGH_TO_REFRESH;116                     } else {117                         mState = State.PULL_TO_REFRESH;118                     }119                     // 大于二分之一可见时,转动箭头120                     if (paddingTop > -mHeaderHeight * 0.5f) {121                         float degree = (paddingTop + mHeaderHeight * 0.5f)122                                 / (mHeaderHeight * 0.5f) * 180;123                         mArrowImage.setRotation(Math.min(degree, 180));124                     }125                 } else {126                     // 向上滑动127                     int paddingTop = mHeaderView.getPaddingTop()128                             + (int) (deltaY / 3);129                     // 当前headerview完全可见,优先减少headerview缩进距离130                     if (paddingTop > 0) {131                         mState = State.ENOUGH_TO_REFRESH;132                     } else if (paddingTop > -mHeaderHeight) {133                         // 当前headerview不完全可见,箭头转动角度回滚134                         float degree = (paddingTop + mHeaderHeight * 0.5f)135                                 / (mHeaderHeight * 0.5f) * 180;136                         mArrowImage.setRotation(Math.max(0, degree));137                         // 当前headerview不完全可见,设置当前状态为下拉刷新状态138                         mState = State.PULL_TO_REFRESH;139                     } else {140                         // 当前headerview不可见141                         mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);142                         mState = State.IDLE;143                         return super.onTouchEvent(ev);144                     }145                     mHeaderView.setPadding(0, paddingTop, 0, 0);146                 }147                 return true;148             }149             break;150         case MotionEvent.ACTION_UP:151             if (mState == State.PULL_TO_REFRESH) {152                 // 下拉刷新状态下,手指弹起153                 smoothAnimate(300, mHeaderView.getPaddingTop(), -mHeaderHeight);154                 if (mScrollStated) {155                     mScrollStated = false;156                     return true;157                 }158             } else if (mState == State.ENOUGH_TO_REFRESH) {159                 // 释放刷新状态下,手指弹起160                 smoothAnimate(300, mHeaderView.getPaddingTop(), 0);161                 return true;162             } else {163                 mState = State.IDLE;164                 hasStarted = false;165                 mScrollStated = false;166             }167             break;168         }169         return super.onTouchEvent(ev);170     }171 172     /** 平滑动画,使headerview的顶部缩进回到指定状态. */173     private void smoothAnimate(int duration, int startValue, int endValue) {174         mValueAnimator = ValueAnimator.ofObject(new TypeEvaluator
() {175 176 @Override177 public Integer evaluate(float fraction, Integer startValue,178 Integer endValue) {179 return (int) (startValue + fraction * (endValue - startValue));180 }181 }, startValue, endValue);182 mValueAnimator.addUpdateListener(new AnimatorUpdateListener() {183 184 @Override185 public void onAnimationUpdate(ValueAnimator animation) {186 int middleValue = (Integer) animation.getAnimatedValue();187 // headerview顶部缩进回滚188 mHeaderView.setPadding(0, middleValue, 0, 0);189 // headerview指示箭头转动角度回滚190 float degree = (mHeaderView.getPaddingTop() + 0.5f * mHeaderHeight)191 / (mHeaderHeight * 0.5f) * 180;192 if (degree > 180) {193 degree = 180;194 }195 mArrowImage.setRotation(Math.max(0, degree));196 }197 });198 // 动画监听199 mValueAnimator.addListener(new AnimatorListener() {200 201 @Override202 public void onAnimationStart(Animator animation) {203 isAnimated = true;204 }205 206 @Override207 public void onAnimationRepeat(Animator animation) {208 }209 210 @Override211 public void onAnimationEnd(Animator animation) {212 isAnimated = false;213 // 手动终止动画时,不改变当前状态,允许继续下拉214 if (isCancelManually) {215 isCancelManually = false;216 return;217 }218 if (isRefreshComplete) {219 isRefreshComplete = false;220 mProgressbar.setVisibility(View.GONE);221 mArrowImage.setVisibility(View.VISIBLE);222 mArrowImage.setRotation(0);223 }224 if (mState == State.PULL_TO_REFRESH) {225 mState = State.IDLE;226 hasStarted = false;227 }228 if (mState == State.ENOUGH_TO_REFRESH) {229 mState = State.REFRESH_LOADING;230 mArrowImage.setVisibility(View.GONE);231 mProgressbar.setVisibility(View.VISIBLE);232 hasStarted = false;233 mScrollStated = false;234 if (mRefreshExecutor != null) {235 mRefreshExecutor.refreshData();236 }237 }238 }239 240 @Override241 public void onAnimationCancel(Animator animation) {242 isAnimated = false;243 }244 });245 mValueAnimator.setDuration(duration);246 mValueAnimator.setInterpolator(new DecelerateInterpolator());247 mValueAnimator.start();248 }249 250 /** 刷新完成后,由刷新任务执行者,调用刷新完成 */251 public void refreshComplete() {252 mState = State.IDLE;253 isRefreshComplete = true;254 smoothAnimate(500, mHeaderView.getPaddingTop(), -mHeaderHeight);255 }256 257 public void setRefreshExecutor(RefreshExecutor executor) {258 mRefreshExecutor = executor;259 }260 261 /** 刷新数据回调接口. */262 public interface RefreshExecutor {263 public void refreshData();264 }265 266 public enum State {267 /** 空闲状态. */268 IDLE,269 /** 下拉刷新状态. */270 PULL_TO_REFRESH,271 /** 释放可刷新状态. */272 ENOUGH_TO_REFRESH,273 /** 正在刷新状态. */274 REFRESH_LOADING275 }276 277 private LinearLayout mHeaderView;278 private ImageView mArrowImage;279 private ProgressBar mProgressbar;280 private int mHeaderHeight;281 /** 当前状态. */282 private State mState = State.IDLE;283 /** 是否开始下拉. */284 private boolean hasStarted = false;285 /** 是否开始滚动. */286 private boolean mScrollStated = false;287 /** 下拉刷新回调接口. */288 private RefreshExecutor mRefreshExecutor;289 /** 是否手动停止动画播放. */290 private boolean isRefreshComplete = false;291 292 /** 属性动画. */293 private ValueAnimator mValueAnimator;294 /** 动画是否已经播放. */295 private boolean isAnimated = false;296 /** 是否手动停止动画播放. */297 private boolean isCancelManually = false;298 299 /** 自定义最小点击滚动距离. */300 private final int mCustomScaledTouchSlop = 6;301 /** 上一次Y坐标. */302 private float mLastY;303 /** 当前Y坐标. */304 private float mCurrentY;305 }
View Code

 header_view.xml

1 
2
6 7
18 19
28 29 30 31
View Code

ProgressBar样式及背景

1 
2
8
View Code

 

用到的资源:

down_arrow.png

progress_refresh.png

转载于:https://www.cnblogs.com/Professionalbutcher/p/3780340.html

你可能感兴趣的文章