简单的下拉刷新 PullToRefreshView

网上下拉刷新的DEMO很多,但是总有各种不满意的地方,有些会下拉卡住,有些回弹不流畅,有些性能太低会各种卡顿,有些emptyView无法下拉......

自己写的才是最合适自己的,代码很简单,也很容易修改,稍微阅读下代码就能改出自己需要的各种效果。

首先,重写ListView,自定义Touch事件,为了使emptyView也可下拉,emptyView也加上Touch事件。 如果要实现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发送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:

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:

<?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() {
                // 模拟刷新数据
                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>
时间: 2024-10-08 07:11:49

简单的下拉刷新 PullToRefreshView的相关文章

Android基础控件——SwipeRefreshLayout最简单的下拉刷新

还在使用传统的下拉刷新,觉得不够漂亮,怕被产品经理骂吗? 还在忧愁自己技术不够好,不会改造带动画的下拉刷新吗? 那么不要担心,使用SwipeRefreshLayout最简单的下拉刷新,既不失美观又简洁 SwipeRefreshLayout下拉刷新是Google自家的下拉刷新控件,使用过程跟开源库PullToRefresh差不多,废话不多说,开车啦 SwipeRefreshLayout实质上是一个ViewGroup,所以我们将其作为我们的根布局进行演示 经过这个步骤之后,其实在页面上就已经能够下拉

原生js实现简单的下拉刷新功能

前言: 我们在浏览移动端web页面的时候,经常会用到下拉刷新. 现在我们用原生的js实现这个非常简单的下拉刷新功能. (温馨提示:本文比较基础,功能也很简单.写的不好的地方,希望大神提点一二.) 一.创建简单的html页面: 假设代码里的float-box是一个主页面. 二.封装下拉刷新的功能模块: (1)首先创建一个Slide构造函数,用来初始化属性与函数. function Slide(dom){ this.start_y=null;//手指滑动屏幕的初始位置 this.end_y=null

简单的下拉刷新--SwipeRefreshLayout

代码工程简要说明:以一个SwipeRefreshLayout包裹ListView,SwipeRefreshLayout接管ListView的下拉事件,若ListView被用户触发下拉动作后,SwipeRefreshLayout启动下拉刷新的UI表现样式,下拉刷新完毕,在SwipeRefreshLayout提供的接口中回调更新ListView中的数据. activity_main.xml: 1 <RelativeLayout xmlns:android="http://schemas.and

简单实现下拉刷新数据

1 #import "AppDelegate.h" 2 #import "SearchController.h" 3 @interface AppDelegate ()<UITableViewDataSource> 4 @property (nonatomic,strong)UITableView *table; 5 @property (nonatomic,strong)NSMutableArray *datas; 6 @property (nonat

iOS简单的下拉刷新

做一个下拉后自动在tableviewCell 上刷新当前时间的一个小demmo. 新建一个UITableviewController,在viewDidLoad里初始化变量,时间和UIRefreshControl,代码如下: #import "TableViewController.h"@interface ViewController()@property(nonatomic,strong)NSMutableArray *logs;@end@implementation ViewCon

MJRefresh–用法最简单的下拉刷新框架

简介 用于为应用添加常用的上拉加载更多与下拉刷新效果,适用 UIScrollView . UITableView . UICollectionView . UIWebView. 项目主页: MJRefresh 最新示例: 点击下载 快速入门 使用环境 ARC iOS 6.0 + 安装 通过CocoaPods安装 pod 'MJRefresh' 手动安装 将 MJRefresh 文件夹中的所有文件拽入项目中,在需要的地方导入主头文件: 类结构图 MJRefreshComponent.h /** 刷

MJRefresh框架介绍 --(简单的下拉刷新)

一.MJRefresh的类解释. 1.MJRefreshComponent              所有刷新控件的基类别.(component: 成分,组件) 2.MJRefreshNormalHeader          默认的下拉刷新控件 3.MJRefreshAutoNormalFooter    默认的上拉刷新控件                下拉刷新控件自适应在页面内容下面 4.MJRefreshAutoGifFooter          带动态图的上拉加载控件        

自定义下拉刷新控件

一.功能效果 1.在很多app中,在信息展示页面,当我们向下拖拽时,页面会加载最新的数据,并有一个短暂的提示控件出现,有些会有加载进度条,有些会记录加载日期.条目,有些还带有加载动画.其基本实现原理都相仿,本文中将探讨其实现原理,并封装出一个简单的下拉刷新控件 2.自定义刷新工具简单的示例 二.系统提供的下拉刷新工具 1.iOS6.0以后系统提供了自己的下拉刷新的控件:UIRefreshControl .例如,refreshControl,作为UITableViewController中的一个属

UWP开发入门(七)——下拉刷新

本篇意在给这几天Win10 Mobile负面新闻不断的某软洗地,想要证明实现一个简单的下拉刷新并不困难.UWP开发更大的困难在于懒惰,缺乏学习的意愿.而不是“某软连下拉刷新控件都没有”这样的想法. 之前我也没有进行过下拉刷新的研究.于是先去google了几篇blog学习了一下,然后再看了某软官方的Sample.(同学们啊官方有下拉刷新的Sample啊!就在Git上啊!不要钱无门槛啊!)学习之后发现实现的方式大体分为两类. 一类是以某软Sample和博客园MS-UAP封装的PullToRefres