仿百度外卖下拉刷新新番

看了Hankkin这位老哥写的文章 ,我抱着试一试的态度copy下,原文链接http://blog.csdn.net/lyhhj/article/details/51120539。写完后下载了最新版的百度外卖app,发现其刷新已经做了改动,但是万变不离其宗,精髓依旧,然后新番出炉。

·points:

1.动画
·· 帧动画:一张张图片不断的切换,形成动画效果。在drawable文件夹下放入图片,并创建animation-list标签文件
·· 补间动画:通过在两个关键帧之间补充渐变的动画效果。
JAVA代码中设置:AlphaAnimation,TranslateAnimation,ScaleAnimation,RotateAnimation,AnimationSet;
XML文件中设置:在res目录下新建anim文件夹,在anim文件夹下创建对应的动画文件alpha,rotate,scale,translate,set。
ps:补间动画只是改变了View对象绘制的位置,而没有改变View对象本身
·· 属性动画:既有动画效果又使得View本身得到了真正改变。
JAVA代码中设置: ObjectAnimator-ofFloat(),ofInt(),ofObject(),ofArgb(),ofPropertyValuesHolder();
XML文件中设置:和补间动画基本一致,Animator anim = AnimatorInflater.loadAnimator(this, R.animator.animator);

2.onTouchEvent事件的处理
··MotionEvent.ACTION_DOWN:
记录按下点的Y坐标。
··MotionEvent.ACTION_MOVE:
通过.setPadding()更新下拉刷新视图的显示高度,并通过offsetY来记录其状态。
··MotionEvent.ACTION_UP:
对两种状态进行处理:没有达到刷新高度的回滚至隐藏,达到或超过其刷新高度的回滚至刷新的高度,并回调接口更新状态。

·talk is less:

1.layout_header_view布局(有坑也要小心)

    
    
        
    
            
    
            
    
            
    
            
    
            
    
                
    
                
    
                
            
        
    
layout_header_view.png

2.TakeRefreshLayout

    public class TakeRefreshLayout extends ListView implements AbsListView.OnScrollListener {
        private static final int PULL_FINISH = 0;   //刷新完毕
        private static final int PULL_TO_DOWN = 1;  //下拉状态
        private static final int PULL_TO_RELEASE = 2;   //释放状态
        private static final int PULL_REFRESHING = 3;   //正在刷新
    
        private Context mContext;
        private RelativeLayout mHeadView;    //刷新的头布局
        private int mHeadViewH;     //头布局的高度
        private int mFirstVisibleItem;  //第一项可见的item索引
        private ImageView ivDriver, ivCloudLeft, ivCloudRight, ivCastleLeft, ivCastleRight, ivSun;
        private Animation sunAnim, cloudLAnim, cloudRAnim, castleLAnim, castleRAnim;
        private AnimationDrawable driverAnim;
    
        private float startY;   //开始时的Y坐标
        private float offsetY;  //Y轴偏移量
    
        private int state;  //状态
        private boolean isRefreshable;  //是否可刷新
        private OnTakeRefreshListener mTakeRefreshListener;
    
        public TakeRefreshLayout(Context context) {
            super(context);
            init(context);
        }
    
        public TakeRefreshLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public TakeRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(context);
        }
    
        /**
         * 回调接口
         */
        public interface OnTakeRefreshListener {
            void onRefresh();
        }
    
        /**
         * 接口回调提供的方法
         *
         * @param onTakeRefreshListener
         */
        public void setOnTakeRefreshListener(OnTakeRefreshListener onTakeRefreshListener) {
            mTakeRefreshListener = onTakeRefreshListener;
            isRefreshable = true;
        }
    
        /**
         * 是否正在刷新
         *
         * @return
         */
        public boolean isRefreshing() {
            return state == PULL_REFRESHING;
        }
    
        /**
         * 刷新完成
         */
        public void finishRefresh() {
            state = PULL_FINISH;
            //刷新完毕
            mHeadView.setPadding(0, -mHeadViewH, 0, 0);
            stopAnim();
        }
    
        /**
         * 初始化
         *
         * @param context
         */
        private void init(Context context) {
            mContext = context;
            //关闭view的OverScroll
            setOverScrollMode(OVER_SCROLL_NEVER);
            setOnScrollListener(this);
            //加载头布局
            mHeadView = (RelativeLayout) LayoutInflater.from(context).inflate(R.layout.layout_head_view, this, false);
            //测量头布局
            measureView(mHeadView);
            //测量头布局
            addHeaderView(mHeadView);
            mHeadViewH = mHeadView.getMeasuredHeight();
            mHeadView.setPadding(0, -mHeadViewH, 0, 0);
    
            //获取头布局控件
            ivDriver = (ImageView) mHeadView.findViewById(R.id.iv_postman);
            ivCastleLeft = (ImageView) mHeadView.findViewById(R.id.iv_castle_left);
            ivCastleRight = (ImageView) mHeadView.findViewById(R.id.iv_castle_right);
            ivCloudLeft = (ImageView) mHeadView.findViewById(R.id.iv_cloud_left);
            ivCloudRight = (ImageView) mHeadView.findViewById(R.id.iv_cloud_right);
            ivSun = (ImageView) mHeadView.findViewById(R.id.iv_sun);
    
            //获取动画
            driverAnim = (AnimationDrawable) ivDriver.getBackground();
            sunAnim = AnimationUtils.loadAnimation(context, R.anim.rotate_sun);
            cloudLAnim = AnimationUtils.loadAnimation(context, R.anim.translate_cloud_left);
            cloudRAnim = AnimationUtils.loadAnimation(context, R.anim.translate_cloud_right);
            castleLAnim = AnimationUtils.loadAnimation(context, R.anim.translate_castle_left);
            castleRAnim = AnimationUtils.loadAnimation(context, R.anim.translate_castle_right);
    
            LinearInterpolator interpolator = new LinearInterpolator();
            sunAnim.setInterpolator(interpolator);
            cloudLAnim.setInterpolator(interpolator);
            cloudRAnim.setInterpolator(interpolator);
            castleLAnim.setInterpolator(interpolator);
            castleRAnim.setInterpolator(interpolator);
    
            state = PULL_FINISH;
            isRefreshable = false;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (isRefreshable) {
                switch (ev.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        //在listview顶部
                        if (mFirstVisibleItem == 0) {
                            startY = ev.getY();
                        }
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if (state != PULL_REFRESHING) {
                            float tempY = ev.getY();
                            offsetY = tempY - startY;
                            //放开刷新的状态
                            if (state == PULL_TO_RELEASE) {
                                setSelection(0);
                                if (offsetY / 3 < DensityUtil.dip2px(mContext, 150)) {
                                    state = PULL_TO_DOWN;
                                } else if (offsetY <= 0) {
                                    state = PULL_FINISH;
                                }
                            }
                            //下拉刷新状态
                            if (state == PULL_TO_DOWN) {
                                setSelection(0);
                                if (offsetY / 3 > DensityUtil.dip2px(mContext, 150)) {
                                    state = PULL_TO_RELEASE;
                                } else if (offsetY <= 0) {
                                    state = PULL_FINISH;
                                }
                            }
                            //finish状态
                            if (state == PULL_FINISH) {
                                if (offsetY >= 0) {
                                    state = PULL_TO_DOWN;
                                    startAnim();
                                }
                            }
                            //需要更新视图的两种状态
                            if (state == PULL_TO_DOWN) {
                                mHeadView.setPadding(0, (int) (-mHeadViewH + offsetY / 3), 0, 0);
                            }
                            if (state == PULL_TO_RELEASE) {
                                mHeadView.setPadding(0, (int) (-mHeadViewH + offsetY / 3), 0, 0);
                            }
                        }
                        break;
                    case MotionEvent.ACTION_UP:
                        //下拉没有超过规定的高度,不执行刷新,抬起手指更新state,停止动画
                        if (state == PULL_TO_DOWN) {
                            smoothScrollBy((int) offsetY / 3, 500);
                            stopAnim();
                            state = PULL_FINISH;
                        }
                        //当下拉高度超过150dp时执行平滑滚动到150dp并刷新
                        if (state == PULL_TO_RELEASE) {
                            if (offsetY / 3 > DensityUtil.dip2px(mContext, 150)) {
                                smoothScrollBy((int) (offsetY / 3 - DensityUtil.dip2px(mContext, 150)), 500);
                            }
                            state = PULL_REFRESHING;
                            //回调onRefresh方法
                            mTakeRefreshListener.onRefresh();
                        }
                        break;
                    default:
                        break;
                }
            }
            return super.onTouchEvent(ev);
        }
    
        /**
         * 开始动画
         */
        private void startAnim() {
            ivSun.startAnimation(sunAnim);
            ivCloudLeft.startAnimation(cloudLAnim);
            ivCloudRight.startAnimation(cloudRAnim);
            ivCastleLeft.startAnimation(castleLAnim);
            ivCastleRight.startAnimation(castleRAnim);
            driverAnim.start();
        }
    
        /**
         * 关闭动画
         */
        private void stopAnim() {
            ivSun.clearAnimation();
            ivCloudLeft.clearAnimation();
            ivCloudRight.clearAnimation();
            ivCastleLeft.clearAnimation();
            ivCastleRight.clearAnimation();
            driverAnim.stop();
        }
    
        /**
         * 测量View
         *
         * @param child
         */
        private void measureView(View child) {
            ViewGroup.LayoutParams params = child.getLayoutParams();
            if (params == null) {
                params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            }
            int childWidthSpec = ViewGroup.getChildMeasureSpec(0, child.getPaddingLeft(), params.width);
            int pHeight = params.height;
            int childHeightSpec;
            if (pHeight > 0) {
                childHeightSpec = MeasureSpec.makeMeasureSpec(pHeight, MeasureSpec.EXACTLY);
            } else {
                childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            }
            child.measure(childWidthSpec, childHeightSpec);
        }
    
        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
    
        }
    
        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            mFirstVisibleItem = firstVisibleItem;
        }
    }

3.MainActivity

    public class MainActivity extends AppCompatActivity implements TakeRefreshLayout.OnTakeRefreshListener {
        private final static int REFRESH_COMPLETE = 0;
    
        private TakeRefreshLayout mRefreshLayout;
        private List mDatas;
        private ArrayAdapter mAdapter;
        private Handler mHandler = new Handler() {
            public void handleMessage(android.os.Message msg) {
                switch (msg.what) {
                    case REFRESH_COMPLETE:
                        if (mRefreshLayout.isRefreshing()) {
                            mRefreshLayout.finishRefresh();
                        }
                        Toast.makeText(MainActivity.this, "刷新成功", Toast.LENGTH_SHORT).show();
                        mAdapter.notifyDataSetChanged();
                        mRefreshLayout.setSelection(0);
                        break;
    
                    default:
                        break;
                }
            }
        };
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mRefreshLayout = (TakeRefreshLayout) findViewById(R.id.take_refresh_view);
            String[] data = new String[]{"a", "b", "c", "d", "e", "f", "g", "h", "i",
                    "j", "k", "l", "m", "n", "o", "p", "q", "r", "s"};
            mDatas = new ArrayList<>(Arrays.asList(data));
            mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mDatas);
            mRefreshLayout.setAdapter(mAdapter);
            mRefreshLayout.setOnTakeRefreshListener(this);
        }
    
        @Override
        public void onRefresh() {
            mDatas.add(0, "new data");
            mHandler.sendEmptyMessageDelayed(REFRESH_COMPLETE, 4000);
        }
    }
show_time.png

·总结

敲一次代码就应该有所收货,每一次进步都应该玩的不亦乐乎。
就总结一下这个下拉刷新版本的不足吧:
1.没有多点触控
2.仅能用在ListView,不能无缝替换RecyclerView
3....

你可能感兴趣的:(仿百度外卖下拉刷新新番)