当前位置:网站首页>下拉刷新及上拉加载更多的ListView

下拉刷新及上拉加载更多的ListView

2022-06-22 17:54:00 刘忆初

PullToRefreshListView

  • 自定义ListView
  • 带简洁的下拉刷新及上拉加载更多功能

实现过程

  • 继承已有控件(ListView)实现自定义控件,下拉刷新三个状态效果如下:
    下拉刷新

    释放刷新

    正在刷新

  • 给ListView添加头部和脚部

  • 头部布局
<?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="60dp"
    android:gravity="center"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/iv_arrow"
        android:layout_width="20dp"
        android:layout_height="45dp"
        android:layout_marginRight="20dp"
        android:src="@drawable/common_listview_headview_red_arrow" />

    <ImageView
        android:id="@+id/iv_loading"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:src="@drawable/action_progress_image"
        android:visibility="gone" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="45dp"
        android:gravity="center"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="wrap_content"
            android:layout_height="25dp"
            android:text="下拉刷新"
            android:textSize="18sp" />

        <TextView
            android:id="@+id/tv_time"
            android:layout_width="wrap_content"
            android:layout_height="20dp"
            android:text="最后刷新时间:mm:hh:ss" />
    </LinearLayout>
</LinearLayout>

头部布局效果

  • 脚部布局
<?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="wrap_content"
    android:gravity="center"
    android:orientation="horizontal">

    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="玩命加载中..." />
</LinearLayout>

脚部布局

  • 下面贴出PullToRefreshListView完整代码,如下:
public class PullToRefreshListView extends ListView implements AbsListView.OnScrollListener { private View header; private int headerMeasuredHeight; private TextView tvTitle; private TextView tvTime; private ImageView ivArrow; private ImageView ivLoading; private RotateAnimation pullAnimaton; private RotateAnimation releaseAnimation; private RotateAnimation loadingAnimation; private onPullToRefreshListener onPullToRefreshListener; private View footer; private int footerMeasureHeight; public void setOnPullToRefreshListener(cn.com.bsoft.pulltorefreshlistview.view.onPullToRefreshListener onPullToRefreshListener) { this.onPullToRefreshListener = onPullToRefreshListener; } public PullToRefreshListView(Context context) { this(context, null); } public PullToRefreshListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PullToRefreshListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //给ListView添加头 initHeader(); initFooter(); } private void initFooter() { footer = View.inflate(getContext(), R.layout.footer_view, null); footer.measure(0, 0); footerMeasureHeight = footer.getMeasuredHeight(); footer.setPadding(0, -footerMeasureHeight, 0, 0); addFooterView(footer); setOnScrollListener(this); } private void initHeader() { header = View.inflate(getContext(), R.layout.header_view, null); header.measure(0, 0);//还运行在构造函数,此时不能直接获取header的高度,所以要调用测量,再获取header高度 headerMeasuredHeight = header.getMeasuredHeight(); header.setPadding(0, -headerMeasuredHeight, 0, 0); addHeaderView(header); tvTitle = ((TextView) header.findViewById(R.id.tv_title)); tvTime = ((TextView) header.findViewById(R.id.tv_time)); ivArrow = ((ImageView) header.findViewById(R.id.iv_arrow)); ivLoading = ((ImageView) header.findViewById(R.id.iv_loading)); //释放刷新动画 releaseAnimation = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); releaseAnimation.setDuration(300); releaseAnimation.setFillAfter(true);//动画完成后停在那 //下拉动画 pullAnimaton = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); pullAnimaton.setDuration(300); pullAnimaton.setFillAfter(true); //正在刷新动画 loadingAnimation = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); loadingAnimation.setDuration(300); loadingAnimation.setRepeatCount(Animation.INFINITE); loadingAnimation.setInterpolator(new LinearInterpolator());//设置匀速的动画插入器 loadingAnimation.setFillAfter(true); } private int downY; @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int moveY = (int) ev.getY();
                int dy = moveY - downY;
                downY = moveY;
                int headerPaddingTop = header.getPaddingTop();
                //1.判断当前完全显示的条目是不是listview的第一个条目
                //2.判断滑动的状态.向下滑显示listview  向上滑隐藏listview
                if (state != State.REFRESHING && getFirstVisiblePosition() == 0 && (dy > 0 || headerPaddingTop > -headerMeasuredHeight)) {
                    headerPaddingTop += dy;
                    header.setPadding(0, headerPaddingTop, 0, 0);
                    if (headerPaddingTop >= 0) {
                        setState(State.RELEASE_TO_REFRESH);
                    } else {
                        setState(State.PULL_TO_REFRESH);
                    }
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                //还原头部
                resetHeader();
                break;
            default:
                break;
        }
        return super.onTouchEvent(ev);
    }

    private void resetHeader() {
        if (state == State.PULL_TO_REFRESH) {//抬起手时  处于下拉刷新状态
            header.setPadding(0, -headerMeasuredHeight, 0, 0);
        } else if (state == State.RELEASE_TO_REFRESH) {//抬起手时  处于释放刷新状态
            //进入正在刷新状态
            header.setPadding(0, 0, 0, 0);
            setState(State.REFRESHING);
            if (onPullToRefreshListener != null) {
                onPullToRefreshListener.onRefresh();
            }
        }
    }

    private boolean isLoading = false;
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        int count = view.getAdapter().getCount();
        if (scrollState == OnScrollListener.SCROLL_STATE_IDLE
                && getLastVisiblePosition() == count - 1
                && isLoading == false) {
            isLoading = true;
            footer.setPadding(0, 0, 0, 0);
            setSelection(count - 1);
            //加载更多数据
            if (onPullToRefreshListener==null) {
                onPullToRefreshListener.loadMore();
            }
        }
    }
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    }
    private enum State {
        RELEASE_TO_REFRESH, PULL_TO_REFRESH, REFRESHING, START
    }

    private State state = State.START;
    /**
     * 设置listview当前状态
     * @param state
     */
    private void setState(State state) {
        if (this.state != state) {
            if (state == State.RELEASE_TO_REFRESH) {//释放刷新
                tvTitle.setText("释放刷新");
                ivArrow.setAnimation(releaseAnimation);
            } else if (state == State.PULL_TO_REFRESH) {
                tvTitle.setText("下拉刷新");
                ivArrow.setAnimation(pullAnimaton);
            } else if (state == State.REFRESHING) {
                tvTitle.setText("正在刷新");
                ivArrow.setVisibility(View.GONE);
                ivArrow.clearAnimation();
                ivLoading.setVisibility(View.VISIBLE);
                ivLoading.setAnimation(loadingAnimation);
                java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("hh:mm:ss");
                String date = dateFormat.format(new Date());
                tvTime.setText(date);
            }
            this.state = state;
        }
    }
    //刷新完成
    public void completeRefresh() {
        ivLoading.clearAnimation();
        ivLoading.setVisibility(View.GONE);
        ivArrow.setVisibility(View.VISIBLE);
        setState(State.PULL_TO_REFRESH);
        header.setPadding(0, -headerMeasuredHeight, 0, 0);
    }
    //加载完成
    public void completeLoadMore(){
        isLoading=false;
        footer.setPadding(0,-footerMeasureHeight,0,0);
    }
}
  • 接口实现:onPullToRefreshListener
public interface onPullToRefreshListener {
    public void onRefresh();
    public void loadMore();
}

使用方法

  • 1.布局中直接引用PullToRefreshListView即可
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="cn.com.bsoft.pulltorefreshlistview.activity.MainActivity">

    <cn.com.bsoft.pulltorefreshlistview.view.PullToRefreshListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/ptr_listview" />
</RelativeLayout>
  • 2.在界面中找到控件传入接口即可:setOnPullToRefreshListener
public class MainActivity extends AppCompatActivity { private PullToRefreshListView pullToRefreshListView; private ArrayAdapter<String> adapter; private List<String> dataList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pullToRefreshListView = ((PullToRefreshListView) findViewById(R.id.ptr_listview)); List<String> datas = getDatas(); adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, datas); pullToRefreshListView.setAdapter(adapter); //设置回调监听 pullToRefreshListView.setOnPullToRefreshListener(new onPullToRefreshListener() { //下拉刷新的回调 @Override public void onRefresh() { //模拟获取数据 new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } final String addData = "弱水三千只取一瓢"; //刷新适配器在主线程 runOnUiThread(new Runnable() { @Override public void run() { dataList.add(0, addData); adapter.notifyDataSetChanged(); //数据获取完之后调用completeRefresh()方法 pullToRefreshListView.completeRefresh(); } }); } }).start(); } //上拉加载更多的回调 @Override public void loadMore() { //模拟获取数据 new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } final String addData = "一点寒芒先到 随后枪出如龙"; runOnUiThread(new Runnable() { @Override public void run() { dataList.add(0, addData); adapter.notifyDataSetChanged(); //数据获取完之后调用completeLoadMore()方法 pullToRefreshListView.completeLoadMore(); } }); } }).start(); } }); } public List<String> getDatas() { dataList = new ArrayList<>(); for (int i = 0; i < 20; i++) { dataList.add("最怕一生碌碌无为,却安慰自己说说平凡可贵!"); } return dataList; } }

GIF效果

效果如下

结束语

  • 第一次玩GIF图,花了半小时还没处理好,上拉加载更多的数据还没出来就结束了。
  • 另外也是第一次使用Markdown编辑器,还不熟练。
  • 以上就是非常简洁的下拉刷新及上拉加载更多的ListView。
  • 不敢跟各种大神的自定义控件做比较,贴出来作为自己学习的小结把,当然,能帮到初学Android的新人那就更好了。
原网站

版权声明
本文为[刘忆初]所创,转载请带上原文链接,感谢
https://blog.csdn.net/qq_34512207/article/details/53907190